ASP.NET Core option dependency in constructor

强颜欢笑 提交于 2019-12-19 10:08:42

问题


I'm using ASP.NET Core and I'm attempting to create a resolvable class which has an optional parameter:

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

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

I've created two constructors for this object so that if a dependency isn't found, I would assume it would just fall back to the default constructor.

However when I run my application I receive this error

Additional information: Unable to resolve service for type 'FluentValidation.IValidator`1[FooEntity]' while attempting to activate 'Foo'

I know there is probably a way to manually resolve the construction of the Foo object. But I would prefer not to do that because I will have to do this for every class that I create without a validator.

Does anyone know how to configure ASP.NET Core DI to fall back to a different constructor if the dependency is not found?

EDIT

Sorry, I should of been a bit more clear before.

This Foo class I'm referring to in really a base class for a CRUD Service, which will be used over and over again.

I'm looking for a generic solution which doesn't require me to configure each Service I create each time.

So using a lambda to resolve this is not an option, The null object pattern seems feasible but I can't comprehend how to write a generic one in which I won't have to configure for each service


回答1:


I think its general behavior of Containers to resolve the constructor with the most parameters.

Basically what AddTransient does is the following:

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

So you can register it yourself like this:

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

At this point in the startup class you should know if IValidator<FooEntity> has been registered. Or, if you are using reflection add this logic to your reflection code.

Difference

The difference between the 2 options is that with the first option is that the lambda function to resolve the class is created on startup. + if you change the constructor no code needs to be changed elsewhere.

If you create the lambda yourself this lambda is compiled on build, so theoretically startup should be faster (I have not tested this).

Great mindset

A great mindset is to own the libraries you are using. In Visual studio/Resharper you can decompile source-code, or you can find the repositories on github nowadays.

There you can see the source code, you can see how the services parameters is 'compiled' to the IServiceProvider (see BuildServiceProvider() method, it will give you alot of insight.)

Also look at:

  • ServiceDescriptor, Your type registration.
  • Service, the one that resolves the type.

Solution

best way to do it is this, (sorry for psuedo code but i have no editor at hand).

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> 



回答2:


You need to register the IValidator<T> first:

  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);


来源:https://stackoverflow.com/questions/42220483/asp-net-core-option-dependency-in-constructor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!