0

I am registering classes dynamically from an assembly, a bunch of command handlers:

class class DummyCommand : ICommand {}

class GetAgeCommandHandler : ICommandHandler<DummyCommand>
{
    public void Handle(DummyCommand command) { }
}

I have code which lists all types that implement the generic interface, in this case i am interested in ICommandHandler<> interfaces with the below helper method:

public static IEnumerable<Type> GetAllTypesImplementingOpenGenericType(this Assembly assembly, Type openGenericType)
{
    return from x in assembly.GetTypes()
            from z in x.GetInterfaces()
            let y = x.BaseType
            where
            (y != null && y.IsGenericType &&
            openGenericType.IsAssignableFrom(y.GetGenericTypeDefinition())) ||
            (z.IsGenericType &&
            openGenericType.IsAssignableFrom(z.GetGenericTypeDefinition()))
            select x;
}

With the below registration code:

var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var implementation in assembly.GetAllTypesImplementingOpenGenericType(typeof(ICommandHandler<>)))
{
    // below is wrong, i cannot get the generic type it is empty
    // var commandType = implementation.UnderlyingSystemType.GenericTypeArguments[0];
    // what should i put to find the type `DummyCommand`

    // registeration would be below
    var handlerType = (typeof(ICommandHandler<>)).MakeGenericType(commandType);
    container.Register(handlerType, implementation);
}

Basically i am trying to register with the SimpleInjector container (but could be any ioc container) the type container.Register(typeof(ICommandHandler<DummyCommand>), typeof(GetAgeCommandHandler)) but with generics at runtime, i need to also be careful to handle cases where a class implements multiple ICommandHandler interfaces (of different command types).

Pointers much appreciated.

morleyc
  • 2,169
  • 10
  • 48
  • 108
  • Open generics are not assignable, so `openGenericType.IsAssignableFrom(y.GetGenericTypeDefinition())` will always be false. See https://stackoverflow.com/questions/5461295/using-isassignablefrom-with-open-generic-types –  Dec 31 '18 at 16:08

1 Answers1

2

You might be interested in reading Simple Injector's fine manual on doing Auto-Registration, as the the block of posted code can be reduced to a simple one-liner:

container.Register(typeof(ICommandHandler<>), AppDomain.CurrentDomain.GetAssemblies());
Steven
  • 166,672
  • 24
  • 332
  • 435
  • Hi Steven many thanks, aware of that function and was using that however had to use `GetAllInstances` (which returned an enumerable whereas i should only ever had one command hahdler per command). Semantics really, will revert to this as code (and check for multiple command handlers after registrations are complete) as is going down a rabbit hole with my code! – morleyc Dec 31 '18 at 16:56
  • 1
    @g18c you don't have to check for multiple command handlers. Simple Injector will throw an exception when it finds multiple implementations of the same closed-generic type when you call `container.Register(Type, Assembly[])`. You might be mistaking the use of `container.Collection.Register`, which will register a _collection_ of instances. – Steven Dec 31 '18 at 17:10
  • Ahhhh yes indeed was using Collection property! Thanks Steven, your simple injector rocks. Will check out the code also to see how it works under the covrer for curiosity – morleyc Dec 31 '18 at 17:16
  • The convention is 'simple': you want an `IEnumerable` injected, use `Collection.Register`, if you want a single instance injected, use `Register`. – Steven Dec 31 '18 at 17:19