在 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 范围,但最终导致注入两个单独的上下文出现问题,一个在我的工作单元工厂中,一个在我的存储库中。
我一直在尝试在 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 范围,但最终导致注入两个单独的上下文出现问题,一个在我的工作单元工厂中,一个在我的存储库中。