繁体   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