Posts
1150
Comments
891
Trackbacks
1
A way to handle open generics using Simple Injector

In case I need to do this again, this works, however ‘ugly’ it might be.  This isn’t supposed to be a best practice or anything (there are no such things as best practices, and even if there were some, I wouldn’t know them, since I’m not a very good developer).

Some code:

public interface ICommand {}

public interface ICommand<TReturnValue> : ICommand
    {
        TReturnValue ReturnValue { get; set; }
    }

public class Command : ICommand  {}

public class Command<TReturnValue> : ICommand<TReturnValue>
    {
        public TReturnValue ReturnValue { get; set; }
    }

There’s more to commands than this, but this will suffice.  And yes, I know that some people don’t like commands that return values.  That’s nice.

So now, I want to be able to handle them.

public interface Handles<T, TReturnValue> where T : ICommand<TReturnValue>
    {
        TReturnValue Handle(T command);
    }

public interface Handles<T> where T : ICommand
    {
        void Handle(T command);
    }

Simple enough.

Now, I need to be able to publish commands.

public interface ICommandBaseBus
    {
        void Publish<TCommand>(TCommand command) where TCommand : Command;
        TReturnValue Publish<TCommand, TReturnValue>(TCommand command) where TCommand : Command<TReturnValue>;
    }

Still quite simple.

Now, here’s where Simple Injector comes in.  I don’t want to ‘hand’ register these things together, I want to just scan that sort of stuff.

I tried Autofac, Ninject, StructureMap, and Simple Injector.  I am showing how to do it with Simple Injector because, well, it’s the first one I got to work.  There’s no other reason than that.

Let’s implement the bus.

public class CommandBaseBus : ICommandBaseBus
{
    private Container _container; 

    public CommandBaseBus()
    {
        _container = new Container();

        _container.RegisterManyForOpenGeneric(typeof(Handles<>),
(serviceType, implTypes) => _container.RegisterAll(serviceType, implTypes),
typeof(Handles<>).Assembly);

        _container.RegisterManyForOpenGeneric(typeof(Handles<,>),
(serviceType, implTypes) => _container.RegisterAll(serviceType, implTypes),
typeof(Handles<,>).Assembly);

        _container.Verify();
    }
}

A few things of note:

You need to specify using SimpleInjector.Extensions namespace to get the RegisterMany…. method, which does the fun automagic stuff of connecting your handlers with the relevant commands.

It can get more complicated than this, which I will show in a subsequent post.

I like the Verify() call.  A lot.  I have a tendency to forget to do things, and so when I was ‘hand’ registering stuff, I’d forget to check if everything was wired up correctly, and so get a bunch of run-time barfdom.   If you have the container manage everything, you get that ‘fail fast’ thing immediately when you fire up the bus.

Your calling code might look like this:

static void Main()
        {
            var cmdBus = new CommandBaseBus();

            var myCommand = new MyCommand();
            var myCommandWithReturnValue = new MyCommandWithReturnValue();

            cmdBus.Publish(myCommand);
            var rv = cmdBus.Publish<MyCommandWithReturnValue, bool>(myCommandWithReturnValue);
        }

MyCommandWithReturnValue implements Command<bool> here.

And when you publish, it might do something like this:

public void Publish<TCommand>(TCommand command) where TCommand : Command
        {
            var regs = _container.GetAllInstances<Handles<TCommand>>();

            foreach(var r in regs)
            {
                r.Handle(command);
            }

            //if (!(command is IVariableResult))
               // _eventBus.Process(command);
        }

and

public TReturnValue Publish<TCommand, TReturnValue>(TCommand command) where TCommand : Command<TReturnValue>
        {
            var regs = _container.GetAllInstances<Handles<TCommand, TReturnValue>>().ToList();

            Handles<TCommand, TReturnValue> r = regs[0];

            TReturnValue trv = r.Handle(command);

            //if(!(command is IVariableResult))
            //    _eventBus.Process<TCommand, TReturnValue>(command);

            return trv;
        }

Ignore the commented out stuff about event bus processing and IVariableResult, I’ll get to that some other time.  Maybe.

And yes, I know that normally you would only have one handler for a command, and yes, I know that my variable names suck.

This takes care of, well, the stuff I care gets taken care of at the moment.

posted on Tuesday, December 30, 2014 9:12 PM Print
Comments
No comments posted yet.

Post Comment

Title *
Name *
Email
Url
Comment *  
Please add 6 and 3 and type the answer here: