Search code examples
c#.netsimple-injector

Conditional Registration of Decorator based on Generic Parameter using SimpleInjector


I have a query handler like this:

public class FooQueryHandler : IQueryHandler<FooQuery, FooResult>
{
      public FooResult Handle(FooQuery query) {
          // Query handled here.
      }
}

The IQueryHandler interface is defined as:

public interface IQueryHandler<TQuery, Result> : where TQuery : IQuery
{
    TResult Handle(TQuery query);
}

FooQuery is defined as:

public class FooQuery : IAuthorizedQuery

where IAuthorizedQuery is defined as:

public interface IAuthorizedQuery : IQuery

IQuery is just simple interface. FooResult is just a simple DTO object.

The IQueryHandler is injected using Simple Injector and is registered with the container as:

container.Register(typeof(IQueryHandler<,>), assemblies);

The Problem

I want to decorate the IQueryHandler with a decorator handler class. This decorator should be applied conditionally only if the query object (e.g. FooQuery) implements IAuthorizedQuery. This is so some role-based authorization can occur.

If the query does not implement IAuthorizedQuery and only IQuery then no decoration should take place.

The handler will need to look like:

 public AuthorizationQueryDecorator : IQueryHandler<TQuery, TResult> 
    where TQuery : IAuthorizedQuery 
 {
      public AuthorizationQueryDecorator(
            IQueryHandler<TQuery, TResult> handler,
            IAuthorizer<TQuery> authorizer)    
      {
           _authorizer = authorizer;
           _handler = handler;
      }

      private readonly IAuthorizer<TQuery> _authorizer;
      private readonly IQueryHandler<TQuery, TResult> handler;

      public TResult Handle(TQuery query) {
          if(!_authorizer.Authorise(query)) {
              // throw exception
          }
          else {
               handler.Handle(query);
          }
      }
 }

The IAuthorizer<TQuery> interface is just a simple interface with a boolean method Authorise(TQuery query) that returns true if the query can be executed based on some role-based logic.

The problem is I'm not sure how to register the decorator conditionally with SimpleInjector.

The condition is essentially that an IQueryHandler should only be decorated if the TQuery object implements IAuthorizationQuery.

How can I do this?

At the moment I have

 container.RegisterDecorator(
            typeof(IQueryHandler<,>),
            typeof(AuthorizationQueryDecorator<,>), \\\ PREDICATE?);

but I don't know what Predicate should be specified?

EDIT

It looks as though the issue I'm having is with the IAuthorizer registration.

I have the following IAuthorizer implementations:

public FooQueryAuthorizer() : IAuthorizer<FooQuery>

and

public NullQueryAuthorizer<TQuery, TResult> : IQueryAuthorizer<TQuery, TResult>
    where TQuery : IAuthorizedQuery<TResult> : IAuthorizer

The SimpleInjector registration for IQueryAuthorizer is:

            container.RegisterConditional(
            typeof(IQueryAuthorizer<,>),
            typeof(NullQueryAuthorizer<,>),
            c => !c.Handled);

However, the NullQueryAuthorizer is always used, regardless of whether a specific implementation (e.g. FooQueryAuthorizer) is present.

How can I get round this?


Solution

  • You don't have to do anything; Simple Injector will automatically apply generic type constraints for you. So your registration is simply this:

    container.RegisterDecorator(
        typeof(IQueryHandler<,>),
        typeof(AuthorizationQueryDecorator<,>));
    

    The documentation describes:

    Simple Injector will automatically apply the registered type conditionally based on it generic type constraints