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; }
}
Best Answer
You should inject a fresh
DbContext
into yourEventService
constructor, just like you did with yourEventController
.DbContext
objects are lightweight, short-lived objects. Passing theDbContext
from your EventController into yourEventService
methods creates unnecessary coupling between the classes without providing any additional benefits, and managing aDbContext
'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 aDbContext
with other classes; just make a new one for each class instance.