使用Castle Windsor解决混合开放封闭泛型问题

3

我正在尝试使用Castle Windsor解决带有约束的混合开放/关闭泛型类型。如果Foo实现了IFoo<>,则应该能够解决任何开放式泛型:

container.Register(Component.For(typeof(IFoo<>).ImplementedBy(typeof(Foo<>)));

我的情况略微复杂:

我有以下Handler类:

public abstract class CommandHandlerBase<TCommand, TResponse>
    : IRequestHandler<TCommand, TResponse>
    where TCommand : IRequest<TResponse>
{
    public abstract Task<TResponse> Handle(
        TCommand request, CancellationToken cancellationToken);
}

public class AddMasterDataEntityCommandHandler<TNewEntityData>
    : IRequestHandler<TNewEntityData, Response>
    where TNewEntityData : IRequest<Response>
{
    public Task<Response> Handle(
        TNewEntityData request, CancellationToken cancellationToken)
    {
       // ...
    }
}

这里的想法是 AddMasterDataEntityCommandHandler 将成为一个通用命令处理程序,可以处理任何类型为 TNewEntityData 的合同。

由于我正在使用 Mediatr,因此我的合约必须实现 IRequest<,>。 在这种情况下,我强制所有处理程序都应返回一个 Response
示例用法:

Response response = await mediator.Send(new AddMasterDataEntityCommand<NewPlace>());

我创建了一个简单的控制台应用程序来隔离此行为:
    public static void Main(string[] args)
    {
        var container = new WindsorContainer();

        container.Register(Types.FromThisAssembly()
                                .BasedOn(typeof(IRequestHandler<,>))
                                .Unless(t => t.IsAbstract || t.IsInterface)
                                .WithServices(typeof(IRequestHandler<,>))
                                .LifestyleTransient());

        var instance = container.Resolve(typeof(IRequestHandler<NewData, Response>));
    }

然而,测试抛出了一个异常,指示我的代码存在错误:

Castle.MicroKernel.Handlers.GenericHandlerTypeMismatchException:“类型ConsoleApp4.NewData,ConsoleApp4.Response不满足组件'ConsoleApp4.AddMasterDataEntityCommandHandler'1'的实现类型ConsoleApp4.AddMasterDataEntityCommandHandler'1'的泛型约束。这很可能是你代码中的一个错误。”

我看不到问题在哪里,CW应该能够解决开放/封闭泛型,对吧?此外,问题似乎与附加的Response参数作为TResponse类型有关。我注册组件的方式有问题吗?我相信我没有搞砸泛型约束...

提前感谢任何人能够查看。


1
似乎NewData没有实现IRequest<Response> - Phil Degenhardt
当您执行 new AddMasterDataEntityCommandHandler<NewData>() 时,它是否有效? - Krzysztof Kozmic
@PhilDegenhardt NewData 实现了 IRequest<Response> - rumblefx0
@KrzysztofKozmic 是的,手动构建确实可行。 - rumblefx0
1
复杂的开放式泛型组件很难支持,因此您可能遇到了一些边缘情况。有一个钩子可以手动处理:IGenericImplementationMatchingStrategy 可能会有所帮助。例如,请参见此链接:http://kozmic.net/2013/07/24/on-castle-windsor-and-open-generic-component-arity/ - Krzysztof Kozmic
1个回答

1

Krzysztof Kozmic帮助我找到了正确的方向:

我最初尝试使用IGenericImplementationMatchingStrategy进行实现,但由于它只能处理一个泛型类型,我无法使其正常工作。最终,我通过一些反射技巧以这种方式进行了注册:

private void RegisterGenericMasterDataCommandHandlers(IWindsorContainer container) {
    foreach (Type contractType in contractTypes) {
        Type requestedType = typeof(IRequestHandler<,>).MakeGenericType(typeof(AddMasterDataEntityCommand<>).MakeGenericType(contractType), typeof(Response));
        Type implementedType = typeof(AddMasterDataEntityCommandHandler<>).MakeGenericType(contractType);

        container.Register(Component.For(requestedType, implementedType));
    }
}

1
你能填写完整的代码,例如contractTypes是什么吗? - Denis
自从我离开公司并发布了这个问题后,我就无法再访问这段代码了,但我相信contractTypes是请求类型的列表。基本上,您可以在运行时构造类型,例如IRequestHandler<AddMasterDataEntityCommand<SomeEntity>,Response>。在这种情况下,SomeEntity是合同类型。 - rumblefx0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接