【问题标题】:ASP.NET Core option dependency in constructor构造函数中的 ASP.NET Core 选项依赖项
【发布时间】:2017-02-14 07:45:13
【问题描述】:

我正在使用 ASP.NET Core,我正在尝试创建一个具有可选参数的可解析类:

public class Foo
{
     public Foo() : this(null)
     {}

     public Foo(IValidator<FooEntity> validator)
     {
     }
}

我为这个对象创建了两个构造函数,所以如果找不到依赖项,我会假设它会回退到默认构造函数。

但是,当我运行我的应用程序时,我收到了这个错误

附加信息:尝试激活“Foo”时无法解析“FluentValidation.IValidator`1[FooEntity]”类型的服务

我知道可能有一种方法可以手动解决 Foo 对象的构造问题。但我不希望这样做,因为我必须在没有验证器的情况下为我创建的每个类都这样做。

有谁知道如何配置 ASP.NET Core DI 以在未找到依赖项时回退到不同的构造函数?

编辑

对不起,我之前应该更清楚一点。

我指的这个 Foo 类实际上是一个 CRUD 服务的基类,它将被反复使用。

我正在寻找一种通用解决方案,它不需要我每次都配置我创建的每个服务。

因此,使用 lambda 解决此问题不是一种选择,空对象模式似乎可行,但我无法理解如何编写一个无需为每个服务配置的通用模式

【问题讨论】:

  • 拥有多个构造函数is an anti-pattern。依赖项不应该是可选的。您应该改用Null Object pattern
  • 另一种选择是在您的类中使用IServiceProvider 来尝试获取可选依赖项。这样它就不会阻止服务被实例化。不过我会尽量避免这种方法。
  • @juunas:你不应该在你的类中的任何地方注入IServiceProvider,除了工厂(只有在没有其他解决方案的情况下才在这里)
  • @Steven 我的 Foo 类实际上是一个通用的 CRUD 服务,我想使用空对象模式,但是有没有办法通用配置 DI,所以我不必创建一个空每个不需要验证器的服务的验证器?

标签: c# dependency-injection asp.net-core


【解决方案1】:

我认为容器的一般行为是解析具有最多参数的构造函数。

AddTransient 的作用基本上如下:

services.AddTransient<Foo>();
//equals to:
services.AddTransient<Foo>(c=> new Foo(c.GetService<IValidator<FooEntity>()));

所以你可以像这样自己注册:

services.AddTransient<Foo>(c=> new Foo());

此时在启动类中,您应该知道IValidator&lt;FooEntity&gt; 是否已注册。或者,如果您使用反射,请将此逻辑添加到反射代码中。

区别

这两个选项的区别在于第一个选项是lambda function解析类是在启动时创建的。 + 如果您更改构造函数,则无需在其他地方更改代码。

如果你自己创建 lambda,这个 lambda 是在构建时编译的,所以理论上启动应该更快(我没有测试过)。

伟大的心态

拥有您正在使用的库是一种很好的心态。在 Visual Studio/Resharper 中你可以反编译源代码,或者你现在可以在 github 上找到存储库。

在那里你可以看到源代码,你可以看到services参数是如何'编译'到IServiceProvider的(见BuildServiceProvider()方法,它会给你很多见解。)

另请看:

解决方案

最好的方法是这样做,(抱歉伪代码,但我手头没有编辑器)。

getTypes()
    .Where(x=> x.EndsWith("Entity") //lets get some types by logic
    .Select(x=> typeof(IValidator<>).MakeGeneric(x)) //turn IValidator into IValidator<T>
    .Where(x=> !services.IsRegistered(x))
    .Each(x=> services.Add(x, c=> null)) //register value null for IValidator<T> 

【讨论】:

  • 谢谢,你的回答很清楚,我知道我可以显式解析 foo 类型,但我想偷懒,因为在我的情况下 FOO 是一个通用的 CRUD 服务,并且我宁愿不必明确配置每个服务是否缺少验证器
  • @johnny5 然后真的很懒惰,只是通过反射自动完成,并为每个缺少验证器的服务记录警告
  • @johnny5 我在回答中添加了可能的方法,如果您愿意,可以在有可行的解决方案时覆盖我的回答代码。
【解决方案2】:

你需要先registerIValidator&lt;T&gt;

  var services = new Microsoft.Extensions.DependencyInjection.ServiceCollection();
  services.AddTransient<IValidator<FooEntity>, RealValidator<FooEntity>>();
  services.AddTransient<Foo>();

  var serviceProvider = services.BuildServiceProvider();
  var validator = serviceProvider.GetService<IValidator<FooEntity>>();
  var foo = serviceProvider.GetService<Foo>();

  Assert.NotNull(validator);
  Assert.NotNull(foo);

【讨论】:

  • 我知道如何注册 IValidator,我只是不想一开始就创建一个,因为我的一些 CRUD 服务不需要验证
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-28
  • 1970-01-01
相关资源
最近更新 更多