调解器模式和简单注入器的逆变
Mediator pattern and contravariance with Simple Injector
这个问题源于我正在尝试为 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 搜索整个对象图并解析 anything 也就是 Pinged
。
我从 Steven 那里找到了 a blog post 关于协方差和逆方差的内容,但我无法在我的示例中使用它。
更新
从 Simple Injector 2.7 版开始,this functionality 现已成为标准,您不再需要下面显示的解决方法。
您缺少该文章中描述的 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());
}
tl;dr:它是 Simple Injector v2.6.0 中的 bug/shortcoming,已在 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 能够解析 PingedHandler
、PingedHandler2
和 请求时的 GenericHandler
:
container.GetAllInstances<INotificationHandler<Pinged>>();
这个问题源于我正在尝试为 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 搜索整个对象图并解析 anything 也就是 Pinged
。
我从 Steven 那里找到了 a blog post 关于协方差和逆方差的内容,但我无法在我的示例中使用它。
更新
从 Simple Injector 2.7 版开始,this functionality 现已成为标准,您不再需要下面显示的解决方法。
您缺少该文章中描述的 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());
}
tl;dr:它是 Simple Injector v2.6.0 中的 bug/shortcoming,已在 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 能够解析 PingedHandler
、PingedHandler2
和 请求时的 GenericHandler
:
container.GetAllInstances<INotificationHandler<Pinged>>();