7

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.

sandeeMPS
  • 217
  • 3
  • 12

1 Answers1

10

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.

StuartLC
  • 104,537
  • 17
  • 209
  • 285
  • thank you Sir. and how to control connection strings settings. I do not intend to change and encrypt it manually for each Server deployment (although I am/Can not using/use any Deployment tool) – sandeeMPS Jan 09 '15 at 17:10
  • @sandeeMPS - DbContext has a constructor that takes a connection string, use that. – Erik Funkenbusch Jan 09 '15 at 18:52
  • @sandeeMPS Abstract the connection string to at least a connection name (DbContext constructor supports either), and you could indicate to the factory constructor. This would be quite unusual however - usually each `DbContext` subclass is targetted to one database per environment - use web.config to indicate environments. (Changing the connection string dynamically would indicate you have multiple databases with the same schema in the same environment, although may be applicable for your purpose of course). – StuartLC Jan 13 '15 at 06:40
  • 1
    This is so close to the way I approached this it is scary! :) However, the biggest problem I've run into now is that I don't want the container to decide when to dispose of DbContext. I'm using the CQS pattern here, so the command/query handler should deal with its lifetime. But if I do that, any other classes I IoC inject below that would benefit from using the same dbcontext reference can't be IoC injected unless I have some sort of IDbContextProvider that holds on to the first instance used. But it feels weird. Any suggestions? – Daniel Lorenz Jul 31 '17 at 01:35