2 sibling nested transactionScope gives: the Transaction has aborted - c#

this code gives me the error: the Transaction has aborted.
if I remove 1 nested transaction than it doesn't throw
using(var scope = new TransactionScope())
{
repo.Insert(new Foo {Fname = "aaaa"});
using(var s = new TransactionScope())
{
repo.Insert(new Foo { Fname = "aaaa" });
//if I remove this transaction it is not going to throw exception
using (var aaa = new TransactionScope())
{
repo.Insert(new Foo { Fname = "aaaa" });
}
using(var ssa = new TransactionScope())
{
repo.Insert(new Foo { Fname = "aaaa" });
}
}
}

What statement does throw the error? I'd assume it is the last repo.Insert.
Since you don't call scope.Complete(), the transaction is rollbacked (aborted) when aaa is disposed.
Generally, transaction rollback is considered an error, so all higher-level transactions also become uncommittable (or are immediately rollbacked).
So, for the last repo.Insert there is no valid transaction to use - that's why it throws an exception.

You might need to specify the TransactionScopeOption like in this example from MSDN:
using(TransactionScope scope1 = new TransactionScope())
//Default is Required
{
using(TransactionScope scope2 = new
TransactionScope(TransactionScopeOption.Required))
{
...
}
using(TransactionScope scope3 = new TransactionScope(TransactionScopeOption.RequiresNew))
{
...
}
using(TransactionScope scope4 = new
TransactionScope(TransactionScopeOption.Suppress))
{
...
}
}
Ref: http://msdn.microsoft.com/en-us/library/ms172152.aspx

yes, it will work. You`ve forgotten to include scope.Complete(); at the end

Related

Why Use Rollback() Method in EF Core 6

What does the Rollback method in EF Core do? If I didn't use Commit, I don't need it anyway. If I used Commit, the transaction has already been completed.
using (var context = new AppDbContext())
{
using (var transaction = context.Database.BeginTransaction())
{
try
{
var myObjectOne = new MyObjectOne() { Name = "Book" };
context.MyObjectOnes.Add(myObjectOne);
context.SaveChanges();
var myVal = myObjectOne.Id * 3.14;
var myObjectTwo = new MyObjectTwo() { Name = "Notebook", Price = 100, ReferenceId = myVal };
context.MyObjectTwos.Add(myObjectTwo);
context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
}
}
}
What does the RollBack Method do? C# EF Core.

Transaction Scope for local database

I'm currently trying to implement some End to End Tests (E2E) using TranactionScope and local file Database (mdf). funny is that the query is not being rolled back, so all my update/inserts are persisted. I dont understand what is done wrong
using (new TransactionScope())
{
var newItem1 = new SomeEntity { Id = 4, Remark = "Test 2" };
var newItem2 = new SomeEntity { Id = 5, Remark = "Test 2" };
var x = new List<SomeEntity> { newItem1, newItem2 };
_testTvp.SaveSomeEntities(x);
var result = _test.GetSomeEntity(4);
Assert.AreEqual(newItem1.Remark, result.Remark);
result = _test.GetSomeEntity(5);
Assert.AreEqual(newItem2.Remark, result.Remark);
}
My connection string is:
for more code, see here: enter link description here
Nothing is wrong. This is how TransactionScope works.
From MSDN
If no exception occurs within the transaction scope (that is, between
the initialization of the TransactionScope object and the calling of
its Dispose method), then the transaction in which the scope
participates is allowed to proceed. If an exception does occur within
the transaction scope, the transaction in which it participates will
be rolled back.
It rollbacks the transaction only if an exception occurs
Why don't you try this with
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
using (SqlTransaction sqlTrans = sqlConnection.BeginTransaction())
{
//put your code here
}
}
Use something like as depending on your stack you might have ambient transactions:
string connStr = "...; Enlist = false";
using (TransactionScope ts = new TransactionScope())
{
using (SqlConnection conn1 = new SqlConnection(connStr))
{
conn1.Open();
conn1.EnlistTransaction(Transaction.Current);
}
}
Under what circumstances is an SqlConnection automatically enlisted in an ambient TransactionScope Transaction?
Try this:
using (new scope = new TransactionScope())
{
var newItem1 = new SomeEntity { Id = 4, Remark = "Test 2" };
var newItem2 = new SomeEntity { Id = 5, Remark = "Test 2" };
var x = new List<SomeEntity> { newItem1, newItem2 };
_testTvp.SaveSomeEntities(x);
var result = _test.GetSomeEntity(4);
Assert.AreEqual(newItem1.Remark, result.Remark);
result = _test.GetSomeEntity(5);
Assert.AreEqual(newItem2.Remark, result.Remark);
//either of the two following:
Transaction.Current.Rollback();
scope.Dispose();
}
Well, I couldnt figure out what exactly is wrong. The possible solution to this is to delete inserted data. Not the best way, but much better one using DbTransaction.I will try to create some test with real sql server and see what is difference.

SqlException: do not abort a transaction

I have a code that adds data to two EntityFramework 6 DataContexts, like this:
using(var scope = new TransactionScope())
{
using(var requestsCtx = new RequestsContext())
{
using(var logsCtx = new LogsContext())
{
var req = new Request { Id = 1, Value = 2 };
requestsCtx.Requests.Add(req);
var log = new LogEntry { RequestId = 1, State = "OK" };
logsCtx.Logs.Add(log);
try
{
requestsCtx.SaveChanges();
}
catch(Exception ex)
{
log.State = "Error: " + ex.Message;
}
logsCtx.SaveChanges();
}
}
}
There is an insert trigger in Requests table that rejects some values using RAISEERROR. This situation is normal and should be handled by the try-catch block where the SaveChanges method is invoked. If the second SaveChanges method fails, however, the changes to both DataContexts must be reverted entirely - hence the transaction scope.
Here goes the error: when requestsCtx.SaveChanges() throws a exception, the whole Transaction.Current has its state set to Aborted and the latter logsCtx.SaveChanges() fails with the following:
TransactionException:
The operation is not valid for the state of the transaction.
Why is this happening and how do tell EF that the first exception is not critical?
Really not sure if this will work, but it might be worth trying.
private void SaveChanges()
{
using(var scope = new TransactionScope())
{
var log = CreateRequest();
bool saveLogSuccess = CreateLogEntry(log);
if (saveLogSuccess)
{
scope.Complete();
}
}
}
private LogEntry CreateRequest()
{
var req = new Request { Id = 1, Value = 2 };
var log = new LogEntry { RequestId = 1, State = "OK" };
using(var requestsCtx = new RequestsContext())
{
requestsCtx.Requests.Add(req);
try
{
requestsCtx.SaveChanges();
}
catch(Exception ex)
{
log.State = "Error: " + ex.Message;
}
finally
{
return log;
}
}
}
private bool CreateLogEntry(LogEntry log)
{
using(var logsCtx = new LogsContext())
{
try
{
logsCtx.Logs.Add(log);
logsCtx.SaveChanges();
}
catch (Exception)
{
return false;
}
return true;
}
}
from the documentation on transactionscope: http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28v=vs.110%29.aspx
If no exception occurs within the transaction scope (that is, between
the initialization of the TransactionScope object and the calling of
its Dispose method), then the transaction in which the scope
participates is allowed to proceed. If an exception does occur within
the transaction scope, the transaction in which it participates will
be rolled back.
Basically as soon as an exception is encountered, the transaction is rolled back (as it seems you're aware) - I think this might work but am really not sure and can't test to confirm. It seems like this goes against the intended use of transaction scope, and I'm not familiar enough with exception handling/bubbling, but maybe it will help! :)
I think I finally figured it out. The trick was to use an isolated transaction for the first SaveChanges:
using(var requestsCtx = new RequestsContext())
using(var logsCtx = new LogsContext())
{
var req = new Request { Id = 1, Value = 2 };
requestsCtx.Requests.Add(req);
var log = new LogEntry { RequestId = 1, State = "OK" };
logsCtx.Logs.Add(log);
using(var outerScope = new TransactionScope())
{
using(var innerScope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
try
{
requestsCtx.SaveChanges();
innerScope.Complete();
}
catch(Exception ex)
{
log.State = "Error: " + ex.Message;
}
}
logsCtx.SaveChanges();
outerScope.Complete();
}
}
Warning: most of the articles about RequiresNew mode discourage using it due to performance reasons. It works perfectly for my scenario, however if there are any side effects that I'm unaware of, please let me know.

Inserting using Linq to Sql but table is still empty

The data is inserted using LINQ to SQL, the id is generated but the database table is empty.
Using a stored procedure there is no problem. But inserting using linq the id is generated everytime but the table is empty.
The code is below:
Int32 t = 2;
using (EduDataClassesDataContext db =new EduDataClassesDataContext())
{
using (var scope = new TransactionScope())
{
db.Connection.ConnectionString = Common.EdukatingConnectionString;
UserLogin userlog = new UserLogin();
userlog.Username = userinfo.Username;
userlog.Password = userinfo.Password;
userlog.UserTypeId = t;
userlog.FullName = userinfo.FullName;
db.UserLogins.InsertOnSubmit(userlog);
db.SubmitChanges();
Int64 n = userlog.Id;
UserInformation userinfor = new UserInformation();
userinfor.FirstName = userinfo.FirstName;
userinfor.LastName = userinfo.LastName;
userinfor.MobileNum = userinfo.MobileNum;
userinfor.Email = userinfo.Email;
userinfor.Gender = userinfo.Gender;
userinfor.Address = userinfo.Address;
userinfor.UserLoginId = n;
userinfor.CreatedBy = n;
userinfor.OrganizationName = userinfo.OrganizationName;
userinfor.DateOfBirth = userinfo.DateOfBirth;
userinfor.CreatedDate = DateTime.Now;
db.UserInformations.InsertOnSubmit(userinfor);
db.SubmitChanges();
}
}
When you are using a TransactionScope, you need to call the Complete method in order to Commit the transaction in the DataBase.
using (var db = new EduDataClassesDataContext())
using (var scope = new TransactionScope())
{
...
db.UserInformations.InsertOnSubmit(userinfor);
db.SubmitChanges();
// The Complete method commits the transaction. If an exception has been thrown,
// Complete is not called and the transaction is rolled back.
scope.Complete();
}
Failing to call this method aborts the transaction, because the
transaction manager interprets this as a system failure, or exceptions
thrown within the scope of transaction.

Problems with TransactionScope in ASP.NET

I've build a class to synchronize data between two different datasources. This synchronization is divided into multiple parts (and methods). Every method has his own TransactionScope and the methods are run sequentially.
Everytime I Run this code I get the following errormessage:
"The transaction associated with the current connection has completed but has not been disposed. The transaction must be disposed before the connection can be used to execute SQL statements."
The following code is an example of such a method with a TransactionScope:
private void SomeMethod()
{
try
{
using (var _transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
{
using (SqlConnection _connection = new SqlConnection(connectionstring))
{
_connection.Open();
DoSomething()...
}
_transactionScope.Complete();
}
}
catch (TransactionAbortedException e)
{
nlog.Error(string.Format("The transaction has been aborted: {0}", e.Message));
throw e;
}
catch (Exception e)
{
throw e;
}
}
It seems that the call "_transactionScope.Complete()" isn't enough to kill the transactionscope.. Does anyone have a clue what i'm doing wrong?
Thanks in advance!
UPDATE
Thanks for your replies. After a few tests I discovered that this problem only exists when there are multiple queries in one method. for example:
try
{
using (TransactionScope _transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
{
using (SqlConnection _connection = new SqlConnection(connectionstring))
{
_connection.Open();
//new method:
using (TransactionScope _transactionScope = new TransactionScope(TransactionScopeOption.Suppress))
{
//a selectquery
}
//an update or insert query
_transactionScope.Complete();
}
}
Try changing the constructor.
using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions()
{
IsolationLevel = System.Transactions.IsolationLevel.Serializable,
Timeout = TimeSpan.FromSeconds(120)
}))
I did a method for creating a Max Timeout value on a transaction scope
public static TransactionScope CreateDefaultTransactionScope(TransactionScopeOption option = TransactionScopeOption.Required)
{
var transactionOptions = new TransactionOptions();
transactionOptions.Timeout = TimeSpan.MaxValue;
transactionOptions.IsolationLevel = IsolationLevel.ReadCommitted;
return new TransactionScope(option, transactionOptions);
}
and then you would use it:
using (TransactionScope transaction = TransactionHelper.CreateDefaultTransactionScope())

Categories

Resources