我正在使用ASP.NET Core,而我正在尝试创建一个具有可选参数的可解析类:
public class Foo
{
public Foo() : this(null)
{}
public Foo(IValidator<FooEntity> validator)
{
}
}
我为这个对象创建了两个构造函数,这样如果找不到依赖项,我会认为它只会回退到默认的构造函数。
然而,当我运行我的应用程序时,我收到此错误
其他信息:无法解决类型&#39; FluentValidation.IValidator`1 [FooEntity]&#39;在尝试激活&#39; Foo&#39;
时
我知道可能有办法手动解析Foo对象的构造。但我宁愿不这样做,因为我必须为没有验证器的每个班级做这个。
如果找不到依赖项,是否有人知道如何配置ASP.NET Core DI以回退到不同的构造函数?
修改
对不起,我之前应该更清楚一点。
这个Foo类我真正指的是一个CRUD服务的基类,它将被反复使用。
我正在寻找一个通用解决方案,它不需要我配置我每次创建的每个服务。
所以使用lambda来解决这个问题不是一个选择。空对象模式似乎可行,但我无法理解如何编写一个我不必为每个服务配置的通用模式< / p>
答案 0 :(得分:3)
我认为Container的一般行为是用最多的参数来解析构造函数。
基本上AddTransient
的作用如下:
services.AddTransient<Foo>();
//equals to:
services.AddTransient<Foo>(c=> new Foo(c.GetService<IValidator<FooEntity>()));
所以你可以这样自己注册:
services.AddTransient<Foo>(c=> new Foo());
在启动课程的这一点上,您应该知道IValidator<FooEntity>
是否已注册。或者,如果您使用反射,请将此逻辑添加到反射代码中。
两个选项之间的区别在于第一个选项是在启动时创建解析类的lambda function
。 +如果更改构造函数,则无需在其他地方更改代码。
如果你自己创建lambda,这个lambda是在build上编译的,所以理论上启动应该更快(我没有测试过)。
一个伟大的思维方式是拥有您正在使用的库。在Visual Studio / Resharper中,您可以反编译源代码,或者您现在可以在github上找到存储库。
在那里你可以看到源代码,你可以看到services
参数是如何被编译的&#39;到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>
答案 1 :(得分:1)
您需要先register IValidator<T>
:
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);