Unity DI Inject DbContext with PerRequestLifetimeManager
Solution 1
So I would have the dependency structure like so:
- UnitOfWork - gets DbContext
- Repository - gets UnitofWork
- Service - gets Repository(ies)
- ApiController - gets Service(s)
and you'd stick with Unity handling the lifetime of each. The thing is though, you'd want the Services to have request scope, just like the others (UoW and Repos). You may have the service lifetime set up that way, but I don't know Unity off the top of my head. I can see that you do have the UofW and repos set with request lifetimes.
The big difference being that the UnitOfWork
doesn't have a dependency on repositories, but rather the other way around. So the repository base class gets its DbSet<T>
via the UnitOfWork
which has the DbContext.
You'd have some method on UnitOfWork
that would return an IDbSet<T>
just as if you were calling that on the DbContext.
The UnitOfWork
being a wrapper for DbContext
which in itself is pretty Unit of Work-like.
public sealed class GenericRepository<T> : IRepository<T> where T : BaseEntity
{
private readonly IDbSet<T> _dbSet;
private readonly IUoW _uoW;
public GenericRepository(IUoW unitOfWork)
{
_uoW = unitOfWork;
_dbSet = _uoW.Set<T>();
}
public IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "", int? page = null, int? pageSize = null)
{
IQueryable<TEntity> query = _dbSet;
if (filter != null)
{
query = query.Where(filter);
}
List<string> properties = includeProperties.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
properties.ForEach(property =>
{
query = query.Include(property);
});
if (orderBy != null)
{
query = orderBy(query);
}
if (page != null && pageSize != null)
{
query = query.Skip((page.Value - 1) * pageSize.Value).Take(pageSize.Value);
}
return query;
}
// other methods like Delete, Update and GetById
}
The UnitOfWork
would be similar, but take the DbContext
as the dependency (you may already have this but omitted the constructor):
public class UnitOfWork : IUnitOfWork
{
private readonly VotingSystemContext _context;
private bool _disposed;
public UnitOfWork(DbContext context)
{
_context = context;
}
public IDbSet<T> Set<T>()
{
return _context.Set<T>();
]
}
The service would have the repository injected:
public class ThemeService
{
private IRepository<Theme> ThemeRepository { get; set; }
public ThemeService(IRepository<Theme> themeRepo)
{
ThemeRepository = themeRepo;
}
public List<Theme> GetAll(int page = 1, int pageSize = 10)
{
return ThemeRepository.Get(null, null, "Comments", page, pageSize).ToList();
}
// etc.
}
The ApiController
would get the needed services injected, in this case the ThemeService
:
public class ApiController ThemeController
{
private ThemeService _themeService;
public ThemeController(ThemeService service) // along with any other needed services
{
_themeService = service;
}
public IEnumerable<VotingModel> Get(int page = 1, int size = 10)
{
//get all themes
List<Theme> themes = _themeService.GetAll(page, size);
//convert themes to VotingModel (same model as Theme just without converting system throw an error about serializing object and also add new filed UserName).
List<VotingModel> model = themes.Select(t =>
{
MembershipUser membershipUser = Membership.GetUser(t.UserId ?? -1);
return t.ToVotingModel(membershipUser != null ? membershipUser.UserName : string.Empty);
}).ToList();
return model;
}
The ultimate idea being that the Unity container handles the lifetime of all the dependencies and the UnitOfWork
doesn't have to try to manage repositories. Your line
DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
would stay and the DbContext
would get disposed by Unity, and you don't have to call Dispose()
on it yourself.
Solution 2
Try to use Microsoft.Practices.Unity.HierarchicalLifetimeManager instead, that´s way:
container.RegisterType<DbContext, VotingSystemContext>(new HierarchicalLifetimeManager(), new InjectionConstructor());
The Microsoft.Practices.Unity.HierarchicalLifetimeManager provides:
- A Dispose() call after each request
- The same DbContext instance per request
Like article: https://jasenhk.wordpress.com/2013/06/11/unit-of-work-and-repository-pattern-with-unity-dependency-injection/
Comments
-
Nicolai almost 2 years
I have the following code that initialize instances with Unity:IUnityContainer container = new UnityContainer(); container.RegisterType<DbContext, VotingSystemContext>(new PerRequestLifetimeManager(), new InjectionConstructor()); container.RegisterType(typeof(IGenericRepository<>), typeof(GenericRepository<>)); container.RegisterType<IUnitOfWork, UnitOfWork>(new PerRequestLifetimeManager()); container.RegisterTypes( AllClasses.FromAssemblies( Assembly.GetAssembly(typeof(IUserService)), Assembly.GetAssembly(typeof(UserService))), WithMappings.FromMatchingInterface, WithName.Default, WithLifetime.PerResolve); DependencyResolver.SetResolver(new Unity.Mvc4.UnityDependencyResolver(container)); GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
I use
PerRequestLifetimeManager
so I followed the suggestion on MSDN and added new line at the end of the code above:DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
But after I placed it. When the page(only static html) is loaded I send ajax request to my WebApi constroller that calls
GenericReposirory
Get()
method that throw error:The operation cannot be completed because the DbContext has been disposed.
Without this line of code everything works OK, but without setting it probably the context will not be dispose.
MyUnitOfWork
class:public class UnitOfWork : IUnitOfWork, IDisposable { private readonly VotingSystemContext _context; private bool _disposed; //GenericRepository properties private void Dispose(bool disposing) { if (!_disposed) { if (disposing) { _context.Dispose(); } } _disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } }
P.S. I use the latest version of Unity 3.5.1404.
Thanks in advance.EDIT:
Get() method of Repository:public sealed class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : BaseEntity { public GenericRepository(VotingSystemContext context) { _context = context; _dbSet = context.Set<TEntity>(); } private readonly DbSet<TEntity> _dbSet; private readonly VotingSystemContext _context; public IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "", int? page = null, int? pageSize = null) { IQueryable<TEntity> query = _dbSet; if (filter != null) { query = query.Where(filter); } List<string> properties = includeProperties.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); properties.ForEach(property => { query = query.Include(property); }); if (orderBy != null) { query = orderBy(query); } if (page != null && pageSize != null) { query = query.Skip((page.Value - 1) * pageSize.Value).Take(pageSize.Value); } return query; } // other methods like Delete, Update and GetById } }
ApiController's
Get()
method:public IEnumerable<VotingModel> Get(int page = 1, int size = 10) { //get all themes List<Theme> themes = _themeService.GetAll(page, size); //convert themes to VotingModel (same model as Theme just without converting system throw an error about serializing object and also add new filed UserName). List<VotingModel> model = themes.Select(t => { MembershipUser membershipUser = Membership.GetUser(t.UserId ?? -1); return t.ToVotingModel(membershipUser != null ? membershipUser.UserName : string.Empty); }).ToList(); return model; }
Service
GetAll()
method:public List<Theme> GetAll(int page = 1, int pageSize = 10) { return UnitOfWork.ThemeRepository.Get(null, null, "Comments", page, pageSize).ToList(); }
-
Nicolai almost 10 yearsThanks for your answer. But the main reason I have UoW is that I want to have all repositories in one place because some of my Service classes use more than one repository and I want to pass only
IUnitOfWork
in constructor. -
bcr almost 10 yearsYeah, as I said you could inject more than one repository into a service by adding them to the constructor arguments. But I think you might want to try taking the Dispose code out of your UnitOfWork and let the Unity container both inject the DbContext and manage its lifetime, to avoid the problem in your OP.
-
Nicolai almost 10 yearsI tried to remove
IDisposable
with itsDispose
method but the problem still persist. -
Chris Paton over 9 yearsI've +1'd this because it is a great example of how to structure an application. There is so much discrepancy out there on how to use UnitOfWork and Repository patterns. This is one of the first examples I've seen that makes a lot of structural sense. Nice.
-
Reynaldi almost 9 yearsWhy dont you use DbContext interface (IDbContext) in unitofwork. So the unitofwork would be easy to test
-
Tsar Bomba over 7 yearsVery nice, this is mostly how I built the reference architecture I use for new projects. It's worth noting that if you don't add the "RegisterModule" line in your Unity config class, Dispose will not get called. We ran into a scenario at work where this has caused several hanging connections left open, and a high ASYNC_NETWORK_IO count is SQL Server. That's our primary suspect, anyhow.
-
Issa Fram almost 6 yearsyou should not reuse the DbContext per request