Search code examples
c#ninjectmediatr

Generic Decorator pattern using ninject


I am trying to wrap my generic interfaces with a decorator but it simply does not work, it seems to me from the other questions that the only way to do this is by explicitly doing it for each decorator, my question is about whether it is possible to wrap all types that implement a certain interface with a specific Decorater in ninject.

code:

 static void BindMediatr(IKernel kernel) {
  kernel.Components.Add < IBindingResolver, ContravariantBindingResolver > ();

  kernel.Bind(scan => scan.FromAssemblyContaining < IMediator > ()
   .SelectAllClasses()
   .BindDefaultInterface());

  kernel.Bind < SingleInstanceFactory > ().ToMethod(ctx => t => ctx.Kernel.Get(t));
  kernel.Bind < MultiInstanceFactory > ().ToMethod(ctx => t => ctx.Kernel.GetAll(t));
  kernel.Bind(
   x => x.FromThisAssembly()
   .SelectAllClasses()
   .InheritedFromAny(typeof(IAsyncRequestHandler < , > ))
   .BindAllInterfaces());

  kernel.Bind(typeof(IAsyncRequestHandler < , > ))
   .To(typeof(Decorater < , > ))
   .WhenInjectedInto < ApiController > ();
 }

 public class Decorater < TRequest, TResponse >
  : IAsyncRequestHandler < TRequest, TResponse >
  where TRequest: IAsyncRequest < TResponse > {
   IAsyncRequestHandler < TRequest,
   TResponse > _decoratee;

   public Decorater(IAsyncRequestHandler < TRequest, TResponse > decoratee) {
    _decoratee = decoratee;
   }

   public Task < TResponse > Handle(TRequest message) {
    // do something here
   }
  }

Solution

  • I found this extension method which does the trick:

    public static class KernelExtensions
        {
            /// <summary>
            /// Binds an open generic type to its implementation and adds all its defined decorators
            /// </summary>
            /// <param name="kernel">Ninject Container</param>
            /// <param name="openGenericType">Open generic Type</param>
            /// <param name="assembly">Assembly to scan for the open generic type implementation</param>
            /// <param name="decoratorTypes">Types of the decorators. Order matters. Order is from the most outer decorator to the inner decorator</param>
            public static void BindManyOpenGenericsWithDecorators(this IKernel kernel, Type openGenericType, Assembly assembly, params Type[] decoratorTypes)
            {
                var allImplementations = GetAllTypesImplementingOpenGenericType(openGenericType, assembly);
    
                foreach (var type in allImplementations.Where(type => !decoratorTypes.Contains(type)))
                {
                    var genericInterface = type.GetInterfaces().FirstOrDefault(x => openGenericType.IsAssignableFrom(x.GetGenericTypeDefinition()));
    
                    // real implementation
                    var parentType = decoratorTypes.Last();
                    kernel.Bind(genericInterface).To(type)
                    .WhenInjectedInto(parentType);
                }
    
                for (var i = 0; i <= decoratorTypes.Count() - 1; i++)
                {
                    var decoratorType = decoratorTypes[i];
    
                    if (i == 0)
                    {
                        // most outer decorator
                        kernel.Bind(openGenericType).To(decoratorType);
                    }
                    else
                    {
                        // inner decorators
                        var parentType = decoratorTypes[i - 1];
                        kernel.Bind(openGenericType).To(decoratorType)
                            .WhenInjectedInto(parentType);
                    }
                }
            }
    
            private static IEnumerable<Type> GetAllTypesImplementingOpenGenericType(Type openGenericType, Assembly assembly)
            {
                return (from type in assembly.GetTypes()
                        from interfaceType in type.GetInterfaces()
                        let baseType = type.BaseType
                        where
                        (baseType != null && baseType.IsGenericType &&
                        openGenericType.IsAssignableFrom(baseType.GetGenericTypeDefinition())) ||
                        (interfaceType.IsGenericType &&
                        openGenericType.IsAssignableFrom(interfaceType.GetGenericTypeDefinition()))
                        select type);
            }
        }