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 handlerfoodhandler: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))
这样的符号。第一