Unit of Work Pattern
DbContext is effectively an implementation of the 'unit of work' pattern - once the DbContext
is created, all changed done to the DbSet
are then persisted in one go when you call SaveChanges
.
So the further question you need to answer in order to properly answer your question is: What is the scope of the changes that make up your unit of work? In other words - what set of changes need to be made atomically - all succeed, or all fail?
A practical (if example of this - say you have an API endpoint that exposes an operation allowing the client to submit an order. The controller uses OrderService to submit the order, and then InventoryService to update the inventory associated with the items in the order. If each service has their own DbContext, you have a risk that the OrderService will succeed to persist the order submission, but the InventoryService will fail to persist the inventory update.
Dependency Injection
To combat this, a common pattern is to create a context per-request and let your IoC container create and dispose the context, and make it available to inject into services per request. This blog post gives a few options for DbContext management, and includes an example of configuring Ninject to do it.
What this means is your ctor will look like:
public ProfileService(CommService commService, AppContext context) {
_commService = commService;
_context = context;
}
And you can safely use the context there without having to worry about how it was created or where it came from.
Medhi's DbScopeFactory
However, my preferred approach for more complex applications is an excellent open source library documented up here: http://mehdi.me/ambient-dbcontext-in-ef6/. Injecting DbContext per request will work fine for simpler applications, but as your application gets more involved (e.g. multiple Contexts per application, multiple databases etc.), the finer grain control offered by his IDbContextScopeFactory
is invaluable.
Edit to Add - Pros and Cons of Injection vs Construction
Following your comment asking for pros/cons of the approach you proposed, I'd say that generally, injection of dependencies (including DbContext) is a far more flexible and powerful approach, and can still achieve the goal of ensuring your devs don't have to be concerned with dbcontext lifecycle management.
The pros and cons are generally the same for all instances of dependency injection not just db context, but here are a few concrete issues with constructing the context within the service (even in a base service):
- each service will have its own instance of the dbcontext - this can lead to consistency problems where your unit of work spans tasks carried out by multiple services (see example above)
- It will be much more difficult to unit test your services, as they are constructing their own dependency. Injecting the dbcontext means you can mock it in your unit tests and test functionality without hitting the database
- It introduces unmanaged state into your services - if you are using dependency injection, you want the IoC container to manage the lifecycle of services. When your service has no per-request dependencies, the IoC container will create a single instance of the service for the whole application, which means your dbcontext saved to the private member will be used for all requests/threads - this can be a big problem and should be avoided.
- (Note: this is less of an issue if you are not using DI and constructing new instances of the services within controllers, but then you are losing the benefits of DI at the controller level as well...)
- All services are now locked to using the same DbContext instance - what if, in the future you decide to split your database and some services need to access a different DbContext? Would you create two different BaseServices? Or pass in configuration data to allow the base service to switch? DI would take care of that, because you would just register the two different Context classes, and then the container would provide each service with the context it needs.
- Are you returning
IQueryable
s anywhere? If you are, then you run a risk that the IQueryable
will cause the Db to hit even after the DbContext has gone out of scope - it may have been disposed by the garbage collector and will not be available.
From a dev perspective, I think nothing is simpler than the DI approach - simply specify the DbContext in your constructor, and let the DI container container take care of the rest.
If you are using DbContext per request, you don't even have to create or dispose the context, and you can be confident that IQueryables will be resolvable at any point in the request call stack.
If you use Mehdi's approach, you do have to create a DbContextScope, but that approach is more appropriate if you are going down a repository pattern path and want explicit control over the context scope.
As you can see, I'm far less concerned about the computational cost of constructing a DbContext when it's not needed (as far as I can tell, it's a fairly low cost until you actually use it to hit the db), and more concerned about the application architecture that permits unit testing and decoupling from dependencies.