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();
}
}
Related
I've built a WPF application using Entity Framework. My data store consists of hierarchical data (one project, with multiple different children entities).
To date I've been using a singleton pattern for my Context, as this allows me to have a global navigation tree in my UI (which then lazy loads as the user chooses to expand a specific parent to show its children). This has been working great up until now, but I'm now running into the dreaded exception:
A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.
I understand that I'm seeing this exception due to some actions being performed on some entities and simultaneous requests being made to load other entities, all from the same singleton context.
I further understand that it's best practice to keep a context as short-lived as possible. However, how will this be possible if I want the user to be able to see the whole project and make changes to some entities at a time? I'm at a complete loss as to how to have this global navigation tree in place with a short-lived context (as I keep running into the 'context has been disposed' problem).
Should I implement some locking mechanism around the context, or worse still, have this locking mechanism check each property before requesting it from the context? What is the recommended best practice for this scenario?
Correct, DbContext instances are cheap (they're just wrappers around pooled database connections).
If you want to maintain entities between persistence operations then you can detach and reattach entities to the new DbContext instance:
See https://msdn.microsoft.com/en-us/data/jj592676.aspx
FooEntity fromPreviousContext = ...
using (DbContext context = new DbContext())
{
context.Foos.Attach( fromPreviousContext );
context.SaveChanges();
}
A side-note: generally the Singleton pattern is considered by many to be an anti-pattern as it is easy to be misused especially when a singleton instance is being used to store contextual data - it becomes just a slightly more polite approach to global-variables. You might want to consider the Context pattern instead (unrelated to DbContext).
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.
I am trying to understand the scope of repository class in ASP.NET application. I assume they are thread safe in request scope since each request runs on a seperate thread. But how about having it singleton, is it a valid scenario.
Because these classes doesn't have state but only methods which manipulate data, so different threads executing these methods might be having different stack frames. Is my understanding right, could anyone provide more insights.
interface ICustomerRepository
{
List<Customers> GetAll();
Customer GetById(int id);
}
public class Customer : ICustomerRepository
{
//implement methods
}
Exposing a repository as a singleton for a concurrent environment is a bad idea.
You could possibly implement interface repository in a way that is safe to be used concurrently but this means that the only guarantee of concurrency consistency lies somewhere in the implementation. There is no other mechanism to enforce that concurrent calls would not fail, the contract at the programming language level (repository interface) is just too weak to express such requirement.
On the other hand, if each http context gets its own instance, the implementation details do not matter.
I suggest you read more on object lifetime. A singleton is just a specific example of a more general idea of controlling the lifetime. You can have objects with transient lifetime, objects that are shared in a single instance for the lifetime of you application but also objects that live in a context of a thread or an http context (that possibly spans multiple threads). And one of ways to control the lifetime is to have a factory that creates instances.
If you need something simple, you could have something that looks like a singleton but controls the lifetime in an arbitrary way:
http://netpl.blogspot.com/2010/12/container-based-pseudosingletons-in.html
The Goal
Use an ADO.NET IDbConnection and IDbCommand to execute multiple commands at the same time, against the same database, given that the ADO.NET implementation is specified at runtime.
Investigation
The MSDN Documentation for IDbConnection does not specify any threading limitations. The SqlConnection page has the standard disclaimer saying "Any instance members are not guaranteed to be thread safe." The IDbCommand and SqlCommand documentation is equally un-informative.
Assuming that no individual instance member is thread-safe, I can still create multiple commands from a connection (on the same thread) and then execute them concurrently on different threads.
Presumably this would still not achieve the desired effect, because (I assume) only one command can execute at a time on the single underlying connection to the database. So the concurrent IDbCommand executions would get serialized at the connection.
Conclusion
So this means we have to create a separate IDbConnection, which is ok if you know you're using SqlConnection because that supports pooling. If your ADO.NET implementation is determined at runtime, these assumptions cannot be made.
Does this mean I need to implement my own connection pooling in order to support performant multi-threaded access to the database?
You will need to manage thread access to your instance members, but most ADO implementations manage their own connection pool. They generally expect that multiple queries will be run simultaneously.
I would feel free to open and close as many connections as is necessary, and handle an exceptions that could be thrown if pooling were not available.
Here's an article on ADO connection pooling
If you create a connection on one thread, you shouldn't use it on a different thread. The same goes for commands.
However, you can create a connection on each of your threads and use those objects safely on their own thread.
Pooling is for when you create lots of short-lived connection objects. It means the underlying ( expensive ) database connections are re-used.
Nick
Are the .net classes relating to DbProviderFactory thread safe?
from msdn:
Thread Safety
Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
When you say "These instances are generated once at run time, and used for the rest of the service's life", do you mean the connection object? Also, do you mean you're keeping the connection object open through out the life of your service? If your service is multi-threaded and you only have one instance of the connection (say for example a singleton or static class), you have to make sure the connection is only used by one thread at a time.
Without seeing much code, it sounds like a problem with how you treat the IDbConnection you get from the factory, instead of the factory itself.
We use the DbProviderFactory very heavily for our multithreaded applications, which connect to Oracle, FoxPro and SqlServer and I haven't seen this issue.
Good luck!
Ricardo.