Search code examples
c#dependency-injectiondryioc

Resolve as null instead of an exception


In one of our projects we have the following scenario: We have a factory that creates a service of another package using it's factory (our factory configures the underlying factory, basically). The underlying factory uses a fluent syntax, and a minimal example looks like this:

public class SomeFactory<T> where T: class
{
    private readonly IFooService _fooService;
    private readonly IBarService<T> _barService;

    private readonly IUnderlyingFactory _factory;

    public SomeFactory(
        IFooService fooService, IBarService<T> barService, IUnderlyingFactory factory)
    {
        _fooService = fooService,
        _barService = barService,
        _factory = factory
    }

    public SomeService<T> Create()
    {
        return _factory
            .WithFooService(_fooService)
            .WithBarService(_barService)
            .Create();
    }
}

Now the problem at hand: For some T, we have implemented the IBarService<T>. However, the underlying package provides a default implementation (which is internal to it, so we can't generically register it) that we want to use in case we don't have an implementation for that T. In that case, we should not call the .WithBarService method at all.

However, if we don't register IBarService<T>, we get an exception from the container as it cannot resolve it.

So the question: Can the container be configured so that it returns null instead of throwing, when it can't resolve IBarService<T> for some T? Or is there another way to solve this problem?

In case we'd receive null, we could change the Create method to:

public SomeService<T> Create()
{
    var service = _factory.WithFooService(_fooService);
    if (_barService != null){
        _factory.WithBarService(_barService);
    }
    return _factory.Create();
}

Just for completeness, this is how we register the BarService for some T, i.e, via reflection on implementing types:

container.RegisterMany(
    ourAssembly.GetTypes()
        .Where(type => type.GetInterfaces().Any(i => i.IsGenericType               
            && !type.IsAbstract)));

Solution

  • Can the container be configured so that it returns null instead of throwing, when it can't resolve IBarService for some T? Or is there another way to solve this problem?

    You can make IBarService<T> parameter optional, then container won't throw if the service is not registered:

    public SomeFactory( IFooService fooService, IUnderlyingFactory factory,
    IBarService<T> barService = null) { _fooService = fooService, _barService = barService, _factory = factory }
    

    Or you may configure service to be injected with IfUnresolved.ReturnDefault option.

    container.Register(typeof(SomeFactory<>), 
      made: Parameters.Of.Type(typeof(IBarService<>), ifUnresolved: IfUnresolved.ReturnDefault))
    

    Note: I may get the syntax of this last wrong, but it should be along the lines.