Nested Transactions in ADO.NET - c#

First, is it possible to have n transactions levels over ADO.Net. Second, is this correct usage?
var tx = cx.BeginTransaction();
cx.Execute("insert into atable(id) values(123123)");
var tx2=tx.BeginTransaction();
cx.Execute("insert into atable(id) values(123127)");
tx2.Commit();
tx.Commit();
...
etc.

You can nest transactions using TransactionScope - however, they will only get committed once the most outer one gets committed.
They will all be rolled back if any one of them will rollback.
In terms of usage - you should wrap the transaction creation in using statements to ensure proper disposal.
using(var tx1 = new TransactionScope())
{
cx.Execute("insert into atable(id) values(123123)");
using(var tx2 = new TransactionScope())
{
cx.Execute("insert into atable(id) values(123127)");
tx2.Complete();
}
tx1.Complete()
}

Related

Entity Framework :Using Transaction Scope how to check whether DbContext has transaction?

As mentioned here
using Entity Framework 6 if we begin a transaction using Database.BeginTransaction()
we can check whether a context has a transaction using this statement:
var transaction = db.Database.CurrentTransaction;
Unfortunately this way is not working if we used TrasctionScope
to begin transaction :
var transactionScope = new TransactionScope();
I'm just wondering if there is any way to check whether a context has a transaction when I'm using TrasctionScope ?
Using entity framework 6 you can use transactions in two ways:
The first way, using Database.BeginTransaction() method:
using (var context = new Context())
{
using (var dbContextTransaction = context.Database.BeginTransaction())
{
try
{
//Some EF Statments
Context.SaveChanges();
dbContextTransaction.Commit();
}
catch (Exception)
{
dbContextTransaction.Rollback();
}
}
The second way, using TransactionScope :
using (var scope = new TransactionScope())
{
//Some EF Statments
Context.SaveChanges();
scope.Complete();
}
If you used first way you can get transaction instance using statement:
`var transaction = context.Database.CurrentTransaction;`
On the other hand if you begin transaction using TrasctionScope you have to use:
var transaction = System.Transactions.Transaction.Current;
to get transaction instance or check whether a context has a transaction or not

SqlConnection doesn't automatically enlist to TransactionScope

I read the following StackOverflow posts about nested transactionScope, but I couldn't get it to work in one of my integration tests. I wanted to insert some records into the database and verify the writes afterward immediately, and then rollback at the end of the test to its origin.
The problem is that I would need to explicitly call connection.EnlistTransaction(Transaction.Current) in order to have it working. One thing that I am not sure about is whether everything has to be in the same layer or file, because in my case (code example on the bottom) the transaction scope is defined in the service tier, and it calls multiple downstream repository objects for low level SQL query operations (CRUD)
TransactionScope error in ambient transaction does not rollback the transaction
Under what circumstances is an SqlConnection automatically enlisted in an ambient TransactionScope Transaction?
Introducing System.Transactions in the .NET Framework 2.0
Here's the code I've had so far
using(TransactionScope scope = new TransactionScope())
{
repository1.CreateEntries(expectedList);
repository2.UpdateEntries(expectedList);
scope.Dispose();
}
this is from repository1 class
void CreateEntries(List<Object> list) {
using (var conn = new SqlConection(CONNECTION_STRING))
{
conn.Open();
// i need to explicitly call this
conn.EnlistTransaction(Transaction.Current);
....
}
}
this is from repository2 class
void UpdateEntries(List<Object> list) {
using (var conn = new SqlConection(CONNECTION_STRING))
{
conn.Open();
conn.EnlistTransaction(Transaction.Current);
....
}
}

What is the right way to use TransactionScopes with MVC4?

I'm using one TransactionScope per request, something like this:
public ActionResult Login(string user, string pass)
{
using (ServerContext context = new ServerContext ())
{
TransactionOptions transOptions = new TransactionOptions();
transOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
transOptions.Timeout = TransactionManager.MaximumTimeout;
using (var scope = new TransactionScope(TransactionScopeOption.Required, transOptions))
{
// Some logic and Linq queries here
}
}
I perform some inserts, updates, deletes and proc calls inside the transaction scope
But, I got some rare DeadLock expceptions with high loads. So, i'm doing it right?
Or it's better to open a TransactionScope per operation? (But I need to rollback all operation if one of the set fails.)
Thnak you.
AFAIK, in L2S When you call SubmitChanges, LINQ to SQL checks to see whether the call is in the scope of a Transaction or if the Transaction property (IDbTransaction) is set to a user-started local transaction. If it finds neither transaction, LINQ to SQL starts a local transaction (IDbTransaction) and uses it to execute the generated SQL commands. When all SQL commands have been successfully completed, LINQ to SQL commits the local transaction and returns.
I have no ideas on MVC/EF but try wrapping like this :
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
using (ServerContext context = new ServerContext ())
{
// Some logic and Linq queries here
}
scope.Complete();
}

How to use transactions with dapper.net?

I would like to run multiple insert statements on multiple tables. I am using dapper.net. I don't see any way to handle transactions with dapper.net.
Please share your ideas on how to use transactions with dapper.net.
Here the code snippet:
using System.Transactions;
....
using (var transactionScope = new TransactionScope())
{
DoYourDapperWork();
transactionScope.Complete();
}
Note that you need to add reference to System.Transactions assembly because it is not referenced by default.
I preferred to use a more intuitive approach by getting the transaction directly from the connection:
// This called method will get a connection, and open it if it's not yet open.
using (var connection = GetOpenConnection())
using (var transaction = connection.BeginTransaction())
{
connection.Execute(
"INSERT INTO data(Foo, Bar) values (#Foo, #Bar);", listOf5000Items, transaction);
transaction.Commit();
}
There are 3 approaches to doing transactions in Dapper.
Simple Transaction
Transaction from Transaction Scope
Using Dapper Transaction (additional nuget package and most favored approach)
You can find out more about these transaction approaches from the official tutorial website here
For reference here's a breakdown of the transaction approaches
1. Simple Transaction
In this example, you create a transaction on an existing db connection, and then pass in the transaction to the Execute method on dapper (which is an optional parameter).
Once you've done all your work, simply commit the transaction.
string sql = "INSERT INTO Customers (CustomerName) Values (#CustomerName);";
using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
connection.Execute(sql, new {CustomerName = "Mark"}, transaction: transaction);
connection.Execute(sql, new {CustomerName = "Sam"}, transaction: transaction);
connection.Execute(sql, new {CustomerName = "John"}, transaction: transaction);
transaction.Commit();
}
}
2. Transaction from Transaction Scope
If you'd like to create a transaction scope, you will need to do this before the db connection is created. Once you've created the transaction scope, you can simply perform all your operations and then do a single call to complete the transaction, which will then commit all the commands
using (var transaction = new TransactionScope())
{
var sql = "INSERT INTO Customers (CustomerName) Values (#CustomerName);";
using (var connection = My.ConnectionFactory())
{
connection.Open();
connection.Execute(sql, new {CustomerName = "Mark"});
connection.Execute(sql, new {CustomerName = "Sam"});
connection.Execute(sql, new {CustomerName = "John"});
}
transaction.Complete();
}
3. Using Dapper Transaction
In my opinion, this is the most favorable approach to achieve transaction in code, because it makes the code easy to read and easy to implement. There is an extended implementation of SQL Transaction called Dapper Transaction (which you can find here), which allows you to run the SQL executes off the transactions directly.
string sql = "INSERT INTO Customers (CustomerName) Values (#CustomerName);";
using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
transaction.Execute(sql, new {CustomerName = "Mark"});
transaction.Execute(sql, new {CustomerName = "Sam"});
transaction.Execute(sql, new {CustomerName = "John"});
transaction.Commit();
}
}
You should be able to use TransactionScope since Dapper runs just ADO.NET commands.
using (var scope = new TransactionScope())
{
// open connection
// insert
// insert
scope.Complete();
}
Considering all your tables are in single database, I disagree with TransactionScope solution suggested in some answers here. Refer this answer.
TransactionScope is generally used for distributed transactions; transaction spanning different databases may be on different system. This needs some configurations on operating system and SQL Server without which this will not work. This is not recommended if all your queries are against single instance of database.
But, with single database this may be useful when you need to include the code in transaction that is not under your control. With single database, it does not need special configurations either.
connection.BeginTransaction is ADO.NET syntax to implement transaction (in C#, VB.NET etc.) against single database. This does not work across multiple databases.
So, connection.BeginTransaction() is better way to go.
Even the better way to handle the transaction is to implement UnitOfWork as explained in this answer.
Daniel's answer worked as expected for me. For completeness, here's a snippet that demonstrates commit and rollback using a transaction scope and dapper:
using System.Transactions;
// _sqlConnection has been opened elsewhere in preceeding code
using (var transactionScope = new TransactionScope())
{
try
{
long result = _sqlConnection.ExecuteScalar<long>(sqlString, new {Param1 = 1, Param2 = "string"});
transactionScope.Complete();
}
catch (Exception exception)
{
// Logger initialized elsewhere in code
_logger.Error(exception, $"Error encountered whilst executing SQL: {sqlString}, Message: {exception.Message}")
// re-throw to let the caller know
throw;
}
} // This is where Dispose is called

EF Transactions MSDTC?

I'm having a bito f a problem with EF and transaction processing.
I'm trying to do this:
using(TransactionScope scope = new TransactionScope())
{
using(MyEntities model = new MyEntities())
{
MyT thing = new MyT{ Value1 = "bla", Value2 = "bla2", Value3 = "foo" };
model.MyT.AddObject(thing);
model.SaveChanges();
thing.Value4 = Service.Call("bar");
// this call causes an exception in MSDTC
model.SaveChanges();
scope.Complete();
}
}
The reason i do this is because I want to do an insert in to the db so MyT has a unique id that i passto the service when i make the call i then get back a unique ref and status from the service depicting what happened during the call which i then need to append to the record.
My understanding is that during a single transaction you can only update a record once / make an insert call but you can't do both as this creates a problem for some reason ... i used to have an MSDN article that explained some logical reason why this couldn't be done (likely lock related).
So my problem is how to get round this but ensure that in event of any failure in any of these calls i can still rollback.
When you make several connections to the db inside the transaction scope, the transaction will be promoted from local transaction to distributed transaction, unless you explicitily use the same connection to the db.
When the transaction is promoted it needs the MSDTC service to manage the transaction, so if this service is not available it will throw an exception.
Something like this:
using(TransactionScope scope = new TransactionScope())
{
using(MyEntities model = new MyEntities())
{
model.Connection.Open();
MyT thing = new MyT{ Value1 = "bla", Value2 = "bla2", Value3 = "foo" };
model.MyT.AddObject(thing);
model.SaveChanges();
thing.Value4 = Service.Call("bar");
// this call shouldn't cause anymore an exception in MSDTC
model.SaveChanges();
scope.Complete();
}
}
Try creating the transactionscope with transaction options. In the transaction options specify ReadUncommitted.
http://msdn.microsoft.com/en-us/library/system.transactions.isolationlevel.aspx
http://msdn.microsoft.com/en-us/library/ms149853.aspx

Categories