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

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

问题描述

我想创建一个工作单元类,它以类似于 这个.

我遇到的问题是试图通过用 IRepository 接口替换示例中的通用存储库来实现依赖注入.在链接文章的 uow 中,他们使用 getter 检查存储库是否已实例化,如果未实例化,则将其实例化.

public GenericRepository<部门>部门资料库{得到{如果(this.departmentRepository == null){this.departmentRepository = new GenericRepository(context);}返回部门存储库;}}

强耦合.

我可以看到两种解决方法.

  1. 使用构造函数注入.
  2. 使用 setter 注入.

1 的问题在于,如果我注入所有存储库,即使我不在该特定工作单元实例中使用它们,我也必须实例化每个存储库.从而产生这样做的开销.我想象着使用一个数据库范围的工作单元类,这样会导致大量不必要的实例化和一个巨大的构造函数.

2 的问题是很容易忘记设置并以空引用异常结束.

在这种情况下是否有任何最佳实践?还有我错过的其他选项吗?

我刚开始接触依赖注入,并且已经完成了我能找到的关于该主题的所有研究,但我可能遗漏了一些关键.

解决方案

解决这个问题的方法是不让 UnitOfWork 通过容器注入来负责创建每个 Repository,而是让每个 Repository 负责确保 UnitOfWork 在实例化时知道它的存在.

这将确保

  • 你的 UnitOfWork 不需要为每个新的 Repository
  • 改变
  • 您没有使用服务定位器(许多人认为是反-模式)

这最好用一些代码来演示 - 我使用 SimpleInjector 所以示例基于此:>

Repository 抽象开始:

公共接口IRepository{无效提交();}公共接口 IRepository:IRepository 其中 T : class { }公共抽象类 GenericRepository:IRepository<T>其中 T : 类 { }

UnitOfWork

公共接口IUnitOfWork{无效注册(IRepository存储库);无效提交();}

每个Repository 必须UnitOfWork注册自己,这可以通过改变抽象父类GenericRepository来完成> 确保完成:

公共抽象类GenericRepository:IRepository<T>其中 T : 班级{公共 GenericRepository(IUnitOfWork unitOfWork){unitOfWork.Register(this);}}

每个真正的Repository 都继承自GenericRepository:

公开课系{}公开课学生{}公共类 DepartmentRepository : GenericRepository{public DepartmentRepository(IUnitOfWork unitOfWork): base(unitOfWork) { }}公共类 StudentRepository : GenericRepository{公共StudentRepository(IUnitOfWork unitOfWork):base(unitOfWork){}}

添加UnitOfWork 的物理实现,一切就绪:

公共类 UnitOfWork : IUnitOfWork{private readonly Dictionary_存储库;公共工作单元(){_repositories = new Dictionary();}公共无效注册(IRepository存储库){_repositories.Add(repository.GetType().Name, repository);}公共无效提交(){_repositories.ToList().ForEach(x => x.Value.Submit());}}

可以将容器注册设置为自动获取所有定义的 IRepository 实例并使用生命周期范围注册它们,以确保它们在您的事务的生命周期内都存在:

公共静态类 BootStrapper{公共静态无效配置(容器容器){var lifetimeScope = new LifetimeScopeLifestyle();container.Register(lifetimeScope);container.RegisterManyForOpenGeneric(typeof(IRepository<>),终生范围,typeof(IRepository<>).Assembly);}}

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

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

公共类 SomeActivity{公共一些活动(IRepository<部门>部门){}}公共类 MainActivity{私有只读 IUnitOfWork _unitOfWork;公共 MainActivity(IUnitOfWork unitOfWork, SomeActivity 活动){_unitOfWork = unitOfWork;}公共无效测试(){_unitOfWork.Commit();}}

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

//注册测试类container.Register();container.Register();

在代码行上放置一个断点:

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

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

class 程序{静态无效主(字符串 [] args){容器容器 = new Container();BootStrapper.Configure(容器);容器验证();使用 (container.BeginLifetimeScope()){MainActivity entryPoint = container.GetInstance();入口点.test();}}}

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

你可以装饰你的 UnitOfWork 来处理事务等.在这一点上我会尊重强大的 .NetJunkie 并推荐你阅读这两篇文章 这里这里.

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