Unity DI Inject DbContext with PerRequestLifetimeManager

12,548

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:

  1. A Dispose() call after each request
  2. 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/

Share:
12,548
Nicolai
Author by

Nicolai

#SOreadytohelp

Updated on June 27, 2022

Comments

  • Nicolai
    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.
    My UnitOfWork 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
    Nicolai almost 10 years
    Thanks 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
    bcr almost 10 years
    Yeah, 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
    Nicolai almost 10 years
    I tried to remove IDisposable with its Dispose method but the problem still persist.
  • Chris Paton
    Chris Paton over 9 years
    I'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
    Reynaldi almost 9 years
    Why dont you use DbContext interface (IDbContext) in unitofwork. So the unitofwork would be easy to test
  • Tsar Bomba
    Tsar Bomba over 7 years
    Very 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
    Issa Fram almost 6 years
    you should not reuse the DbContext per request