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

时间:2012-04-18 00:47:17

标签: c# entity-framework unit-of-work

我正在使用以下 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();
  }
}

但是,我遇到了一些问题,我有两个单独的类,它们为单个实体独立地实例化UnitOfWork,这意味着创建了该实体的两个存储库。当我尝试保存实体时,这会导致实体框架抛出“实体对象无法被IEntityChangeTracker的多个实例”引用时出现问题。

我想我需要更加密切地遵循UOW模式,如“创建工作单元类”部分中的 post by Tom Dykstra 所述,其中存储库存储在私有字段,如果存在则重用,如果为null则重新创建。我对此的问题是我希望我的UOW继续使用泛型 - Repository<T> - 而不必为我的代码中的所有实体类型声明私有存储库字段。

我如何能够存储已经实例化给定类型的存储库并重新使用它而不是创建它的新实例的事实?

---编辑:这是修复---

使用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();

2 个答案:

答案 0 :(得分:2)

问题不在多个存储库中,而是在多个DbContext实例中。 EF告诉您,您不能一次将同一实体实例添加到多个上下文。因此共享存储库无济于事。此外,拥有多个UoW共享单例存储库是通往地狱的道路。处理并发(EF不是线程安全的),状态等是非常困难的。

要解决此问题,您应该执行以下任一操作:

  1. 分享工作单元,以便在整个单一业务操作中使用相同的UoW(和DbContext)实例
  2. 在将实体实例添加到另一个UoW之前创建它们的副本。
  3. 选择取决于您的具体情况。

答案 1 :(得分:0)

你觉得这个解决方案怎么样?

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