To eliminate the decoupling (and testing), you can create your own interface for your DbContext ( IMyDbContext ) and re-parse all the typed objects of DbSets , SaveChanges() and, possibly, several other methods, you should also make this interface Disposable .
public interface IMyDbContext : IDisposable { IDbSet<Foo> Foos { get; set; } IDbSet<Bar> Bars { get; set; } int SaveChanges(); DbEntityEntry<T> Entry<T>(T entity) where T : class; }
(You can also consider reading and reading and writing versions of the interface)
Then modify your specific DbContext to implement this interface. You are now reasonably separate from DbContext (for Unit Testing, etc.), but you still have access to the IQueryable utility, an integral unit of work and caching offered by DbContext .
Then here are two options for IMyDbContext into your business / service classes
- Enter
IDbContext constructor
OR
- Create a Factory method and a Factory interface to create specific
DbContext s, and then embed the IMyDbContextFactory Factory interface IMyDbContextFactory (you will need an interface, not a specific factory, again for testing Mocking).
The choice here depends on what you need to do with your DbContext . # 1 can be difficult to configure in an IoC container, since you need to pass lifetime control to the container. But it can be useful in web applications if it can be configured on a new instance for the request, so if the request (the supposed single stream) can use it as a cache.
Personally, I prefer # 2, as it allows direct context control:
using(var db = _myContextFactory.CreateDB()) { db.SaveChanges(); }
But, obviously, we are losing any potential advantage of long-term contexts such as caching. But there are many other alternative technologies for caching, if necessary.
One caveat: DbContext not a completely safe thread - if you use TPL, make sure that each task gets its own instance of DbContext - for example. use localinit overload Parallel.For / ForEach to create an instance when using this.