【问题标题】:DI with UnitOfWork and Repository patterns具有 UnitOfWork 和存储库模式的 DI
【发布时间】:2014-04-26 20:47:37
【问题描述】:

作为一个学习过程,我尝试使用存储库模式来实现 UoW,并且我有以下设置:

public interface IUnitOfWork : IDisposable
{
  MyEntities Context { get; set; }

  string GetConnectionString()
  void Complete();
}

public abstract class RepositoryBase<TEntity> : IRepository<TEntity> where TEntity : class
{
  protected MyEntities context;

  protected DbSet<TEntity> dbSet;

  protected RepositoryBase(IUnitOfWork unitOfWork)
  {
     context = unitOfWork.Context;
     dbSet = context.Set<TEntity>();
  }
}

public class ItemRepository : RepositoryBase<Item>, IItemRepository
{
  public ItemRepository(IUnitOfWork unitOfWork)
     : base(unitOfWork)
  {
  }

  public Item GetById(int id)
  {
     // return a item
  }
 }

要实际使用 ItemRespoitory,我正在执行以下操作:

using (var unitOfWork = new UnitOfWork())
{
    var ItemRepository = new ItemRepository(unitOfWork);

    var item = ItemRepository.GetById(1);
}

效果很好,现在我想使用 IoC/DI 和简单的注入器来做到这一点:

var container = new Container();

container.Register<IUnitOfWork, UnitOfWork>();
container.Register<IItemRepository, ItemRepository>();

container.Verify();

DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));

但现在我的问题是,因为我将 ItemRepository 注入到控制器的构造函数中

public MyController(IItemRepository itemRepository)
{
   _itemRepository = itemRepository;
}

当我完成存储库后,我应该如何处置工作单元?因为我不必 创建一个工作单元并将其包装在 using 语句中。我这样做正确吗?在我的情况下,将 DI 与 UoW 和 repo 模式应用的正确方法应该是什么?谢谢。

【问题讨论】:

  • 我认为您可以通过仔细控制所涉及的所有内容的生命周期来解决它。我说它必须是每个请求的生命周期,但从有限的经验来看,生命周期并不总是他们看起来/声称要提供的。

标签: c# asp.net-mvc dependency-injection repository simple-injector


【解决方案1】:

您需要将您的数据服务注册为每个请求 - 对于上面的示例,它可以是Per Web RequestPer Web Api Request

在 nuget 上找到并安装 SimpleInjector.Integration.Web,然后更改:

 container.Register<IUnitOfWork, UnitOfWork>();
 container.Register<IItemRepository, ItemRepository>();

对此(假设 MVC):

 container.RegisterPerWebRequest<IUnitOfWork, UnitOfWork>();
 container.RegisterPerWebRequest<IItemRepository, ItemRepository>();

你就完成了。

【讨论】:

  • 开箱即用的原因是RegisterPerWebRequest 扩展方法利用了WebRequestLifestyleWebRequestLifestyle 继承自 ScopedLifestyle 基类,它允许确定性地处理缓存实例。换句话说,所有作用域的生活方式(例如LifetimeScopeLifestyleWebApiRequestLifestyleWebRequestLifestyle)将确保为该生活方式创建的实例将在作用域结束时被释放。
【解决方案2】:

如果您将工作单元 (UoW) 与存储库结合使用,更好的方法是设置 UoW 以将存储库作为属性保存,而不是将 Uow 注入每个存储库。

例子:

工作单元(接口):

public interface IUnitOfWork
{
    void Commit();
    IRepository<Store> Stores { get; }
    IRepository<Product> Products { get; }
}

存储库(接口):

public interface IRepository<T> where T : class
{
    IQueryable<T> GetAll();
    T GetById(Guid id);
    void Add(T entity);
    void Update(T entity);
    void Delete(T entity);
    void Delete(Guid id);
}

现在在您的控制器中,您将拥有:

public class ProductsController: Controller
{

    protected IUnitOfWork Uow { get; set; }

    public ProductsController(IUnitOfWork uow)
    {
        if (uow == null)
            throw new ArgumentNullException("Unit of Work");

        Uow = uow;
    }


}

然后像这样设置你的 IoC:

public static void RegisterIoc(HttpConfiguration config)
{
     var container = new Container();

     container.Register<IUnitOfWork, UnitOfWork>();
     container.Register<IRepository<Store>, Repository<Store>>();
     container.Register<IRepository<Product>, Repository<Product>>();

}

现在,当您需要来自 DbContextProduct 时,您只需访问注入到控制器中的单个 Uow。还要注意Commit 方法,这意味着在您的实现中只调用_dbContext.SaveChanges()。这就是工作单元的全部内容,对您的DbContext 进行一些更新调用并访问数据库一次。因此,您有一个类似 setup 的事务(意思是,如果出现问题代码,数据库中不会更新任何内容)。

请注意,这不是一个完全有效的示例,但它应该让您了解如何进行设置。如果你想要一个关于如何让这个工作的教程,你可以查看 John Papa 在 PluralSight 上的 Single Page Application 课程(这是此代码的基础)。但是他有一个非常复杂的设置(虽然非常灵活),开始可能有点多。

【讨论】:

  • 这样做的缺点是它违反了Open\Closed principle,因为每次添加新数据实体时都必须更改UnitOfWork。有关将UnitOfWork 注入Repository 的工作示例,请参阅here
  • @qujck 我从来没有意识到这一点。谢谢。
猜你喜欢
  • 2019-03-29
  • 2016-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多