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

ras*_*ghp 39 c# entity-framework dependency-injection unit-of-work

我想创建一个环绕在类似的方式对存储库工作类的单元.

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

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

这是强烈耦合的.

我可以通过两种方式看到这一点.

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

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

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

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

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

quj*_*jck 54

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

这将确保这一点

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

一些代码最好地证明了这一点 - 我使用SimpleInjector,因此示例基于此:

Repository抽象开始:

public interface IRepository 
{
    void Submit();
}
public interface IRepository<T> :IRepository where T : class { }
public abstract class GenericRepository<T> : IRepository<T> where T : class { }
Run Code Online (Sandbox Code Playgroud)

UnitOfWork

public interface IUnitOfWork
{
    void Register(IRepository repository);
    void Commit();
}
Run Code Online (Sandbox Code Playgroud)

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

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

每个真正的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) { }
}
Run Code Online (Sandbox Code Playgroud)

添加物理实现,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());
    }
}
Run Code Online (Sandbox Code Playgroud)

容器注册可以设置为自动获取所有已定义的实例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);
    }
}
Run Code Online (Sandbox Code Playgroud)

通过这些抽象和围绕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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

//register the test classes
container.Register<SomeActivity>();
container.Register<MainActivity>();
Run Code Online (Sandbox Code Playgroud)

对代码行设一个断点:

_repositories.ToList().ForEach(x => x.Value.Submit());
Run Code Online (Sandbox Code Playgroud)

最后,运行此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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

您可以装饰您的UnitOfWork以处理交易等.此时我会尊重强大的.NetJunkie,并建议您在此处此处阅读这两篇文章.

  • 您可以使用Container注册上下文,并将其注入到`UnitOfWork`和`Repository`的对象构造函数中.使用上面显示的`lifetimeScope`注册`Context`将确保向所有对象注入相同的实例. (3认同)
  • 这是整个存储库/UOW 场景的优雅解决方案 - 非常全面的代码示例并且解释得很好。关于答案末尾链接的 .net 瘾君子文章 - 我现在将这种方法用于生产中的解决方案,并且效果非常好。 (2认同)

Lad*_*nka 5

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

  • 你能举个例子吗? (2认同)