Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 632
sandeeMPS Avatar asked Jan 09 '15 16:01

sandeeMPS


People also ask

Should DbContext be singleton or transient?

DbContext should not be used as a singleton because it is holding a connection object which cannot be used by multiple threads at the same time.

Is DbContext scoped or transient?

This example registers a DbContext subclass called ApplicationDbContext as a scoped service in the ASP.NET Core application service provider (a.k.a. the dependency injection container). The context is configured to use the SQL Server database provider and will read the connection string from ASP.NET Core configuration.

What is DbContext and DbSet in Entity Framework?

DbContext generally represents a database connection and a set of tables. DbSet is used to represent a table.


1 Answers

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 DbContexts, 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 - e.g. use the localinit overloads of Parallel.For / ForEach to instantiate it when using same.

like image 122
StuartLC Avatar answered Oct 22 '22 11:10

StuartLC