Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to allow for optional services with Microsoft.Extension.DependencyInjection?

I am playing around with ASP.NET Core on my own hobby project, I want to create a framework that will be consumed by a developer, and I want to allow optional service and use defaults if they are not registered.

I am getting the Unable to resolve service for type 'XXX' error, but I would prefer the DI to return null rather then throw an exception. I want to allow for optional services, so if a service is found, use that in the constructor, if not found, pass null into the constructor.

In my implementation I have:

public IServiceManager(IService service, ...)
{
    _service = service ?? new DefaultService();
    ...
}

So as you can see, if the service cannot be found (null) use the default. Perhaps I am misunderstanding how DI works. Perhaps I could use a factory to do this instead? However, in my system I using default services when non is provided will be a common occurrence, so I need a solution that doesn't require the consumer of the API to register a service.

Is there a way to configure ASP.NET Core DI to return null rather then throw an exception?

like image 590
Luke T O'Brien Avatar asked Mar 18 '17 23:03

Luke T O'Brien


2 Answers

Add default value to that parameter in the constructor.

public IServiceManager(IService service = null, ...)
{
  _service = service ?? new DefaultService();
  ...
}
like image 135
laika Avatar answered Oct 19 '22 07:10

laika


By their very nature, constructor injection is always considered as mandatory.

The very first versions of the Microsoft DI (I don't like using the term ASP.NET Core DI, because it does not depend on ASP.NET Core and can be used outside of it) only supported the constructor with the most parameters.

I think this has been changed since then to allow multiple constructors and the IoC container will choose a fitting one. That being said, you'd likely need to define multiple constructors.

public IServiceManager(IService service, IOtherService otherService)
{
}

public IServiceManager(IOtherService otherService)
{
}

Then the second constructor should be called, if IService isn't registered with the IoC container.

But it's still quite a questionable practice at best and makes your code harder to maintain and hold its invariant/loose coupling.

You should never have to instantiate your types inside your services, not even for optional services.

Instead, you should provide registrations which allow a user to override them with their own implementations.

public static IServiceCollection AddMyLibrary(this IServiceCollection services)
{
    services.TryAddTransient<IService, Service>();
    services.TryAddTransient<IOtherService, OtherService>();
}

Then the user override it.

services.AddTransient<IService, CustomService>();
services.AddMyLibrary();

Now CustomService will be injected where IService is requested.

like image 26
Tseng Avatar answered Oct 19 '22 09:10

Tseng