1

I've been working with the entity framework for a little bit now, and I've come across several scenarios where two contexts would try to access the same entity etc, so I'm wondering if perhaps I'm not opening/closing my dbcontexts in the best way.

Currently I basically open a DbContext on each of my controllers the way the basic MVC app was set up originally, which means I have a private dbcontext field on the controller, and I override the controller's dispose method to call dispose on the context.

However I also sometimes make queries against the db in some of my other classes, which can get called from within the controller, which also has a context open.

Is there a way to access an open context without an explicit handler? It wouldn't really make sense for me to pass around a DbContext reference through a dozen different methods.

Tevis
  • 729
  • 1
  • 12
  • 27
  • 4
    Open, use and close it. Preferable with a using construction. – Ako Mar 16 '15 at 17:52
  • @Ako While that is the advice I would give for ADO.NET `SqlConnection` connections, EF does do some special stuff internally (specificly caching entities) that having a long lived DbContext that suvirives multiple data calls can give performance benitifits. – Scott Chamberlain Mar 16 '15 at 23:57
  • Did yo check [this](http://stackoverflow.com/questions/12913108/nested-dbcontext-due-to-method-calls-entity-framework) post. I think it might answer your question. – Nilesh Mar 17 '15 at 09:17

3 Answers3

4

Using Dependency Injection

As others have said and will probably reiterate, the 'right way' is often considered to be dependency injection.

In my latest project, I've organized things so that I'm almost done with the project and DI has been so effortless that I'm doing it myself (rather than using an injector). One major factor in that has been adhering fairly strictly to this structure:

                           WebProject
                             |    |
                             |   DataServices
                             |    |        | 
                           ViewModels    EntityModels

Access to all data services during one unit of work occurs through a single DataServiceFactory instance, that requires an instance of MyDbContext. Another factor has been an entirely RESTful application design - it means I don't have to intersperse persistence functionality throughout my code.

Without Dependency Injection

That said, maybe DI isn't right for you on this project. Maybe:

  • you don't plan to write unit tests
  • you need more time to understand DI
  • your project structure already has EF deeply integrated

In ASP.NET MVC, the unit of work often entirely coincides with the request lifetime - i.e. HttpContext.Current. As a result, you can lazily instanciate a repository 'singleton' per-request, instead of using DI. Here is a classic singleton pattern with current context as the backing store, to hold your DbContext:

public class RepositoryProxy {
    private static HttpContext Ctx { get { return HttpContext.Current; } }
    private static Guid repoGuid = typeof(MyDbContext).GUID;

    public static MyDbContext Context {
        get {
            MyDbContext repo = Ctx.Items[repoGuid];
            if (repo == null) {
                repo = new MyDbContext();
                Ctx.Items[repoGuid] = result;
            }
            return repo;
        }
    }

    public static void SaveIfContext() {
        MyDbContext repo = Ctx.Items[repoGuid];
        if (repo != null) repo.SaveChanges();
    }
}

You can SaveChanges automatically too, if you are feeling especially lazy (you'll still need to call it manually to inspect side-effects, of course, like retrieving the id for a new item):

public abstract class ExtendedController : Controller {
    protected MyDbContext Context {
        get { return RepositoryProxy.Context; }
    }

    protected override void OnActionExecuted(ActionExecutedContext filterContext) {
        RepositoryProxy.SaveIfContext();
        base.OnActionExecuted(filterContext);
    }
}
shannon
  • 8,664
  • 5
  • 44
  • 74
  • Thanks for the detailed answer. I'm still struggling with the concept of dependency injection. On your third "maybe" point, do you mean that Entity Framework is altogether separate from a dependency injection approach, i.e. mutually exclusive? I think I'm going to give your singleton approach a try and see how that goes. – Tevis Mar 17 '15 at 13:20
  • You're welcome. No, Entity Framework is ideally suited for dependency injection. However, you mentioned that you are "pass[ing] around a DbContext reference through a dozen different methods." That sounds like your project is currently relying directly on persistence pretty broadly. From some pretty deep dependencies. Perhaps you call EF directly from ViewModels, for example. Or from various business logic. DI doesn't really make that problem go away, it just makes it more painful to deal with so you are less likely to do it :D – shannon Mar 17 '15 at 13:32
  • Ok I have the singleton implemented and it's much cleaner already. I know that DI is good for unit testing because of the interface usage lets you mock the context easily, but are there other benefits to the approach? It seems like every article I read on it has a different take. – Tevis Mar 17 '15 at 13:55
  • That's a tough question. The benefits of IoC come from the abstraction that is followed. It's not just mocks you can inject. You can replace components or reuse it for other purposes. But that would typically require design intention from someone already very familiar with IoC. A DI framework would reduce the effort of plugging dependents in, in an IoC design. What's the benefit to you initially? Probably really nothing besides learning, but that prepares you to get real use out of it. – shannon Mar 17 '15 at 14:29
  • Ok I think I'm starting to get a feel for the usefulness. That seems to be the hardest part to explain with paradigms. Half the sites say "do this because it follows the DI paradigm," as opposed to "this is the DI paradigm because it allows us to X". Thanks again for all your help! – Tevis Mar 17 '15 at 14:32
  • IMO, there are much better places to learn about DI than a back-end project that is already underway. I'd recommend, for example, using AngularJS on an upcoming project. It's a sweet client-side tool that implements DI as a core feature. – shannon Mar 17 '15 at 14:33
  • I just added a comment to my answer while I was thinking of it. If you use that code verbatim, you will still need to call `SaveChanges` manually sometimes, for example to retrieve the Id of new entities. – shannon Mar 17 '15 at 14:42
  • Oh, your dispose can go in the base Controller, also. – shannon Mar 18 '15 at 05:36
3

The "best" way to handle DbContext instances is to make it a parameter on each method that needs it (or, if an entire class needs it, a parameter to the constructor).

This is a basic part of IoC (Inversion of Control) which allows the caller to specify dependencies for a method thus allowing the caller to "control" the behavior of the called method.

You can then add a Dependency Injection framework as well. They can be configured to use a singleton instance of your DbContext and inject that instance into any methods that need it.

Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
2

Instead of passing instance of DbContext I would suggest you pass instance of some kind of factory class, that will create an instance of DbContext for you.

For most scenarios you can just create interface, that your DbContext will implement, something like this:

public interface IDbContext
{
    IDbSet<TEntity> Set<TEntity>() where TEntity : class;
    DbSet Set(Type entityType);
    DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
    int SaveChanges();
}

and then a factory to return your instance:

public interface IContextFactory
{
    IDbContext Retrieve();
}

The factory can be easily mocked to return whatever implementation you want - which is a good approach especially if you intend to test the application. Your DbContext will just derive from IDbContext interface and implement necessary methods and properties. This approach assumes, that to tell EF about your database tables you use OnModelCreating(DbModelBuilder modelBuilder) method and with modelBuilder.Configurations.Add(new EntityTypeConfiguratinOfSomeType()); instead of having properties of type DbSet<T> all around your context.

To completely abstract from Entity framework, you can create yet another layer of abstraction, for example with UnitOfWork or Repository pattern approaches, but I guess the former covers your concerns for now.

mcs_dodo
  • 738
  • 1
  • 10
  • 17