4

这个问题源于我正在尝试为 MediatR 创建一个简单的注入器实现:https ://github.com/jbogard/MediatR/pull/14 。

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

public interface INotificationHandler<in TNotification>
    where TNotification : INotification
{
    void Handle(TNotification notification);
}

INotifcation只是一个空的标记界面。

Pinged我为(实现INotification)事件定义了以下处理程序:

public class PingedHandler : INotificationHandler<Pinged>
{
    public void Handle(Pinged notification) { }
}

public class PingedHandler2 : INotificationHandler<Pinged>
{
    public void Handle(Pinged notification) { }
}

还有一个通用处理程序(注意这应该处理每个INotification):

public class GenericHandler : INotificationHandler<INotification>
{
    public void Handle(INotification notification) { }
}

通过以下注册:

var container = new Container();

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

现在我期望:

GetAllInstances<INotificationHandler<Pinged>>();

解决这两个PingedHandler问题PingedHandler2。但它并没有解决,GenericHandler因为它实现了INotificationHandler<INotification>而不是INotificationHandler<Pinged>. 我想知道是否有办法让 Simple Injector 搜索整个对象图并解决任何问题Pinged

我发现了 Steven 的一篇关于协方差和逆变的博客文章,但我无法让它适用于我的示例。

4

2 回答 2

3

更新

从 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());
}

GenericHandler但是通用化可能更容易:

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());
}
于 2015-01-12T21:25:39.277 回答
2

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


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

// Simple Injector v3.x
container.RegisterCollection(typeof(INotificationHandler<>),
    AppDomain.CurrentDomain.GetAssemblies());

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

现在 Simple Injector 能够解析PingedHandlerPingedHandler2 并且GenericHandler请求时:

container.GetAllInstances<INotificationHandler<Pinged>>();
于 2015-01-13T09:49:57.293 回答