Unity DI 使用 PerRequestLifetimeManager 注入 DbContext

Nic*_*lai 3 .net c# entity-framework unity-container


我有以下代码用 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);
Run Code Online (Sandbox Code Playgroud)

我使用PerRequestLifetimeManager所以我遵循了MSDN上的建议并在上面的代码末尾添加了新行:

DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
Run Code Online (Sandbox Code Playgroud)

但是在我放置之后。当页面(仅静态 html)加载时,我将 ajax 请求发送到我的 WebApi 控制器,该控制器调用GenericReposirory Get()抛出错误的方法:The operation cannot be completed because the DbContext has been disposed.
没有这行代码一切正常,但没有设置它可能不会处理上下文。
我的UnitOfWork班级:

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);
   }
}
Run Code Online (Sandbox Code Playgroud)

PS 我使用最新版本的Unity 3.5.1404
提前致谢。

编辑:

Repository 的 Get() 方法:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

ApiController 的Get()方法:

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;
}
Run Code Online (Sandbox Code Playgroud)

服务GetAll()方式:

public List<Theme> GetAll(int page = 1, int pageSize = 10)
{
    return UnitOfWork.ThemeRepository.Get(null, null, "Comments", page, pageSize).ToList();
}
Run Code Online (Sandbox Code Playgroud)

bcr*_*bcr 5

所以我会有这样的依赖结构:

  • UnitOfWork - 获取 DbContext
  • 存储库 - 获取工作单元
  • 服务 - 获取 Repository(ies)
  • ApiController - 获取服务

你会坚持使用 Unity 处理每个的生命周期。问题是,您希望服务具有请求范围,就像其他服务(UoW 和 Repos)一样。您可能以这种方式设置了服务生命周期,但我对 Unity 一无所知。我可以看到您确实有 UofW 和 repos 设置了请求生命周期。

最大的区别在于它UnitOfWork不依赖于存储库,而是相反。所以存储库基类DbSet<T>通过UnitOfWork它获得它,它有DbContext.你有一些方法,UnitOfWork它会返回一个IDbSet<T>就像你在DbContext.The上调用它一样,它UnitOfWork是一个包装器,DbContext它本身就是一个非常类似于工作单元的包装器。

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
}
Run Code Online (Sandbox Code Playgroud)

TheUnitOfWork将是类似的,但将 theDbContext作为依赖项(您可能已经有了 this 但省略了构造函数):

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>();
   ]
}
Run Code Online (Sandbox Code Playgroud)

该服务将注入存储库:

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.
}
Run Code Online (Sandbox Code Playgroud)

ApiController会得到所需要的服务注入,在这种情况下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;
}
Run Code Online (Sandbox Code Playgroud)

最终的想法是 Unity 容器处理所有依赖项的生命周期,并且UnitOfWork不必尝试管理存储库。你的线路

DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
Run Code Online (Sandbox Code Playgroud)

会留下来,DbContext由 Unity 处理,您不必Dispose()自己调用它。


Gab*_*mas 5

尝试使用 Microsoft.Practices.Unity.HierarchicalLifetimeManager 代替,\xc2\xb4s 方式:

\n\n
container.RegisterType<DbContext, VotingSystemContext>(new HierarchicalLifetimeManager(), new InjectionConstructor());\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n

Microsoft.Practices.Unity.HierarchicalLifetimeManager 提供:

\n\n
    \n
  1. 每次请求后调用 Dispose()
  2. \n
  3. 每个请求使用相同的 DbContext 实例
  4. \n
\n
\n\n

喜欢文章:https://jasenhk.wordpress.com/2013/06/11/unit-of-work-and-repository-pattern-with-unity-dependency-injection/

\n

  • 您不应该在每个请求中重复使用 DbContext (2认同)