I have read that when a DbContext.SaveChanges() runs all the operations are automatically wrapped in a transaction for you behind the scenes. That is, if any of the operations inside during SaveChanges() fail, everything is rolled back maintaining consistent state.
However, one term I've come across several times is that the changes can run as part of an ambient transaction. What exactly does that mean?
My specific concern is: I have a multithreaded application, in which I have one context per operation. None of my DbContext objects are shared across different threads. Am I guaranteed that the operations of each DbContext.SaveChanges() will run in separate transactions?
In your case, yes, you are guaranteed that each DbContext.SaveChanges() will run in separate transactions.
The term "ambient" transaction refers to a transaction that was started higher-up in the call stack. So that this is a per-thread concept. See Transaction.Current and TransactionScope. It is a feature that allows you do to something like this:
using (TransactionScope scope123 = new TransactionScope())
{
using (SqlConnection connection1 = new SqlConnection(connectString1))
{
// Do some work
using (SqlConnection connection2 = new SqlConnection(connectString2))
{
// Do some more work
}
}
Both of the above connections automatically use the "ambient" transaction "scope123." It sounds like the entity framework now knows how to do this. But the TransactionScope won't cross threads, so you are okay. And it doesn't sound like you are explicitly creating transaction scopes anyway.
Based on this: http://msdn.microsoft.com/en-us/data/dn456843.aspx
By default, SaveChanges will open a new transaction, and dispose of it once complete. In EF 6, functionality was added such that you can override this behavior. So long as you don't go out of your way to reuse transactions - you should be okay.
Related
Hi firstly thank you for your attention on this question;
Is there any way to implement a transaction like this in c#
using (Transactionscope x=new Transactionscope ())
{
Thead A()=> Independent Transactionscope() A(Insert into table X )
Thead B()=> Independent Transactionscope() B(Insert into table Y )
Thead C()=> Independent Transactionscope() C(Insert into table Z )
Thread.WaitAll(A,B,C)
commit big transaction x/ rollback big transaction x
}
Note that Distributed Transactions are currently not supported on .Net Core, only on .Net Framework.
In order to use a TransactionScope to span multiple threads, you'll need to use DependentClone to tie the threads into the parent TransactionScope.
The steps are:
Start a TransactionScope on your main / first thread
Just before creating each thread, use DependentClone to create a DependentTransaction, and then pass this DependentTransaction instance to the new thread.
On the child thread, you can use the TransactionScope(DependentTransaction) constructor overload to create a linked TransactionScope, in which the child thread can perform local transactions.
As the work on each child thread is completed successfully, then commit both the thread TransactionScope and the DependentTransaction
On the main thread, wait until all threads are complete, and then commit the root TransactionScope
There's some caveats too:
Using DependentTransaction on multiple threads will immediately require the use of MSDTC.
Using multiple threads under a large DTC transaction isn't going to make insertion into the same table any quicker (Use SqlBulkCopy for that), and you'll want to measure whether parallel inserts into different tables, same database under a DTC transaction warrants the locking overhead or returns any performance benefit.
If you're using async, then you'll need TransactionScopeAsyncFlowOption.Enabled
More about Transction Scope here
I have a service that use several repositories. I want them all to use the same transaction so that, if anything goes wrong, I can rollback the transactions and nothing is left in an invalid state in the database.
I've created a connection factory that returns the same connection to all clients.
public IDbConnection Connection => _db ?? (_db = _factory.OpenDbConnection());
Repositories takes the class holding this property as a constructor argument. This seemingly works, and enables them to use the same connection, and I can manage the transaction on the outer level. Both the connection factory and its clients are registered in IoC with ReuseScope.Request.
I am wondering though, are there any pitfalls to this?
What happens if someone™ starts using async/await with this shared connection? Do I have to ensure that the connection is never shared across threads? (I was thinking about storing it in a ThreadLocal inside the connection factory).
Anything else?
I'm sorry for this kind of vague question, but I believe this must be a quite common use case.
ADO.NET's IDbConnection resource instance is not thread safe and should never be used in multiple threads. Typically each Thread would retrieve its own pooled DB Connection from the Db connection factory.
Whilst async/await can continue on a different thread, they're not executed concurrently so the same DB Connection can be used as it doesn't get used by multiple threads at the same time.
When I do use repositories they would be responsible for logical units of functionality, (never per-table which I consider an anti-pattern forcing unnecessary friction and abstraction penalties), so I'd rarely have transactions spanning multiple repositories, if I did I'd make it explicit and pass the same DB connection to each repository within an explicit DB transaction scope, e.g:
public object Any(MyRequest request)
{
using (var dbTrans = Db.OpenTransaction())
{
MyRepository1.Something(Db, request.Id);
MyRepository2.Something(Db, request.Id);
//....
dbTrans.Commit();
}
}
On my c# project, i have an SQL connection in MARS mode that is being used by multiple threads to perform CRUD operations. Some of these operations are required to be performed as a transaction. After i completed the data access module, i started testing and got an InvalidOperationException from one of the selects, stating that since the connection had an active transaction, the select itself needed to be in a transaction. Snooping around MSDN i found the following remark:
Caution: When your query returns a large amount of data and calls BeginTransaction, a SqlException is thrown because SQL Server does not allow parallel transactions when using MARS. To avoid this problem, always associate a transaction with the command, the connection, or both before any readers are open.
I could easily create a method that would aggregate commands into a transaction, this would even allow me to have a timer thread committing transactions on a regular interval, but is this the right way? Should i instead halt commands that don't need a transaction until an active transaction is committed?
I would stay away from MARS.
See:
used by multiple threads to perform CRUD operations
That screams "every thread one connection, and it's own transaction" unless you have a very rare case here. This absolutely does not sound like a valid use case for MARS.
I have one context created and then using Parallel.ForEach(...) I perform multiple EF queries simultaneously.
I have been encountering 'The connection was not closed. The connection's current state is connecting.' and other exceptions along those lines.
I this due to the threaded nature of my application? Can you not use a single context simulataneously to simply read?
Object context is not a thread safe, so you probably you don't want to use Parrallel.Foreach on multi threading scenarios
However, you can execute multiple queries on the same connection in parrallel with enabling MARS for example
foreach (var employee in context.employees.where(...))
{
var department = employee.departments.FirstOrDefault(...);
}
however, you need to be aware of the performance.
I am using SQLite from system.data.sqlite.org
We need to access the database from many threads (for various reasons). I've read a lot about sqlite thread safe capabilities (the default synchronized access mode is fine for me).
I wonder if it is possible to simply open a connection per thread. Is something like this possible? I really don't care about race conditions (request something that hasn't been inserted yet). I am only interested in the fact that it is possible to access the data using one SQLiteConnection object per thread.
Yes. In fact, it's the proper way, as SQLite is not thread safe (by default. You can make it threadsafe compiling with some option). And just to ensure it works: SQLite is being used in some small websites, so multithreading is there :)
Here more information: http://www.sqlite.org/faq.html#q6
Given you use a separate connection per thread you should be fine.
From docs
Note that SQLiteConnection instance is not guaranteed to be thread
safe. You should avoid using the same SQLiteConnection in several
threads at the same time. It is recommended to open a new connection
per thread and to close it when the work is done.