4

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)));
EluciusFTW
  • 2,565
  • 6
  • 42
  • 59
  • 2
    I'm not that familiar with DryIoc but in general, problems like these are best solved using the Null Object design pattern. This means that you should create an empty, generic `IBarService` implementation that contains no behavior, and use that 'null implementation' as fallback registration that you use in case no explicit implementation exists for a given `T`. Most DI Containers allow you to register this fallback behavior. – Steven Dec 06 '19 at 14:08
  • @Steven, thanks. This actually already is in place. That's what I mean by the default behavior that is already provided by the base package. I explicitly don't want to repeat it here in this package, and the default one is internal to the package. – EluciusFTW Dec 06 '19 at 16:13

1 Answers1

3

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.

dadhi
  • 4,807
  • 19
  • 25