在 WPF 中使用工作单元模式依赖注入正确处理上下文

Properly Disposing a context with Unit of Work Pattern Dependency Injection in WPF

我一直在尝试在 WPF 的工作单元/存储库模式中使用 DI。我目前 运行 遇到的问题是,如果我调用 _UserRepo.Add(User) 之类的存储库并抛出异常。每次对存储库的新调用都会引发异常,因为上下文永远不会被释放。

我试过的

工作单元

 public class UnitOfWork : IUnitOfWork
{
    private DbContextTransaction _trans;
    private BomConfiguratorContext _context;

    public UnitOfWork(BomConfiguratorContext context)
    {
        _context = context;
        _trans = context.Database.BeginTransaction();
    }

    public void Dispose()
    {
        try
        {
            _context.SaveChanges();
            _trans.Commit();
        }
        catch (Exception)
        {
            _trans.Rollback();
        }
        finally
        {
            _context.Dispose(); //This obviously does not work
        }
    }
}

工作单位工厂

 public class UnitOfWorkFactory : IUnitOfWorkFactory
{
    private BomConfiguratorContext _context;
    public UnitOfWorkFactory(BomConfiguratorContext context)
    {
        _context = context;
    }
    public UnitOfWork Create()
    {
        return new UnitOfWork(_context);
    }
}

我的通用存储库

public interface IRepository<TEntity> where TEntity : class
{
    void Add(TEntity entity);
    void AddRange(IEnumerable<TEntity> entities);

    void Remove(TEntity entity);
    void RemoveRange(IEnumerable<TEntity> entities);

    TEntity Get(int id);
    IEnumerable<TEntity> GetAll();
    IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);

    void Update(TEntity entity);
}

通用存储库实现

 public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    protected readonly BomConfiguratorContext Context;

    public Repository(BomConfiguratorContext context)
    {
        Context = context;
    }
    public virtual void Add(TEntity entity)
    {
        Context.Set<TEntity>().Add(entity);
    }

    public void AddRange(IEnumerable<TEntity> entities)
    {
        Context.Set<TEntity>().AddRange(entities);
    }

    public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
    {
        return Context.Set<TEntity>().Where(predicate);
    }

    public TEntity Get(int id)
    {
        return Context.Set<TEntity>().Find(id);
    }

    public IEnumerable<TEntity> GetAll()
    {
        return Context.Set<TEntity>().ToList();
    }

    public void Remove(TEntity entity)
    {
        Context.Set<TEntity>().Remove(entity);
    }

    public void RemoveRange(IEnumerable<TEntity> entities)
    {
        Context.Set<TEntity>().RemoveRange(entities);
    }
    public void Update(TEntity entity)
    {
        Context.Set<TEntity>().Attach(entity);
        Context.Entry(entity).State = System.Data.Entity.EntityState.Modified;
    }
}

用户存储库

public class UserRepository : Repository<User>,IUserRepository
{
    public UserRepository(BomConfiguratorContext context)
        :base(context)
    {

    }
}

用例

using (var UOW = _UnitOfWorkFactory.Create())
{
     //Submit the user
     _UserRepository.Add(ExampleNewUser);

}

所以目前我正在使用 MVVM Light 来完成我所有的 DI 工作,现在我知道使用 mvvm light 你只能注入单例范围。所以我很确定我最终将不得不切换到 Ninject 这样的东西,这样我就可以利用他们的 .InTransientScope 或 .InNamedScope(根据我一直在阅读的内容)。

显然上述代码不适用于 MVVM Light,因为上下文从未被正确处理过。

问题

所以我要问你的问题是,我是否要换成使用 Ninject 并开始将我的上下文注入这些存储库/工作单元。我如何正确配置它以 AWLAYS 在我的存储库工作单元中注入新的上下文。

我读到 Ninject MVC 有 .InRequestScope 可以完全解决问题。但是对于 WPF 呢?怎么实现同种注入?

我似乎找不到确切的 solution/pattern 或者也许有更好的方法来做到这一点?任何建议和帮助将不胜感激。

我的解决方案是创建一个 ContextFactory。

界面

public interface IContextFactory
{
    BomConfiguratorContext Create();
    BomConfiguratorContext Get();
}

上下文工厂

工厂允许我获取现有上下文或创建新上下文。

public class ContextFactory : IContextFactory
{
    private BomConfiguratorContext _context;

    public ContextFactory(BomConfiguratorContext context)
    {
        _context = context;
    }

    public BomConfiguratorContext Create()
    {
        _context = new BomConfiguratorContext();
        return _context;
    }

    public BomConfiguratorContext Get()
    {
        return _context;
    }
}

新基础存储库

通过调用 ContextFactory.Get() 方法,我使用缓存的上下文而不是创建新的上下文。

 public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    protected readonly IContextFactory ContextFactory;

    public Repository(IContextFactory factory)
    {
        ContextFactory = factory;
    }
    public virtual void Add(TEntity entity)
    {

        ContextFactory.Get().Set<TEntity>().Add(entity);
    }

    public void AddRange(IEnumerable<TEntity> entities)
    {

        ContextFactory.Get().Set<TEntity>().AddRange(entities);

    }

    public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
    {
        return ContextFactory.Get().Set<TEntity>().Where(predicate);
    }

    public TEntity Get(int id)
    {
        return ContextFactory.Get().Set<TEntity>().Find(id);
    }

    public IEnumerable<TEntity> GetAll()
    {
        return ContextFactory.Get().Set<TEntity>().ToList();
    }

    public void Remove(TEntity entity)
    {
        ContextFactory.Get().Set<TEntity>().Remove(entity);
    }

    public void RemoveRange(IEnumerable<TEntity> entities)
    {
        ContextFactory.Get().Set<TEntity>().RemoveRange(entities);
    }
    public void Update(TEntity entity)
    {
        ContextFactory.Get().Set<TEntity>().Attach(entity);
        ContextFactory.Get().Entry(entity).State = System.Data.Entity.EntityState.Modified;
    }
}

工厂新单位

当调用工厂的 Create() 方法时,我调用上下文工厂的 Create() 方法来创建新的上下文。

public class UnitOfWorkFactory : IUnitOfWorkFactory
{
    private IContextFactory _contextFactory;

    public UnitOfWorkFactory(IContextFactory factory)
    {
        _contextFactory = factory;
    }
    public UnitOfWork Create()
    {
        return new UnitOfWork(_contextFactory.Create());
    }
}

通过这种方式,我现在可以将我的上下文工厂注入到我所有的存储库中。我试图在原始问题中使用上面提到的 Ninject 范围,但最终导致注入两个单独的上下文出现问题,一个在我的工作单元工厂中,一个在我的存储库中。