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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16064902/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 23:53:26  来源:igfitidea点击:

Dependency injection in unit of work pattern using repositories

c#entity-frameworkdependency-injectionunit-of-work

提问by rashleighp

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.

我遇到的问题是试图通过用 IRepository 接口替换示例中的通用存储库来实现依赖注入。在链接文章的 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.
  1. 使用构造函数注入。
  2. 使用 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. 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.

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

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.

我刚刚进入依赖注入并完成了我能找到的关于这个主题的所有研究,但我可能会遗漏一些关键的东西。

采纳答案by qujck

A way to approach this is to not make the UnitOfWorkresponsible for creating each Repositorythrough Container injection, but instead to make it the responsibility of each Repositoryto ensure that the UnitOfWorkknows of its existence upon instantiation.

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

This will ensure that

这将确保

  • your UnitOfWorkdoesn't need to change for each new Repository
  • you are not using a service locator (considered by many to be an anti-pattern)
  • UnitOfWork不需要为每个新的改变Repository
  • 你没有使用服务定位器(被许多人认为是一种反模式

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

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

Starting with the Repositoryabstraction:

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 Repositorymustregister itself with the UnitOfWorkand this can be done by changing the abstract parent class GenericRepositoryto 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 Repositoryinherits 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 UnitOfWorkand 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 IRepositoryand 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 UnitOfWorkthat 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.

通过这些抽象和围绕 DI 构建的体系结构,您可以UnitOfWork了解Repository在任何服务调用中已实例化的所有' ,并且您可以在编译时验证所有存储库都已定义。您的代码对扩展开放,但对修改关闭

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:

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

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 IRepositoryready 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 hereand here.

您可以装饰您的 UnitOfWork 来处理事务等。在这一点上,我将向强大的 .NetJunkie 致敬,并建议您在此处此处阅读这两篇文章。

回答by Ladislav Mrnka

Instead of injecting repository instances inject single factory object which will be responsible for creating those instances. Your getters will then use that factory.

不是注入存储库实例,而是注入负责创建这些实例的单个工厂对象。然后您的吸气剂将使用该工厂。

回答by sonmt

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

}