3

I'm having some concerns about injecting my Entity Framework DbContext into my MVC controller.

Below is a sample of my code. The way I designed this is so that I can better unit test my Controller code by injecting a real or mock DbContext.

What I'm currently planning to do is using Unity and the IDbEntities interface to inject the DbContext into the controller. Every time a new action is called from this controller, Unity will call the controller constructor and make a new DbContext object which implements the IDbEntities interface. That way I have a fresh context object on each new request, which I think is pretty safe.

My concern is what happens next. In the Action, I can have multiple calls to the same or different Data Service using the same DbContext object. For example, in my Index() action I can have calls to UpdateEvent(dbContext) and then call SoftDeleteEvent(dbContext) using the same DbContext object, or even pass the same context to a different Data Service, for example UserService.LogoutUser(dbContext).

I'm not sure if all this passing around of the same DbContext object is safe.

Is it safe to pass the DbContext like this or is there a better way to extract the DbContext from the DataService into the Controller for unit testing purposes?

Controller:

public class EventsController : Controller
{    
    private IDbEntities _dbContext;

    public EventsController(IDbEntities entities)
    {
        _dbContext = entities;
    }

    public ActionResult Index()
    {
        // Hypothetical operations. In reality update and delete of the same record rarely happen in the same code branch
        var events = _eventService.AllEvents(_dbContext);
        _eventService.UpdateEvent(id, "NewName", _dbContext);
        _eventService.SoftDeleteEvent(id, _dbContext);

        return View(events);
    }
}

Data Service:

public class EventService : IEventService
{
    public IEnumerable<Event> AllEvents(IDbEntities entities)
    {
        return entities.Events.ToList();
    }

    public void UpdateEvent(Guid id, string name, IDbEntities entities)
    {
        var item = entities.Events.Find(id);
        if (item != null)
        {
            item.Name = name;
            entities.SaveChanges();
        }
    }

    public bool SoftDeleteEvent(Guid id, IDbEntities entities)
    {
        var item = entities.Events.Find(id);
        if (item != null)
        {
            item.Purged = true;
            entities.SaveChanges();
        }

        return true;
    }
}

DbContext Interface:

// This is an interface that my Entity Framework DbContext implements for Dependency Injection purposes
public interface IDbEntities
{
    DbSet<Event> Events { get; set; }
}
0

1 Answer 1

2

You should inject a fresh DbContext into your EventService constructor, just like you did with your EventController.

DbContext objects are lightweight, short-lived objects. Passing the DbContext from your EventController into your EventService methods creates unnecessary coupling between the classes without providing any additional benefits, and managing a DbContext's lifetime isn't the responsibility of a controller anyway.

If you don't need the DbContext in your controller (as your Controller code seems to suggest), then you don't need to inject it into the controller at all.

Class boundaries are the appropriate level of abstraction for a DbContext. In general, you shouldn't share a DbContext with other classes; just make a new one for each class instance.

4
  • Thanks Robert! Like you suggested, it makes sense to move the DbContext injection down a level from the Controller to the EventService. But that still allows multiple different EventService methods to call the same DbContext, potentially dirtying it or causing it to have inconsistent data. What do you think about that?
    – Mité
    Commented Oct 25, 2017 at 23:39
  • As long as you call those methods in a thread-safe manner (and use entities that have been read from that same dbcontext), I think you'll be all right. Commented Oct 26, 2017 at 15:19
  • That's a bad idea if you need a shared transaction between different services
    – gcali
    Commented Mar 22, 2018 at 10:39
  • @Odexios: Sounds like a candidate for a new class. Commented Mar 22, 2018 at 13:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.