How to inject null for unregistered services

I have a decorator and an associated service that looks like this:

public interface IAdditionalDataService<TResult, TModel>
{
    TResult PopulateAdditionalData(TResult model, 
        params Expression<Func<TModel, object>>[] properties);
}

public class AdditionalDataDecorator<TQuery, TResult, TModel> 
    : IQueryHandler<TQuery, TResult>
    where TQuery : QueryBase<TResult, TModel>, IQuery<TResult>
    where TModel : ModelBase
{
    private readonly IQueryHandler<TQuery, TResult> _decorated;
    private readonly IAdditionalDataService<TResult, TModel> _additionalDataService;

    public AdditionalDataDecorator(IQueryHandler<TQuery, TResult> decorated,
        IAdditionalDataService<TResult, TModel>  additionalDataService)
    {
        _decorated = decorated;
        _additionalDataService = additionalDataService;
    }

    public TResult Handle(TQuery query)
    {
        var result = _decorated.Handle(query);

        if (query.RelatedDataProperties != null && query.RelatedDataProperties.Any())
            result = _additionalDataService.PopulateAdditionalData(result,
                query.RelatedDataProperties.ToArray());

        return result;
    }
}

The decorator wraps a query handler so that the results are manipulated by the AdditionalDataService and then returned.

The issue is not all of my query results will need an AdditionalDataService, so not every possible TResult has an associated AdditionalDataService implementation. Simple Injector throws an exception if I try to use the decorator without a valid AdditionalDataService implementation for the corresponding Type.

What I need Simple Injector to do - only for AdditionalDataService - is if it doesn't find an implementation for the corresponding type inject "null". From there I can test for null before calling into the service.

So my question is

How can I tell Simple Injector to inject null when it encounters a request for a service that hasn't been registered, instead of throwing an exception?

Answers


You should never inject null into your application components' constructors. This is bad practice, because it complicates your code with null checks. A component should simply always be able to assume that it gets a value. Because of this, Simple Injector is built to make it (almost) impossible to inject null values into constructors.

Instead you should use the well-known Null Object pattern, which is what @manji is referring to. But instead of using the ResolveUnregisteredType event, there is a much easier way to do this.

I assume that you register your IAdditionalDataService<TResult, TModel> implementations using Register much like this:

// Simple Injector v3.x
container.Register(typeof(IAdditionalDataService<,>),
    new[] { typeof(IAdditionalDataService<,>).Assembly });

// Simple Injector v2.x
container.RegisterManyForOpenGeneric(typeof(IAdditionalDataService<,>),
    typeof(IAdditionalDataService<,>).Assembly);

A fallback registration can now be registered as follows:

// Simple Injector v3.x
container.RegisterConditional(
    typeof(IAdditionalDataService<,>),
    typeof(EmptyAdditionalDataService<,>),
    c => !c.Handled);

// Simple Injector v2.x
container.RegisterOpenGeneric(
    typeof(IAdditionalDataService<,>),
    typeof(EmptyAdditionalDataService<,>));

That's it.


You could create an 'EmptyAdditionalDataService' (that does nothing) and return it for unregistred services using Container.ResolveUnregisteredType Event.

You can then check if it's an instance of EmptyAdditionalDataService in the Handled method.


Need Your Help

animated gifs not playing in browser under html5

javascript html html5 html5-canvas animated-gif

I'm trying to add an animated gif to an html5 canvas object. The problem is that once added it doesn't play and stays on the first frame. I've tested the HTML5 document in Chrome and Firefox and th...

How to do URL rewriting

url-rewriting apache2

I am very new to httpd server. And i have a immediate problem to solve. My server instance is running on centos box.