2

我有以下界面...

public interface IHandler<in TFor> where TFor : IRequest
{
    void Handle(IEnumerable<TFor> requests);
}

这通常是这样实现的......

public class AssignmentHandler : HandlerBase, IHandler<AssignmentRequest>
{
    public void Handle(IEnumerable<AssignmentRequest> assigmentRequests)
    {
        foreach(var request in assignmentRequests).....
    }
}

IRequest只是一个标记界面(此时)。

我通过以下约定注册所有处理程序...

public override void Load()
    {

        Kernel.Bind(x => x.FromAssemblyContaining<IHandler>()
            .SelectAllClasses()
            .InheritedFrom(typeof(IHandler<>))
            .BindAllInterfaces());

        //i also tried...
        Kernel.Bind(x => x.FromAssemblyContaining<IHandler>()
            .SelectAllClasses()
            .InheritedFrom<IHandler<IRequest>>()
            .BindSingleInterface());

    }

他们单独解决就好了。

在一种情况下,我想解析所有处理程序,并将它们注入到这样的构造函数中......

public SomeConstructor(IEnumerable<IHandler<IRequest>> allHandlers)

这不起作用并且总是返回空。

我的理解是因为我按照惯例将它们注册为IHandler<ConcreteRequest>,而不是IHandler<IRequest>2 个不同的签名。

我如何按照惯例注册所有处理程序,以便将它们共同识别为IEnumerable<IHandler<IRequest>>同时也单独识别?

第二次注册是可以的,但更喜欢通过两个签名解决一个实现。

4

2 回答 2

1

处理程序FooHandler : IHandler<FooRequest>不能强制转换为IHandler<IRequest>. 您将无法使用任何 IoC 实现此目的,因为 TRequest 类型参数不能同时输入和输出。

如果你想要“双向”,你必须将处理程序绑定到一些通用接口,比如一个空IHandler接口。然后注入所有这些并通过反射找到正确的并使用转换为正确类型的参数调用它。

当然,另一种方法是更改​​ to 的签名void Handle(IEnumerable<TFor> requests)void Handle(IEnumerable<IRequest> requests)让实现进行强制转换。但我认为这对您的场景来说是一个更糟糕的解决方案。

Bind<IHandler<Foo>, IHandler>().To<FooHandler>().InSingletonScope()对于每种类型,您还需要两个不同的绑定或一个多重绑定。当您需要范围界定时,多重绑定非常有用。如果你有两个相同类型的绑定InSingletonScope,就会有两个实例。如果您使用多重绑定,则只有一个实例。


为了使Steven使用ninject CompositeHandler,您必须稍微调整解决方案并为复合处理程序引入一个新接口:

public class CompositeRequest<TFor> : IRequest
{
    public CompositeRequest(params TFor[] requests)
    {
        this.Requests = requests;
    }

    public TFor[] Requests { get; private set; }
}

public interface ICompositeHandler<TFor> : IHandler<CompositeRequest<TFor>> { }

public class CompositeHandler<TFor> : ICompositeHandler<TFor>
    where TFor : IRequest
{
    private readonly IHandler<TFor> handler;

    public CompositeHandler(IHandler<TFor> handler)
    {
        this.handler = handler;
    }

    public void Handle(CompositeRequest<TFor> request)
    {
        foreach (var r in request.Requests)
        {
            this.handler.Handle(r);
        }
    }
}

然后按如下方式创建绑定:

        var kernel = new StandardKernel();

        kernel.Bind(typeof(ICompositeHandler<>)).To(typeof(CompositeHandler<>));

        kernel.Bind(x => x.FromThisAssembly()
            .SelectAllClasses()
            .InheritedFrom(typeof(IHandler<>))
            .Excluding(typeof(CompositeHandler<>))
            .BindDefaultInterfaces());

        kernel.Get<ICompositeHandler<Foo>>();

我已经验证它可以工作。

于 2014-07-28T12:17:37.267 回答
1

不是您问题的答案,但在我看来,您缺少抽象,因为您的所有处理程序都包含相同的 foreach 循环,这意味着您违反了 DRY。我建议将界面更改为以下内容:

public interface IHandler<in TFor> where TFor : IRequest
{
    void Handle(TFor request);
}

并有一个允许处理多个实例的通用实现:

public class CompositeRequest<TFor> : IRequest
{
    public CompositeRequest(params TFor[] requests)
    {
        this.Requests = requests;
    }

    public TFor[] Requests { get; private set; }
}

public class CompositeHandler<TFor> : IHandler<CompositeRequest<TFor>> 
    where TFor : IRequest
{
    private readonly IHandler<TFor> handler;

    public CompositeHandler(IHandler<TFor> handler)
    {
        this.handler = handler;
    }

    public void Handle(CompositeRequest<TFor> request)
    {
        foreach (var r in request.Requests)
        {
            this.handler.Handle(r);
        }
    }
}

这消除了每个处理程序实现 foreach 循环的需要,如果处理列表的方式发生变化,您只需在一个地方进行更改。

不幸的是,我不知道如何在 Ninject 中注册它。

更新

使用 Simple Injector,注册过程如下:

// using SimpleInjector.Extensions;

// Batch register all handlers in the system.
container.RegisterManyForOpenGeneric(typeof(IHandler<>), 
    typeof(AssignmentHandler).Assembly);

// Register the open-generic CompositeHandler<TFor>
container.RegisterOpenGeneric(typeof(IHandler<>), typeof(CompositeHandler<>));
于 2014-07-28T13:50:05.807 回答