簡體   English   中英

使用存儲庫在工作單元模式中的依賴注入

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

我想創建一個環繞在類似的方式對存儲庫工作類的單元

我遇到的問題是嘗試通過用IRepository接口替換示例中的通用存儲庫來實現依賴注入。 在鏈接文章中的uow中,他們使用getter來檢查存儲庫是否被實例化,如果不是,則實例化它。

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

這是強烈耦合的。

我可以通過兩種方式看到這一點。

  1. 使用構造函數注入。
  2. 使用setter注入。

1的問題是,如果我注入所有存儲庫,我必須實例化每個存儲庫,即使我不在特定的工作單元實例中使用它們。 因此招致了這樣做的開銷。 我想象使用一個數據庫范圍的工作單元,這會導致很多不必要的實例化和一個巨大的構造函數。

2的問題是很容易忘記設置和結束空引用異常。

在這種情況下是否有任何最佳實踐? 還有其他我錯過的選擇嗎?

我剛剛進入依賴注入並完成了我可以在這個主題上找到的所有研究,但我可能會遺漏一些關鍵。

解決這個問題的方法是不要讓UnitOfWork負責通過Container注入創建每個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());

最后,運行此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();
        }
    }
}

您將發現代碼在斷點處停止,並且您已准備好一個IRepository活動實例,並等待Submit()對數據庫的任何更改。

您可以裝飾您的UnitOfWork以處理交易等。此時我會尊重強大的.NetJunkie,並建議您在此處此處閱讀這兩篇文章。

注入單個工廠對象而不是注入存儲庫實例,這將負責創建這些實例。 然后,您的吸氣劑將使用該工廠。

我的解決方案是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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM