简体   繁体   中英

EF6 DbContext IOC Dependency

I am using EF6 in Windows Service / Console Application. I have successfully implemented IOC/DI for my Business layer interfaces and implementation. Using Constructor Injection. Also I am using Object Database, Task Parallel Library. for better performance and I am happy with it.

Also using System.IO.Abstractions to make my code more testable.

EF6 creates POCO clases for all the domain entities using .tt files which are quite handy. In order to perform Database queries I am writing every where

using(var db = new MyContext())
{
// code reading from/writing to database
...
...
}

I know it is not right practise and makes noise in my code various places. I want to make it loosely coupled. Now for my database operations - I am confused how to go forward to make it more testable and loosely coupled..Can anyone point me to a good example,article which can be refereed to.

2 major things I want to achive is to have more control over Connection string configuration (for various servers deployment) and to have DbContext very loosely coupled within my code.

To address decoupling (and testing), you can create your own interface for your DbContext ( IMyDbContext ), and re-expose all the typed entity DbSets , SaveChanges() , and possibly a few 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 might also consider read only and read-write versions of the interface)

Then change your concrete DbContext to implement this interface. You are now reasonably decoupled from the DbContext (for Unit Testing, etc), but still have access to the usefulness of IQueryable , the inherent unit of work, and caching offered by the DbContext .

Then, here's two options for injecting the IMyDbContext into your business / service classes

  1. Constructor injection of the IDbContext

OR

  1. Create a Factory method and a Factory interface for creating concrete DbContext s, and then do constructor injection of IMyDbContextFactory factory interface (you'll want the interface, not the concrete factory, again for Mocking test purposes).

The choice here depends on what you need to do with your DbContext . #1 can be tricky to configure in the IoC container, as you need to hand off lifespan management to the container. But this can be beneficial in web apps if it can be configured new instance per-request, so that if the request (assumed single thread) can use it as a cache.

Personally I prefer #2, as this allows direct management of the context:

using(var db = _myContextFactory.CreateDB())
{
    db.SaveChanges();
}

But obviously, we then lose any potential benefit of long-lived contexts such as caching. But there are many other alternative technologies for caching, if needed.

One caveat : DbContext isn't at all thread safe - if you are using TPL, make sure that each task obtains its own instance of DbContext - eg use the localinit overloads of Parallel.For / ForEach to instantiate it when using same.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM