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

时间:2018-11-15 16:12:43

标签: wpf dependency-injection repository ninject dbcontext

我一直试图在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并开始将Context注入这些存储库/工作单元中。如何正确配置它以AWLAYS在我的工作单元中为存储库注入新的上下文。

我读到Ninject MVC具有.InRequestScope可以完全解决问题。但是WPF呢?您如何实现相同的注射?

我似乎找不到确切的解决方案/模式,或者也许有更好的方法可以做到这一点?任何建议和帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

我对这个问题的解决方案是创建一个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范围,但最终导致在注入两个单独的上下文时出现问题,一个在我的工作单位工厂中,另一个在我的存储库中。