依赖注入工作格局的单位使用信息库 [英] Dependency injection in unit of work pattern using repositories

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

问题描述

我想创建环绕以类似的方式存储库工作类的单位<一个href=\"http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application\">this.

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

问题我在试图通过这个例子与IRepository接口取代了通用的存储库来实现依赖注入。在他们使用的getter检查,如果存储库实例化和链接的文章中的UOW如果它不是那么实例化。

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;
    }
}

这是强耦合。

我可以看到两种方法解决这个问题。

I can see two ways around this.


  1. 使用构造器注入。

  2. 使用setter注入。

1的问题是,如果我注入所有的库我要每个实例库,即使我没有在工作实例的特定单位使用它们。因而导致这样的开销。我使用一个数据库全,工作类的单位所以这将导致大量不必要的初始化和一个巨大的构造想象。

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.

2个问题是,它会很容易忘记设置和空引用异常结束。

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.

推荐答案

解决这个是不是让的UnitOfWork 负责创建每个库通过容器注入,而是要让它每个责任来确保的UnitOfWork 知道在实例它的存在。

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.

这将确保


  • 的UnitOfWork 并不需要更改每个新的

  • 您没有使用服务定位器(被许多人认为是反模式

  • 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)

这是最好的一些code证明 - 我使用 SimpleInjector 这样的例子是基于解​​决此问题:

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

抽象开始:

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

的UnitOfWork

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

每个必须的使用的UnitOfWork 注册本身,这可以这样做改变抽象父类 GenericRepository ,以确保它完成的:

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);
    }
}

每个真正的继承的 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) { }
}

在添加物理实施的UnitOfWork 和你所有的设置:

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());
    }
}

容器注册可设置为自动拿起 IRepository 的所有定义的实例,并提供终身范围内进行注册,以确保它们全部存活的寿命你交易:

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);
    }
}

使用这些抽象和建筑周围DI建有一个所有这一切都知道的UnitOfWork 的那被实例化的服务调用内,你必须编译所有的仓库都被定义时验证。您code是对扩展开放,但对修改封闭

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();
    }
}

这些行添加到 BootStrapper.Configure()

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

将一个破发点对code线:

Put a break-point against the line of code:

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

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

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();
        }
    }
}

您会发现code停在破发点,你有一个 IRepository 准备和等待,以提交一个活动实例()对数据库的任何更改。

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.

您可以装饰你的UnitOfWork处理事务等。我会推迟到浩浩荡荡.NetJunkie在这一点上我建议你阅读这两篇文章的这里这里

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天全站免登陆