问题
Here's my scenario:
public static class DomainCommandProcessor
{
public static void Dispatch<T>(T command) where T : IDomainCommand
{
var serviceLocator = ServiceLocator.Current;
var handler = serviceLocator.GetInstance<IDomainCommandHandler<T>>();
if (handler != null)
handler.Handle(command);
}
}
public class FakeGenericCommand<T1, T2> : IDomainCommand
{
public FakeGenericCommand(T1 first, T2 second)
{
First = first;
Second = second;
}
public T1 First { get; private set; }
public T2 Second { get; private set; }
}
public class FakeGenericCommandHandler<T1, T2> : IDomainCommandHandler<FakeGenericCommand<T1, T2>>
{
public void Handle(FakeGenericCommand<T1, T2> command)
{
// something interesting
}
}
Usage:
DomainCommandProcessor.Dispatch(new FakeGenericCommand<string, string>("hi", "there"))
I can't get the Windsor registration right. The following works great for all of my non-generic commands:
container.Register(Classes.FromAssemblyNamed(namespaceName)
.BasedOn(typeof(IDomainCommandHandler<>))
.WithService.AllInterfaces()
.LifestyleTransient());
If I register each possible implementation directly, it works but is obviously sub-optimal:
container.Register(
Component.For<IDomainCommandHandler<FakeGenericCommand<string, string>>>()
.UsingFactoryMethod(input => new FakeGenericCommandHandler<string, string>())
.LifestyleTransient());
Suggestions?
回答1:
What you have here is a textbook-case of needing a typed factory: I answered a question where I described how to use it
This answer is heavily inspired by this article.
Register all your handlers
container.Register(Classes.FromAssemblyInThisApplication()
.BasedOn<IDomainCommandHandler>()
.WithServiceAllInterfaces());
Declare a factory interface that will return the handlers for a command
public interface IDomainCommandHandlerFactory
{
IDomainCommandHandler[] GetHandlersForCommand(IDomainCommand command);
}
You need to link your command types to the handlers, which you can do with a custom selector:
public class HandlerSelector:ITypedFactoryComponentSelector
{
public TypedFactoryComponent SelectComponent(MethodInfo method, Type type, object[] arguments)
{
var message = arguments[0];
var handlerType = typeof(IDomainCommandHandler<>).MakeGenericType(message.GetType());
return new TypedFactoryComponentCollection(handlerType.MakeArrayType(), new Arguments(arguments));
}
}
Then tell Windsor you want a factory that will return a IDomainCommandHandler<T>
. Don't code anything for the factory.
container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<ITypedFactoryComponentSelector>().ImplementedBy<HandlerSelector>());
container.Register(Component.For<IDomainCommandHandlerProvider>().AsFactory());
You can now use the factory to retrieve your handlers for a command
var provider = container.Resolve<IDomainCommandHandlerFactory>();
var msg = new Type2Message();
var msgHandler = provider.GetHandlersForCommand(msg.MessageType);
Please note that the handlers in the example are not running against the command itself but have a Execute
function. If you want to return closed generic objects you need to cast them after resolution since you cannot return different types from one method.
I'd advise you reading the original article since it also contains additional information regarding lifestyle, releasing of components and other interesting points.
Here an example of the Selector I use for requests where I specify the request and the response as generic components
protected override Type GetComponentType(MethodInfo method, object[] arguments)
{
var request = arguments[0].GetType();
var response = arguments[1] as Type;
var handlerType = typeof(IHandlerOf<,>).MakeGenericType(request, response);
return handlerType;
}
And here is the result of the call to the factory (T is the request, R the response)
var handler = handlerFactory.GetHandler<T>(input, typeof(R));
var requestType = input.GetType();
var responseType = typeof(R);
var handlerType = typeof(IHandlerOf<,>).MakeGenericType(requestType, responseType);
r = (R)handlerType.GetMethod("Handle").Invoke(handler, new object[] { input });
来源:https://stackoverflow.com/questions/25998996/windsor-registration-for-generic-commands-command-handlers