一尘不染

使用存储库以工作单元为单位进行依赖注入

c#

我想创建一个工作单元类,以类似于方法的方式包装存储库。

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

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

这是紧密耦合的。

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

  1. 使用构造函数注入。
  2. 使用二传手注射。

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

2的问题在于,很容易忘记设置并以空引用异常结束。

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

我只是进入依赖注入,并完成了我可以在该主题上找到的所有研究,但是我可能缺少一些关键的东西。


阅读 357

收藏
2020-05-19

共1个答案

一尘不染

解决此问题的方法不是通过容器注入使UnitOfWork创建每个对象负责Repository,而是使每个对象都有责任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 { }

UnitOfWork

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

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

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

每个实数都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) { }
}

添加的物理实现,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并在生命周期范围内注册它们,以确保它们在事务的生命周期中都有效:

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可以知道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();
    }
}

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

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

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

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

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

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

您会发现代码在断点处停止,并且您有一个处于活动状态的IRepositoryready 实例,正在等待Submit()数据库的任何更改。

您可以装饰UnitOfWork来处理事务等。此时,我将遵循强大的.NetJunkie,并建议您在此处此处阅读这两篇文章。

2020-05-19