Optional constructor injection arguments with .NET Core

Viewed 6020

In some IoC containers it is possible to have arguments in the constructor that can't be fulfilled by the container. Is this possible with the Microsoft.Extensions.DependencyInjection libraries and IServiceProvider? If not, what is a clean solution for this sort of problem?

For example:

class InContainer
{
    public InContainer(NotInContainer dependency) { ... }
}

class Consumer
{
    public Consumer(IServiceProvider serviceProvider)
    {
         NotInContainer currentDependency = ... // from some other source
         // passing the anonymous object here is not supported, 
         // but I would like to 
         InContainer = serviceProvider.GetService<InContainer>(
             new { dependency = currentDependency }
         );
    }
}
3 Answers

In your example, you provide the serviceProvider with a runtime value currentDependency. Application components should not require runtime data during construction, as explained here. The solution is to refactor your design, as explained in that article.

About optional arguments:

The fact that some DI Containers support optional arguments, doesn't make it a good practice to use them. As a matter of fact, injection constructor arguments should never be optional.

As explained in this article:

An optional dependency implies that the reference to the dependency will be null when it’s not supplied. Null references complicate code because they require specific logic for the null-case. Instead of passing in a null reference, the caller could insert an implementation with no behavior, i.e. an implementation of the Null Object Pattern.

If not, what is a clean solution for this sort of problem?

As stated, the Null Object pattern is the solution for this, even when using a DI Container that actually supports optional constructor dependencies.

Normally, I create a factory by hand in this case.

public class TheFactory
{
    public TheFactory( SomeType fromContainer )
    {
        _fromContainer = fromContainer;
    }

    public IProduct Create( SomeOtherType notFromContainer ) => new TheProduct( _fromContainer, notFromContainer );

    private readonly SomeType _fromContainer;

    private class TheProduct : IProduct
    {
        // ...
    }
}

If you need per-product dependencies from the container, the factory's Create has to resolve them. Or, in the case of e.g. unity, the factory gets a Func from the container.

Is there a reason not to just do this? I had a situation where I needed the HttpContext, but in the course of initializing my web app, a few calls would inevitably be made. Since it was during init, there was no request and hence no context.

This was all for fairly typical database auditing, by the way, so obviously you want the username for changes the user makes, but during init it just ends up using "SYSTEM" or something like that. So...

public class CurrentHttpUserProvider : ICurrentUserProvider
{
    public CurrentHttpUserProvider(IServiceProvider container)
    {
        var httpContext = container.GetService(typeof(HttpContext)); // just returns null if it can't be found...
        if (httpContext != null)
            CurrentUser = ((HttpContext)httpContext).User.Identity.Name;
        else
            CurrentUser = "SYSTEM";
    }

    public string CurrentUser { get; set; }
}

This seems to be like it would be 100% as efficient as if I'd just injected the HttpContext as a constructor param, but perhaps I'm wrong.

Related