c# - 使用存储库的工作单元模式中的依赖注入(inject)

标签 c# entity-framework dependency-injection unit-of-work

我想创建一个工作类单元,以类似于 this 的方式环绕存储库.

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

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

强耦合。

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

  1. 使用构造函数注入(inject)。
  2. 使用 setter 注入(inject)。

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

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

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

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

最佳答案

解决这个问题的方法是不让 UnitOfWork 负责通过容器注入(inject)创建每个 Repository,而是让它成为每个 Repository 的责任 以确保 UnitOfWork 在实例化时知道它的存在。

这将确保

  • 您的 UnitOfWork 不需要为每个新的 Repository 更改
  • 您没有使用服务定位器(许多人认为是 anti-pattern)

最好用一些代码来证明这一点——我使用 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,并且您有编译时间验证您的所有存储库都已定义。您的密码是 open for extension but closed for modification .

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

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

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

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

关于c# - 使用存储库的工作单元模式中的依赖注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16064902/

相关文章:

c# - WinForms - 无边界 DateTimePicker

c# - 从方法返回一次性对象时出现 CA2000

c# - 如何检查 $(ConfigurationName) 是否包含字符串

c# - ASP.NET CORE 没有 app.UseEndpoints() 方法

entity-framework - 移动到单声道 : Entity Framework alternatives

c# - EntityCollection 已经初始化

c# - 提高 Linq SQL 查询性能

c# - 如何在 Ninject 中绑定(bind)通用类型接口(interface)

dependency-injection - 使用 .Net 和程序集引用进行控制反转

c# - 在 Signalr 2.0 自托管中使用依赖注入(inject)?