A new modern approach as articulated here is adviced in such scenarios.
If you're familiar with the TransactionScope
class, then you already know how to use a DbContextScope
. They're very similar in essence - the only difference is that DbContextScope
creates and manages DbContext
instances instead of database transactions. But just like TransactionScope
, DbContextScope
is ambient, can be nested, can have its nesting behaviour disabled and works fine with async execution flows.
public void MarkUserAsPremium(Guid userId)
{
using (var dbContextScope = _dbContextScopeFactory.Create())
{
var user = _userRepository.Get(userId);
user.IsPremiumUser = true;
dbContextScope.SaveChanges();
}
}
Within a DbContextScope
, you can access the DbContext
instances that the scope manages in two ways. You can get them via the DbContextScope.DbContexts
property like this:
public void SomeServiceMethod(Guid userId)
{
using (var dbContextScope = _dbContextScopeFactory.Create())
{
var user = dbContextScope.DbContexts.Get<MyDbContext>.Set<User>.Find(userId);
[...]
dbContextScope.SaveChanges();
}
}
But that's of course only available in the method that created the DbContextScope
. If you need to access the ambient DbContext
instances anywhere else (e.g. in a repository class), you can just take a dependency on IAmbientDbContextLocator
, which you would use like this:
public class UserRepository : IUserRepository
{
private readonly IAmbientDbContextLocator _contextLocator;
public UserRepository(IAmbientDbContextLocator contextLocator)
{
if (contextLocator == null) throw new ArgumentNullException("contextLocator");
_contextLocator = contextLocator;
}
public User Get(Guid userId)
{
return _contextLocator.Get<MyDbContext>.Set<User>().Find(userId);
}
}