Windsor注册通用命令/命令处理程序

时间:2014-09-23 15:27:18

标签: .net inversion-of-control castle-windsor

这是我的情景:

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
    }
}

用法:

DomainCommandProcessor.Dispatch(new FakeGenericCommand<string, string>("hi", "there"))

我无法正确注册Windsor。以下内容适用于所有非泛型命令:

container.Register(Classes.FromAssemblyNamed(namespaceName)
    .BasedOn(typeof(IDomainCommandHandler<>))
    .WithService.AllInterfaces()
    .LifestyleTransient());

如果我直接注册每个可能的实现,它可以工作,但显然是次优的:

container.Register(
    Component.For<IDomainCommandHandler<FakeGenericCommand<string, string>>>()
        .UsingFactoryMethod(input => new FakeGenericCommandHandler<string, string>())
        .LifestyleTransient());

建议?

1 个答案:

答案 0 :(得分:1)

这里有一本教科书 - 需要typed factory的情况:我answered我在这里描述了如何使用它的问题


这个答案深受this article的启发。

注册所有处理程序

container.Register(Classes.FromAssemblyInThisApplication()
    .BasedOn<IDomainCommandHandler>()
    .WithServiceAllInterfaces());

声明一个工厂接口,它将返回命令的处理程序

public interface IDomainCommandHandlerFactory
{
    IDomainCommandHandler[] GetHandlersForCommand(IDomainCommand command);
}

您需要将命令类型链接到处理程序,您可以使用自定义选择器进行处理:

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));
    }
}

然后告诉Windsor你想要一个返回IDomainCommandHandler<T>的工厂。不要为工厂编码任何东西。

container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<ITypedFactoryComponentSelector>().ImplementedBy<HandlerSelector>());
container.Register(Component.For<IDomainCommandHandlerProvider>().AsFactory());

您现在可以使用工厂检索命令的处理程序

var provider = container.Resolve<IDomainCommandHandlerFactory>();
var msg = new Type2Message();
var msgHandler = provider.GetHandlersForCommand(msg.MessageType);

请注意,示例中的处理程序不是针对命令本身运行,而是具有Execute函数。如果要返回已关闭的通用对象,则需要在解析后对其进行转换,因为无法从一种方法返回不同的类型。

我建议您阅读原始文章,因为它还包含有关生活方式,组件发布和其他有趣观点的其他信息。


这里是我用于请求的选择器示例,其中我将请求和响应指定为通用组件

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;
}

这是调用工厂的结果(T是请求,R是响应)

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 });