简体   繁体   English

在 UnitOfWork 中存储对通用存储库的引用

[英]Store References to Generic Repositories in UnitOfWork

I am using the following UnitOfWork class in order to get repositories for my entities by using generics.我正在使用以下UnitOfWork类,以便通过使用泛型为我的实体获取存储库。

public class UnitOfWork : IUnitOfWork
{
  private readonly EfDbContext _context;

  public UnitOfWork()
  {
    _context = new EfDbContext();
  }

  public IRepository<T> RepositoryFor<T>() where T : Entity
  {
    return new Repository<T>(_context);
  }

  public void Save()
  {
    _context.SaveChanges();
  }
}

However, I have run into some problems where I have two separate classes that instantiate the UnitOfWork independently for a single Entity, meaning that two repositories for that Entity are created.但是,我遇到了一些问题,我有两个单独的类为单个实体独立实例化 UnitOfWork,这意味着为该实体创建了两个存储库。 This causes problems with Entity Framework throwing " An entity object cannot be referenced by multiple instances of IEntityChangeTracker " errors when I attempt save the Entity.当我尝试保存实体时,这会导致实体框架抛出“多个 IEntityChangeTracker 实例无法引用实体对象”错误的问题。

I am thinking that I need to more closely follow the UOW pattern as described in this post by Tom Dykstra under the "Creating the Unit of Work Class" section where the repository is stored in a private field and reused if existing or created if null.我认为我需要更密切地遵循Tom Dykstra在“创建工作单元类”部分下的这篇文章中描述的 UOW 模式,其中存储库存储在私有字段中,如果存在则重用,如果为空则创建。 The issue that I have with this is that I want my UOW to continue using generics - Repository<T> - and not have to declare private repository fields for all the entity types I have in my code.我遇到的问题是我希望我的 UOW 继续使用泛型 - Repository<T> - 而不必为我的代码中的所有实体类型声明私有存储库字段。

How might I be able to store the fact that a repository of a given type has already been instantiated and to re-use that instead of creating a new instance of it?我如何能够存储给定类型的存储库已经实例化的事实并重新使用它而不是创建它的新实例?

--- EDIT: Here's the fix --- --- 编辑:这是修复---

Using Ninject, I adjusted my bindings to only instantiate a single instance of both the repositories and UOW by specifying InRequestScope使用 Ninject,我调整了我的绑定,通过指定InRequestScope仅实例化存储库和 UOW 的单个实例

kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>)).InRequestScope();
//kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope(); // Removed based on comment below
kernel.Bind(typeof(EfDbContext)).ToSelf().InRequestScope();

The problem is not in multiple repositories, but in multiple DbContext instances.问题不在于多个存储库,而在于多个DbContext实例。 EF tells you that you can't add the same entity instance to multiple contexts at once. EF 告诉您不能一次将相同的实体实例添加到多个上下文中。 So sharing repositories won't help.因此共享存储库无济于事。 Moreover, having multiple UoW sharing singleton repositories is the road to hell.此外,拥有多个 UoW 共享单例存储库是通往地狱的道路。 It will be very difficult to handle concurrency (EF isn't thread-safe), state, etc.处理并发(EF 不是线程安全的)、状态等将非常困难。

To fix the issue you should do either:要解决此问题,您应该执行以下任一操作:

  1. share Units of Work so that you use the same instance of UoW (and DbContext ) throughout a single business operation共享工作单元,以便您在单个业务操作中使用相同的 UoW(和DbContext )实例
  2. create copies of entity instances before adding them to another UoW.在将实体实例添加到另一个 UoW 之前创建实体实例的副本。

The choice depends on your specific situation.选择取决于您的具体情况。

what do you think about this solution?你怎么看这个解决方案?

public class UnitOfWork
    {
        private IRepository<Animal> _animalRepository;
        public IRepository<Animal> Animals => _animalRepository ??= new Repository<Animal>();

        public IRepository<T> Repository<T>()
        {
            var result = GetType()
                .GetProperties()
                .FirstOrDefault(r => typeof(IRepository<T>) == r.PropertyType)?
                .GetValue(this, null);

            return (IRepository<T>)result;
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM