C# 简单注入器的中介模式与逆变

C# 简单注入器的中介模式与逆变,c#,simple-injector,mediator,mediatr,C#,Simple Injector,Mediator,Mediatr,这个问题源于我试图为MediatR创建一个简单的注入器实现 我在尝试解决通用处理程序接口的实现时遇到问题。考虑以下通知处理程序接口: public interface INotificationHandler<in TNotification> where TNotification : INotification { void Handle(TNotification notification); } 还有一个通用处理程序(注意,它应该处理每个INotificat

这个问题源于我试图为MediatR创建一个简单的注入器实现

我在尝试解决通用处理程序接口的实现时遇到问题。考虑以下通知处理程序接口:

public interface INotificationHandler<in TNotification>
    where TNotification : INotification
{
    void Handle(TNotification notification);
}
还有一个通用处理程序(注意,它应该处理每个
INotification
):

公共类GenericHandler:INotificationHandler
{
公共无效句柄(INotification通知){}
}
注册如下:

var container = new Container();

container.RegisterManyForOpenGeneric(
    typeof (INotificationHandler<>),
    (service, impls) => container.RegisterAll(service, impls),
    AppDomain.CurrentDomain.GetAssemblies());
var container=newcontainer();
container.RegisterManyForOpenGeneric(
类型(INotificationHandler),
(服务,impls)=>container.RegisterAll(服务,impls),
AppDomain.CurrentDomain.GetAssemblys());
现在我希望:

GetAllInstances<INotificationHandler<Pinged>>();
GetAllInstances();
要同时解析
PingedHandler
PingedHandler2
,它会这样做。但是它没有解析
GenericHandler
,因为它实现了
INotificationHandler
,而不是
INotificationHandler
。我想知道是否有一种方法可以让Simple Injector搜索整个对象图,并解决任何被ping的问题


我从Steven那里找到了协方差和反方差,但我无法让它在我的例子中起作用。

更新

从Simple Injector 2.7版开始,现在是标准版,您不再需要下面所示的解决方法


您缺少了那篇文章中描述的
MultipleDispatchEventHandler
的一个变体。采用基本逻辑并将其应用于您的抽象,确实会得到您期望的结果:

[Fact]
public void RegisterAll_Contravariant_Succeeds()
{
    var container = new Container();

    container.RegisterManyForOpenGeneric(
        typeof(INotificationHandler<>),
        (service, impls) => container.RegisterAll(service, impls),
        AppDomain.CurrentDomain.GetAssemblies());

    var handlersType = typeof(IEnumerable<INotificationHandler<Pinged>>);

    var handlersCollection = (
        from r in container.GetCurrentRegistrations()
        where handlersType.IsAssignableFrom(r.ServiceType)
        select r.GetInstance())
        .Cast<IEnumerable<INotificationHandler<Pinged>>>()
        .ToArray();

    var result = 
        from handlers in handlersCollection
        from handler in handlers
        select handler;

    Assert.Equal(3, result.Count());
}
简化登记手续

[Fact]
public void RegisterAll_WithOpenAndClosedGenerics_Succeeds()
{
    var container = new Container();

    var types = OpenGenericBatchRegistrationExtensions
        .GetTypesToRegister(
            container,
            typeof(INotificationHandler<>),
            AccessibilityOption.AllTypes,
            AppDomain.CurrentDomain.GetAssemblies())
        .ToList();

    types.Add(typeof(GenericHandler<>));

    container.RegisterAll(typeof(INotificationHandler<>), types);

    var result = container.GetAllInstances<INotificationHandler<Pinged>>().ToList();

    Assert.Equal(3, result.Count());
}
[事实]
带有OpenandClosedGenerics的公共无效注册表成功()
{
var container=新容器();
变量类型=OpenGenericBatchRegistrationExtensions
.GetTypeStoreRegister(
集装箱,
类型(INotificationHandler),
AccessibilityOption.AllType,
AppDomain.CurrentDomain.GetAssemblys())
.ToList();
添加(typeof(GenericHandler));
container.RegisterAll(typeof(INotificationHandler),types);
var result=container.GetAllInstances().ToList();
Assert.Equal(3,result.Count());
}

tl;dr:这是Simple Injector v2.6.0中的一个bug/缺点,在v2.7.0中已修复。问题中的配置(如下所示)现在可以工作了


总结一下@qujck的回答和@Steven的评论:我通过以下配置安装Simple Injector v2.7.0-beta2(实际上与问题中的配置相同),使其正常工作:


INotificationHandler
INotificationHandler
不兼容,因此这并不奇怪。如果您想要所有处理程序,您是否尝试过
GetAllInstances
?@Lee
container.GetAllInstances()
只解析
GenericHandler
,这就是我希望它能做的。我想要的是,
GenericHandler
总是被解析的,因为每个通知都应该实现
INotification
@Lee,但是
INotificationHandler
INotificationHandler
@Lee
[Fact]兼容,因此可以使用statistanceofpinged()调用public void GenericHandler{var handler=new GenericHandler();Assert.DoesNotThrow(()=>handler.Handle(new Pinged());}
您所遇到的是v2.6中的一个限制/错误。如果您升级到v2.7.0-beta1,您将看到您可以使用@qujck在其答案中显示的
Registeral
注册来解决非通用
GenericHandler
问题。我更新了您对v2.7.0-beta2的答案。2.7.0-beta1中的一个错误导致注册变为neEdless很复杂。注册现在正是你所期望的。谢谢@Steven!你打算什么时候发布这个?Henk,Simple Injector 2.7已经发布。在我看来,这个答案应该被标记为“The”答案。
[Fact]
public void RegisterAll_Contravariant_Succeeds()
{
    var container = new Container();

    container.RegisterManyForOpenGeneric(
        typeof(INotificationHandler<>),
        (service, impls) => container.RegisterAll(service, impls),
        AppDomain.CurrentDomain.GetAssemblies());

    var handlersType = typeof(IEnumerable<INotificationHandler<Pinged>>);

    var handlersCollection = (
        from r in container.GetCurrentRegistrations()
        where handlersType.IsAssignableFrom(r.ServiceType)
        select r.GetInstance())
        .Cast<IEnumerable<INotificationHandler<Pinged>>>()
        .ToArray();

    var result = 
        from handlers in handlersCollection
        from handler in handlers
        select handler;

    Assert.Equal(3, result.Count());
}
public class GenericHandler<TNotification> : INotificationHandler<TNotification>
    where TNotification : INotification
{
    public void Handle(TNotification notification) { }
}
[Fact]
public void RegisterAll_WithOpenAndClosedGenerics_Succeeds()
{
    var container = new Container();

    var types = OpenGenericBatchRegistrationExtensions
        .GetTypesToRegister(
            container,
            typeof(INotificationHandler<>),
            AccessibilityOption.AllTypes,
            AppDomain.CurrentDomain.GetAssemblies())
        .ToList();

    types.Add(typeof(GenericHandler<>));

    container.RegisterAll(typeof(INotificationHandler<>), types);

    var result = container.GetAllInstances<INotificationHandler<Pinged>>().ToList();

    Assert.Equal(3, result.Count());
}
// Simple Injector v3.x
container.RegisterCollection(typeof(INotificationHandler<>),
    AppDomain.CurrentDomain.GetAssemblies());

// Simple Injector v2.x
container.RegisterManyForOpenGeneric(
    typeof(INotificationHandler<>),
    container.RegisterAll,
    AppDomain.CurrentDomain.GetAssemblies());
container.GetAllInstances<INotificationHandler<Pinged>>();