C# 带Ninject的开放泛型和IEnumerable

C# 带Ninject的开放泛型和IEnumerable,c#,ninject,open-generics,C#,Ninject,Open Generics,我有以下界面 public interface IHandler<in TFor> where TFor : IRequest { void Handle(IEnumerable<TFor> requests); } 他们各自都很好地解决了问题 有一种情况,我希望解析所有的处理程序,并将它们注入构造函数中,就像这样 public class AssignmentHandler : HandlerBase, IHandler<AssignmentReques

我有以下界面

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).....
    }
}
public SomeConstructor(IEnumerable<IHandler<IRequest>> allHandlers)
public-SomeConstructor(IEnumerable-allHandlers)
这不起作用,并且总是返回空

我的理解是因为我按照惯例将它们注册为
IHandler
,而不是
IHandler
,这是两个不同的签名

我如何按照约定注册所有处理程序,使它们在被单独标识的同时被集体标识为
IEnumerable


第二次注册可以,但更希望通过两个签名来解析一个实现。

处理程序
foodhandler:IHandler
不能强制转换为
IHandler
。使用任何IoC都无法实现这一点,因为TRequest类型参数不能同时输入和输出

如果您希望“双向”,则必须将处理程序绑定到某个通用接口,例如空的
IHandler
接口。然后注入所有参数,通过反射找到正确的参数,并使用转换为正确类型的参数调用它

当然,另一种选择是将
void句柄(IEnumerable请求)
的签名更改为
void句柄(IEnumerable请求)
,并让实现进行转换。但我认为这对你的情况来说是一个更糟糕的解决方案

对于每种类型,还需要两个不同的绑定或一个多绑定,如
Bind().To().InSingletonScope()
。 当需要作用域时,多重绑定非常有用。如果您使用
InSingletonScope
为同一类型提供了两个绑定,则将有两个实例。如果使用多重绑定,则只有一个实例


要使Steven的
CompositeHandler
与ninject配合使用,您必须稍微调整解决方案,并为复合处理程序引入一个新的界面:

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);
        }
    }
}
公共类复合请求:IRequest
{
公共CompositeRequest(用于[]请求的参数)
{
this.Requests=请求;
}
[]请求{get;private set;}的公共TFor
}
公共接口ICompositeHandler:IHandler{}
公共类CompositeHandler:ICompositeHandler
其中TFor:IRequest
{
私有只读IHandler处理程序;
公共复合处理器(IHandler处理器)
{
this.handler=handler;
}
公共无效句柄(CompositeRequest请求)
{
foreach(request.Requests中的var r)
{
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>>();
// 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<>));
var-kernel=new-StandardKernel();
Bind(typeof(ICompositeHandler)).To(typeof(CompositeHandler));
Bind(x=>x.FromThisAssembly()
.SelectAllClasses()
.继承自(typeof(IHandler))
.不包括(类型(复合手柄))
.BindDefaultInterfaces());
Get();

我已经验证了它的有效性。

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

public interface IHandler<in TFor> where TFor : IRequest
{
    void Handle(TFor request);
}
公共接口IHandler,其中TFor:IRequest
{
无效句柄(用于请求);
}
并具有允许处理多个实例的通用实现:

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);
        }
    }
}
公共类复合请求:IRequest
{
公共CompositeRequest(用于[]请求的参数)
{
this.Requests=请求;
}
[]请求{get;private set;}的公共TFor
}
公共类复合句柄:IHandler
其中TFor:IRequest
{
私有只读IHandler处理程序;
公共复合处理器(IHandler处理器)
{
this.handler=handler;
}
公共无效句柄(CompositeRequest请求)
{
foreach(request.Requests中的var r)
{
this.handler.Handle(r);
}
}
}
这消除了每个处理程序实现foreach循环的需要,如果列表的处理方式发生了变化,您只需在一个地方进行更改

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

更新

对于简单的注入器,注册简单如下:

        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>>();
// 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<>));
//使用SimpleInjector.Extensions;
//批量注册系统中的所有处理程序。
container.RegisterManyForOpenGeneric(typeof(IHandler)),
类型(AssignmentHandler).Assembly);
//注册打开的泛型CompositeHandler
容器注册器通用(typeof(IHandler),typeof(CompositeHandler));

a handler
foodhandler:IHandler
不能强制转换为
IHandler
。使用任何IoC都无法实现这一点,因为
TRequest
类型参数不能同时是
in
out
。当您可以直接使用
IRequest
接口时,为什么要使用
TFor
泛型?因为每个请求类型都有自己的处理程序类型。Steven,您将如何在SimpleInjector中注册此项?这可能是切换的一个令人信服的理由:)@Baldy:我在回答其他DI容器的问题时试图阻止升级Simple Injector,但因为您明确提出:请参阅我的更新:-)。对于ninject,开放泛型绑定是这样做的:
IBindingRoot.Bind(typeof(IHandler)).to(typeof(MultipleHandler))@BatteryBackupUnit:你试过了吗?那不行。当您尝试解析复合处理程序时,Ninject抛出“检测到循环依赖项”异常。好的,抱歉,是的,我忽略了这一部分。我更新了我的答案,以反映如何使用ninject实现这一点。Ninject确实不支持像
.Bind(typeof(IHandler))
这样的符号。第一