使用仓库的工作模式的依赖注入 [英] Dependency injection in unit of work pattern using repositories

查看:95
本文介绍了使用仓库的工作模式的依赖注入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个工作类单元,以类似于的mvc / implementation-the-repository-and-work-of-work-pattern-in-a-asp-net-mvc-application =noreferrer>



我遇到的问题是尝试使用IRepository接口替换示例中的通用存储库来实现依赖注入。在链接的文章中,他们使用getter来检查存储库是否被实例化,如果它不是实例化的。

  public GenericRepository< Department> DepartmentRepository 
{
get
{
if(this.departmentRepository == null)
{
this.departmentRepository = new GenericRepository< Department>(context) ;
}
return departmentRepository;
}
}

哪个是强耦合的。



我可以看到两种方法。


  1. 使用构造函数注入。

  2. 使用setter注入。

1的问题是如果我注入所有的存储库,我必须实例化每个存储库如果我不在这个特定的工作单位中使用它们。因此导致这样做的开销。我想象的是使用一个数据库范围的工作类单位,这样会导致很多不必要的实例化和巨大的构造函数。



2的问题是很容易忘记设置和结束空引用例外。



在这种情况下有什么样的最佳做法吗?还有其他的选择我错过了吗?



我刚刚进入依赖注入,并已经做了所有的研究,我可以找到的主题,但我可能会缺少一些键。

解决方案

一种方法来解决这个问题,是不要使 UnitOfWork 负责通过容器注入来创建每个 Repository ,而是使每个资源库的责任确保 UnitOfWork 知道它在实例化时的存在。



这将确保





这是最好的演示与一些代码 - 我使用 SimpleInjector ,所以这些例子是基于这个:



存储库抽象:

  public interface IRepository 
{
void Submit();
}
public interface IRepository< T> :IRepository其中T:class {}
public abstract class GenericRepository< T> :IRepository< T>其中T:class {}

UnitOfWork

  public interface IUnitOfWork 
{
void Register(IRepository repository);
void Commit();
}

每个存储库 em>必须使用 UnitOfWork 注册自己,这可以通过更改抽象父类 GenericRepository 以确保完成:

  public abstract class GenericRepository< T> :IRepository< T>其中T:class 
{
public GenericRepository(IUnitOfWork unitOfWork)
{
unitOfWork.Register(this);
}
}

每个真实的 code>继承自 GenericRepository

 公共课部门{} 
public class Student {}

public class DepartmentRepository:GenericRepository< Department>
{
public DepartmentRepository(IUnitOfWork unitOfWork):base(unitOfWork){}
}

public class StudentRepository:GenericRepository&Student
{
public StudentRepository(IUnitOfWork unitOfWork):base(unitOfWork){}
}

加入 UnitOfWork 的物理实现,您都设置了:

  public class UnitOfWork:IUnitOfWork 
{
private readonly Dictionary< string,IRepository> _repositories;
public UnitOfWork()
{
_repositories = new Dictionary< string,IRepository>();
}

public void注册(IRepository存储库)
{
_repositories.Add(repository.GetType()。Name,repository);
}

public void Commit()
{
_repositories.ToList()。ForEach(x => x.Value.Submit());
}
}

容器注册可以设置为自动提取所有定义的 IRepository 实例,并将其注册到终身的范围,以确保他们在您的交易的一生中都能够幸存下来:



public static class BootStrapper
{
public static void Configure(Container container)
{
var lifetimeScope = new LifetimeScopeLifestyle();

container.Register< IUnitOfWork,UnitOfWork>(lifetimeScope);

container.RegisterManyForOpenGeneric(
typeof(IRepository<)),
lifetimeScope,
typeof(IRepository&))。
}
}

使用这些抽象和一个围绕DI构建的架构一个 UnitOfWork 知道在任何服务调用中已被实例化的所有 Repository ,并且您有编译时验证您的所有存储库都已被定义。您的代码是打开扩展程序,但关闭修改



要测试所有这些 - 添加这些类

  public class SomeActivity 
{
public SomeActivity(IRepository&Department> departments){}
}

public class MainActivity
{
private readonly IUnitOfWork _unitOfWork;
public MainActivity(IUnitOfWork unitOfWork,SomeActivity activity)
{
_unitOfWork = unitOfWork;
}

public void test()
{
_unitOfWork.Commit();
}
}

将这些行添加到 BootStrapper .Configure()

  //注册测试类
container.Register< SomeActivity> ;();
container.Register< MainActivity>();

对代码行设一个断点:

  _repositories.ToList()。ForEach(x => x.Value.Submit()); 

最后,运行此控制台测试代码:



$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $
$
BootStrapper.Configure(container);
container.Verify();
using(container.BeginLifetimeScope())
{
MainActivity entryPoint = container.GetInstance< MainActivity>();
entryPoint.test();
}
}
}

你会发现代码在断点处停止,并且您有一个活动的实例 IRepository 准备好,等待 Submit()任何更改数据库。



您可以装饰您的UnitOfWork来处理事务等。我将推迟到强大的.NetJunkie,并建议您阅读这两篇文章这里 here


I want to create a unit of work class that wraps around repositories in a similar way to this.

The problem I'm having is trying to implement dependency injection by replacing the generic repositories in the example with an IRepository interface. In the uow in the linked article they use getters to check if the repository is instantiated and if it isn't then instantiate it.

public GenericRepository<Department> DepartmentRepository
{
    get
    {
        if (this.departmentRepository == null)
        {
            this.departmentRepository = new GenericRepository<Department>(context);
        }
        return departmentRepository;
    }
}

Which is strongly coupled.

I can see two ways around this.

  1. Use constructor injection.
  2. Use setter injection.

The problem with 1 is that if I inject all the repositories I have to instantiate each repository even if I don't use them in that particular unit of work instance. Thus incurring the overhead of doing so. I was imagining using one, database-wide, unit of work class so this would lead to a lot of needless instantiating and a gigantic constructor.

The problem with 2 is that it would be easy to forget to set and end up with null reference exceptions.

Is there any sort of best practices in this scenario? And are there any other options I have missed?

I'm just getting in to dependency injection and have done all the research I can find on the topic but I may be missing something key.

解决方案

A way to approach this is to not make the UnitOfWork responsible for creating each Repository through Container injection, but instead to make it the responsibility of each Repository to ensure that the UnitOfWork knows of its existence upon instantiation.

This will ensure that

  • your UnitOfWork doesn't need to change for each new Repository
  • you are not using a service locator (considered by many to be an anti-pattern)

This is best demonstrated with some code - I use SimpleInjector so the examples are based around this:

Starting with the Repository abstraction:

public interface IRepository 
{
    void Submit();
}
public interface IRepository<T> :IRepository where T : class { }
public abstract class GenericRepository<T> : IRepository<T> where T : class { }

and the UnitOfWork

public interface IUnitOfWork
{
    void Register(IRepository repository);
    void Commit();
}

Each Repository must register itself with the UnitOfWork and this can be done by changing the abstract parent class GenericRepository to ensure it is done:

public abstract class GenericRepository<T> : IRepository<T> where T : class
{
    public GenericRepository(IUnitOfWork unitOfWork)
    {
        unitOfWork.Register(this);
    }
}

Each real Repository inherits from the GenericRepository:

public class Department { }
public class Student { }

public class DepartmentRepository : GenericRepository<Department> 
{
    public DepartmentRepository(IUnitOfWork unitOfWork): base(unitOfWork) { }
}

public class StudentRepository : GenericRepository<Student>
{
    public StudentRepository(IUnitOfWork unitOfWork) : base(unitOfWork) { }
}

Add in the physical implementation of UnitOfWork and you're all set:

public class UnitOfWork : IUnitOfWork
{
    private readonly Dictionary<string, IRepository> _repositories;
    public UnitOfWork()
    {
        _repositories = new Dictionary<string, IRepository>();
    }

    public void Register(IRepository repository)
    {
        _repositories.Add(repository.GetType().Name, repository);
    }

    public void Commit()
    {
        _repositories.ToList().ForEach(x => x.Value.Submit());
    }
}

The container registration can be set up to automatically pick up all the defined instances of IRepository and register them with a lifetime scope to ensure they all survive for the lifetime of your transaction:

public static class BootStrapper
{
    public static void Configure(Container container)
    {
        var lifetimeScope = new LifetimeScopeLifestyle();

        container.Register<IUnitOfWork, UnitOfWork>(lifetimeScope);

        container.RegisterManyForOpenGeneric(
            typeof(IRepository<>),
            lifetimeScope,
            typeof(IRepository<>).Assembly);
    }
}

With these abstractions and an architecture built around DI you have a UnitOfWork that knows of all Repository's that have been instantiated within any service call and you have compile time validation that all of your repositories have been defined. Your code is open for extension but closed for modification.

To test all this - add these classes

public class SomeActivity
{
    public SomeActivity(IRepository<Department> departments) { }
}

public class MainActivity
{
    private readonly IUnitOfWork _unitOfWork;
    public MainActivity(IUnitOfWork unitOfWork, SomeActivity activity) 
    {
        _unitOfWork = unitOfWork;
    }

    public void test()
    {
        _unitOfWork.Commit();
    }
}

Add these lines to BootStrapper.Configure()

//register the test classes
container.Register<SomeActivity>();
container.Register<MainActivity>();

Put a break-point against the line of code:

_repositories.ToList().ForEach(x => x.Value.Submit());

And finally, run this Console test code:

class Program
{
    static void Main(string[] args)
    {
        Container container = new Container();
        BootStrapper.Configure(container);
        container.Verify();
        using (container.BeginLifetimeScope())
        {
            MainActivity entryPoint = container.GetInstance<MainActivity>();
            entryPoint.test();
        }
    }
}

You'll find the code stops at the break point and you have one active instance of a IRepository ready and waiting to Submit() any changes to the database.

You can decorate your UnitOfWork to handle transactions etc. I will defer to the mighty .NetJunkie at this point and recommend you read these two articles here and here.

这篇关于使用仓库的工作模式的依赖注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆