簡體   English   中英

依賴注入注冊必須放在哪里?

[英]Where dependency-injection registrations have to be put?

我已經閱讀了問題Ioc/DI - 為什么我必須引用應用程序入口點中的所有層/程序集?

因此,在 Asp.Net MVC5 解決方案中,組合根位於 MVC5 項目中(並且擁有負責所有注冊的 DependencyInjection 程序集沒有意義)。

在這張圖片中,我不清楚以下哪種方法更好。

方法一

具體實現是public class ...並且所有注冊子句都集中在組合根中(例如,在 CompositionRoot 文件夾下的一個或多個文件中)。 MVC5 項目必須引用所有提供至少一個要綁定的具體實現的程序集。 沒有庫引用 DI 庫。 MVC 項目可以包含要綁定的接口,沒有任何缺點。

方法二

具體實現是internal class ... 每個庫都公開一個 DI“本地”配置處理程序。 例如

public class DependencyInjectionConfig {
    public static void Configure(Container container) {
        //here registration of assembly-provided implementations
        //...
    }
}

這取決於注冊自己的實現。 組合根通過調用所有Configure()方法來觸發注冊,每個項目只調用一個方法。 然后 MVC5 項目必須引用所有程序集,提供至少一個要綁定的具體實現。 必須引用 DI 庫。 在這種情況下,MVC5 項目不能包含接口(否則會出現循環引用):需要一個 ServiceLayer 程序集來保存要綁定的公共接口。

方法三

與方法 2 相同,但本地配置模塊是通過程序集反射動態發現的(按慣例?)。 所以MVC5項目沒有引用庫。 MVC 項目可以包含接口並且可以被庫引用。 必須引用 DI 庫。

這里的最佳做法是什么? 還有其他更好的可能嗎?

編輯 1 (2016-12-22)感謝收到的答案,我發布了這個 github 項目,描述了我迄今為止找到的最佳解決方案。

編輯 2 (2018-09-09) 這個答案提供了一個有趣的選項。

EDIT 3 (2020-12-29)最后,我想出了一個完整的解決方案,以 WebApi 應用程序模板的形式打包。 我在 GitHub HERE上發布了這個解決方案。 這種方法不僅可以清楚地了解 DI 規則必須放在哪里,而且還建議根據 SOLID 原則和 CQRS 模式設置應用程序。 該項目的提交歷史已被構建為具有教育目的。

我通常喜歡將這些類型的東西封裝到每個項目中。 例如,我可能有以下內容。 (這是一個極其簡化的示例,我將在此示例中使用AutoFac ,但我想所有 DI 框架都具有以下內容)。

僅用於 POCO 和接口的公共區域。

// MyProject.Data.csproj
namespace MyProject.Data
{
  public Interface IPersonRepository
  {
    Person Get();
  }

  public class Person
  {
  }
}

存儲庫和數據訪問的實現

// MyProject.Data.EF.csproj
// This project uses EF to implement that data
namespace MyProject.Data.EF
{
  // internal, because I don't want anyone to actually create this class
  internal class PersonRepository : IPersonRepository
  {
    Person Get()
    {  // implementation  }
  }

  public class Registration : Autofac.Module
  {
    protected override void Load(ContainerBuilder builder)
    {
      builder.Register<PersonRepository>()
        .As<IPersonRepository>()
        .IntancePerLifetimeScope();
    }
  }
}

消費者

// MyPrject.Web.UI.csproj
// This project requires an IPersonRepository
namespace MyProject.Web.UI
{
  // Asp.Net MVC Example
  internal class IoCConfig
  {
    public static void Start()
    {
      var builder = new ContainerBuilder();

      var assemblies = BuildManager.GetReferencedAssemblies()
        .Cast<Assembly>();

      builder.RegisterAssemblyModules(assemblies);
    }
  }
}

所以依賴關系看起來像:

MyProject.Data.csproj 
- None

MyProject.Data.EF.csproj 
- MyProject.Data

MyProject.Web.UI.csproj 
- MyProject.Data
- MyProject.Data.EF

在此設置中,Web.UI 無法了解已注冊的內容或原因。 它只知道 EF 項目有實現但無法訪問它們。

我可以非常輕松地將 EF 刪除為 Dapper,因為每個項目都封裝了它自己的實現和注冊

如果我添加單元測試並有一個 InMemoryPersonRepository,我將如何將 PersonRepository 換成我的 InMemoryPersonRepository?

假設我們忽略任何業務邏輯層並讓 MVC 控制器直接訪問我們的數據訪問器,我的代碼可能如下所示:

public class MyController
{
  private readonly IPersonRepository _repo;

  public MyController(IPersonRepository repo)
  {
    _repo = repo;
  }

  public IActionResult Index()
  {
    var person = _repo.Get();

    var model = Map<PersonVM>(person);

    return View(model);
  }
}

然后使用 nSubstitute 的測試可能看起來像:

public class MyControllerTests
{
  public void Index_Executed_ReturnsObjectWithSameId
  {
    // Assign
    var repo = Substitute.For<IPersonRepository>();
    var expectedId = 1;
    repo.Get().Returns(new Person { Id = expected });
    var controller = new MyController(repo);

    // Act
    var result = controller.Index() as ActionResult<PersonVM>;

    // Assert
    Assert.That(expectedId, Is.EqualTo(result.Value.Id));
}

你發現了一個真正的問題。 (可以說這是一個很好的問題。)如果報名申請A引用BB引用C ,並且B和/或C需要一些 DI 注冊,這使得A (您的報名申請)負責了解足夠的細節BC來注冊所有的依賴。

解決方案是使用一個單獨的程序集來處理組合BC所有注冊。 A引用了它,它提供了A需要使用BC所有容器配置。

好處是

  • ABC了解並不多於它應該知道的
  • ABC都不必綁定到一個特定的 DI 框架,如 Unity 或 Windsor。

這是一個例子 這是一個最適用於 DI 容器的事件總線類。 但是為了使用它,您不必了解它需要注冊的所有依賴項。 所以對於 Windsor 我創建了一個DomainEventFacility 你只要打電話

_container.AddFacility<DomainEventFacility>();

並且所有依賴項都已注冊。 您唯一注冊的是您的事件處理程序。

然后,如果我想將相同的事件總線庫與不同的 DI 容器(如 Unity)一起使用,我可以創建一些類似的程序集來處理 Unity 的相同配置。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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