简体   繁体   English

使用存储库在工作单元模式中的依赖注入

[英]Dependency injection in unit of work pattern using repositories

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

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. 使用setter注入。

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. 1的问题是,如果我注入所有存储库,我必须实例化每个存储库,即使我不在特定的工作单元实例中使用它们。 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. 2的问题是很容易忘记设置和结束空引用异常。

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. 解决这个问题的方法是不要让UnitOfWork负责通过Container注入创建每个Repository ,而是让每个Repository负责确保UnitOfWork在实例化时知道它的存在。

This will ensure that 这将确保这一点

  • your UnitOfWork doesn't need to change for each new Repository 您的UnitOfWork不需要为每个新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: 一些代码最好地证明了这一点 - 我使用SimpleInjector,因此示例基于此:

Starting with the Repository abstraction: Repository抽象开始:

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 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: 每个Repository 必须使用UnitOfWork注册自己,这可以通过更改抽象父类GenericRepository来完成,以确保完成:

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

Each real Repository inherits from the GenericRepository : 每个真实的Repository继承自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: 添加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());
    }
}

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: 容器注册可以设置为自动获取所有已定义的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<>).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. 通过这些抽象和围绕DI构建的体系结构,您有一个UnitOfWork ,它知道已在任何服务调用中实例化的所有Repository ,并且您具有编译时验证,即已定义了所有存储库。 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() 将这些行添加到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: 最后,运行此Console测试代码:

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. 您将发现代码在断点处停止,并且您已准备好一个IRepository活动实例,并等待Submit()对数据库的任何更改。

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 . 您可以装饰您的UnitOfWork以处理交易等。此时我会尊重强大的.NetJunkie,并建议您在此处此处阅读这两篇文章。

Instead of injecting repository instances inject single factory object which will be responsible for creating those instances. 注入单个工厂对象而不是注入存储库实例,这将负责创建这些实例。 Your getters will then use that factory. 然后,您的吸气剂将使用该工厂。

My solution is UnitOfWork still responsible for creating Repository but I made a GetRepository() factory method in UnitOfWork to do that. 我的解决方案是UnitOfWork仍负责创建Repository,但我在UnitOfWork中创建了一个GetRepository()工厂方法来执行此操作。

public interface IUnitOfWork : IDisposable
{
    T GetRepository<T>() where T : class;
    void Save();
}

public class UnitOfWork : IUnitOfWork
{
    private Model1 db;

    public UnitOfWork() :  this(new Model1()) { }

    public UnitOfWork(TSRModel1 dbContext)
    {
        db = dbContext;
    }

    public T GetRepository<T>() where T : class
    {          
        var result = (T)Activator.CreateInstance(typeof(T), db);
        if (result != null)
        {
            return result;
        }
        return null;
    }

    public void Save()
    {
        db.SaveChanges();
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                db.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

public class TestRepository : GenericRepository<Test>, ITestRepository
{
    public TestRepository(Model1 db)
       : base(db)
    {
    }
}

public class TestManager: ITestManager
{
    private IUnitOfWork unitOfWork;
    private ITestRepository testRepository;
    public TestManager(IUnitOfWork unitOfWork)
    {
        this.unitOfWork = unitOfWork;
        testRepository = unitOfWork.GetRepository<TestRepository>();
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用存储库以工作模式为单位的.Net依赖项注入 - .Net Dependency injection in unit of work pattern using repositories Repository Pattern 工作单元依赖注入 Ninject - Repository Pattern Unit of work Dependency Injection Ninject 工作单元的依赖注入 - Dependency Injection to Unit of Work 工作单元和依赖注入 - Unit Of Work and dependency Injection 如何使用工作单元实例化依赖项注入? - How dependency injection is instantiated using Unit of Work? CRUD的通用类 - 使用存储库和工作单元模式的C#中的依赖注入 - Generic Class for CRUD - Dependency Injection in C# using Repository and Unit Of Work Pattern Net Core:使用Xunit为工作单元模式和DBContext执行依赖注入 - Net Core: Execute Dependency Injection with Xunit for Unit of Work Pattern and DBContext 使用 Repository 模式、工作单元和 autofac 依赖注入实现 webapi - Implement webapi with Repository pattern, unit of work and autofac dependency injection 如何在C#中将依赖注入与工作单元和存储库一起使用? (不是基于Web的应用程序) - How do I use Dependency Injection with Unit of Work and Repositories in C#? (Not a web based App) Autofac 工作单元中的依赖注入 - Dependency Injection in Unit of work with Autofac
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM