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; }
}
1 Answer 1
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.
-
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é– Mité10/25/2017 23:39:08Commented 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.Robert Harvey– Robert Harvey10/26/2017 15:19:12Commented Oct 26, 2017 at 15:19
-
That's a bad idea if you need a shared transaction between different servicesgcali– gcali03/22/2018 10:39:07Commented Mar 22, 2018 at 10:39
-
@Odexios: Sounds like a candidate for a new class.Robert Harvey– Robert Harvey03/22/2018 13:30:50Commented Mar 22, 2018 at 13:30
Explore related questions
See similar questions with these tags.