August 3

Registering Types With the SimpleIoC Container

I've been kicking the tires on the Command Query Seperation principle/pattern lately. I've been using the CQS-Sample as a starting point. So far, the approach to breaking things down into Commands and Queries is pretty slick and much easier to unit test. The interfaces are used for dependency injection with an IoC container like SimpleIoC (which comes with MVVMLight). If you have done any work with IoC containers, you'll know that you need to register your types with the container either explicitly or through some kind of convention. With a few classes it's easy to just bang out the few lines of code you need to do it. In this case, the number of commands and queries is growing and having to explicitly register each one is becoming monotonous. So how to I register my queries and commands without having to write any code to explicitly register my types?

Assumptions:

  1. We are working with MVVMLight and SimpleIoC.
  2. For any query or command processor interface there is exactly 1 implementation of it.
  3. All queries are marked with an IQuery interface.
  4. All commands are marked with an ICommand interface (not to be confused with the System.Windows.Input.ICommand).
  5. All command processors implement ICommandProcessor<TCommand> where TCommand is a class that inherits from ICommand.

Step 1: Get an instance of MethodInfo for SimpleIoC's Register<TInterface, TClass>() method.

Normally, getting the MethodInfo for a method you'd like to invoke is more straight forward. This method has 8 overloads so some LINQ was needed to get the correct one.

var registerMethodInfo = SimpleIoc.Default.GetType().GetMethods()
.Where(m => m.Name == "Register")
.Select(m => new
{
Method = m,
Params = m.GetParameters(),
Args = m.GetGenericArguments()
})
.Where(x => x.Params.Length == 0)
.Where(x => x.Args.Length == 2)
.Select(x => x.Method)
.First();

Step 2: Get a list of all types in the current AppDomain.

This step is pretty straightforward. Notice the .ToList() call at the end of method chain. This is to make sure that when we use types in the following steps, we won't be enumerating the IEnumerable of types more than once.

var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).ToList();

Step 3: Get all query interfaces that inherit from IQuery.

var queryInterfaces = from t in types
where t != typeof (IQuery)
where t.IsInterface
where typeof (IQuery).IsAssignableFrom(t)
select t;

Step 4: Create a query implementation class to query interface mapping

This step creates the query interface to query type mapping. The result is projected into an anonymous type that will be easy to use in the next step.

var queryMappings = from queryInterface in queryInterfaces
from t in types
where t.GetInterface(queryInterface.Name) == queryInterface
select new {queryInterface, Query = t};

Step 5: Register the query types

This is the last bit of reflection needed to register the queries. This code iterates of the query mappings from the previous step, gets a generic MethodInfo using the interface and implementation, and then invokes it on the current SimpleIoC instance.

foreach (var queryMapping in queryMappings)
{
var genericRegisterMethodInfo = registerMethodInfo.MakeGenericMethod(queryMapping.queryInterface, queryMapping.Query);
genericRegisterMethodInfo.Invoke(SimpleIoc.Default, null);
}

Step 6: Get a list of classes that inherit from ICommand.

var commands = from t in types
where t != typeof (ICommand)
where typeof (ICommand).IsAssignableFrom(t)
select t;

Step 7: Make the ICommandProcessor<T> interfaces.

This is an interesting step in the process. The command processors do not have a common non-generic interface, so we'll have to make the generic type of ICommandProcessor<TCommand>.

 

var commandProcessorInterfaces = from command in commands
select new {CommandProcessorInterface = typeof (ICommandProcessor<>).MakeGenericType(command)};

Step 8: Map ICommandProcessor<T> interfaces to ICommandProcessor<T> implementations

Now we'll create the mapping between the ICommandProcessor<TCommand>s and their implementations.

var commandProcessorMappings = from commandProcessorInterface in commandProcessorInterfaces
from t in types
where t.GetInterface(commandProcessorInterface.CommandProcessorInterface.Name) == commandProcessorInterface.CommandProcessorInterface
select new {commandProcessorInterface.CommandProcessorInterface, CommandProcessor = t};

Step 9: Register the command processor types

Same thing as step 5. Just iterating over the command mappings to make the generic MethodInfos and then invoking them on the current SimpleIoC instance.

foreach (var commandProcessorMapping in commandProcessorMappings)
{
var genericRegisterMethodInfo = registerMethodInfo.MakeGenericMethod(commandProcessorMapping.CommandProcessorInterface, commandProcessorMapping.CommandProcessor);
genericRegisterMethodInfo.Invoke(SimpleIoc.Default, null);
}

And that's how I register all my command processors and queries through reflection. LINQ and Reflection FTW!

Are there better approaches? Sure. Are there better IoC containers out there? You betcha. But, this approach is Good Enough™ for now.

Tags: , , , ,
Copyright 2023. All rights reserved.

Posted August 3, 2015 by codegorilla in category "C#", "LINQ