【问题标题】:Unity DI Inject DbContext with PerRequestLifetimeManagerUnity DI 使用 PerRequestLifetimeManager 注入 DbContext
【发布时间】:2019-10-18 23:38:11
【问题描述】:


我有以下代码用 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);

我使用PerRequestLifetimeManager,所以我按照MSDN 上的建议在上面的代码末尾添加了新行:

DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));

但是在我放置之后。加载页面(仅静态 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);
   }
}

附:我使用最新版本的 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
    }
}

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;
}

服务GetAll()方法:

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

【问题讨论】:

  • 查看您的存储库 Get() 代码以及您的控制器可能会有所帮助。
  • @bcr,你好,我添加了其他信息。
  • 我用这个人的解决方案:thorarin.net/blog/post/2013/02/12/….
  • 我没有看到 UnitOfWork 类的构造函数。它是否注入了DbContext?此外,该服务是否注入了UnitOfWork?没有更多信息,这里的链条似乎有点不稳定。
  • @bcr,这两个问题的答案都是肯定的。

标签: c# .net entity-framework unity-container


【解决方案1】:

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

  • UnitOfWork - 获取 DbContext
  • 存储库 - 获取 UnitofWork
  • 服务 - 获取存储库
  • ApiController - 获取服务

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

最大的区别在于UnitOfWork 不依赖于存储库,而是相反。因此,存储库基类通过具有 DbContext.UnitOfWork 获取其 DbSet&lt;T&gt; 您将在 UnitOfWork 上有一些方法,该方法将返回 IDbSet&lt;T&gt;,就像您在 DbContext. 上调用它一样UnitOfWorkDbContext 的包装器,它本身非常类似于工作单元。

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
}

UnitOfWork 与此类似,但将DbContext 作为依赖项(您可能已经有了它但省略了构造函数):

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>();
   ]
}

该服务将注入存储库:

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.
}

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;
}

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

DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));

会留下来,DbContext 会被 Unity 处理掉,你不必自己打电话给Dispose()

【讨论】:

  • 感谢您的回答。但我拥有 UoW 的主要原因是我希望将所有存储库放在一个地方,因为我的一些服务类使用多个存储库,并且我只想在构造函数中传递 IUnitOfWork
  • 是的,正如我所说,您可以通过将多个存储库添加到构造函数参数来将多个存储库注入到服务中。但我认为您可能想尝试将 Dispose 代码从您的 UnitOfWork 中取出并让 Unity 容器注入 DbContext 并管理其生命周期,以避免您的 OP 中出现问题。
  • 我尝试使用Dispose 方法删除IDisposable,但问题仍然存在。
  • 我为此 +1 了,因为它是如何构建应用程序的一个很好的例子。关于如何使用 UnitOfWork 和 Repository 模式存在很大差异。这是我见过的第一个具有结构意义的例子。不错。
  • 为什么不在unitofwork中使用DbContext接口(IDbContext)。所以工作单元很容易测试
【解决方案2】:

尝试改用 Microsoft.Practices.Unity.HierarchicalLifetimeManager,这样:

container.RegisterType<DbContext, VotingSystemContext>(new HierarchicalLifetimeManager(), new InjectionConstructor());

Microsoft.Practices.Unity.HierarchicalLifetimeManager 提供:

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

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

【讨论】:

  • 您不应该在每个请求中重复使用 DbContext
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-05
相关资源
最近更新 更多