autofac 解析所有类型的开放泛型类型?
autofac Resolve all types of open generic type?
我猜想没有办法用 Autofac 做类似下面的事情,以 ctor 注入开放泛型类型的可枚举集合?各种 Handle 类型具有依赖性,否则我会动态构建它们。
class EventOne : IEvent {...}
class EventTwo : IEvent {...}
class EventThree : IEvent {...}
interface IHandleEvent<T> where T : IEvent {...}
class HandleEventOne : IHandleEvent<EventOne> {...}
class HandleEventTwo : IHandleEvent<EventTwo> {...}
class HandleEventThree : IHandleEvent<EventThree> {...}
builder.RegisterAssemblyTypes(myAssembies).AsClosedTypesOf(typeof(IHandleEvent<>));
builder.RegisterType<AService>().As<IAService>();
class AService : IAService
{
public AService(IEnumerable<IHandleEvent<IEvent>> handles)
{...}
}
您将无法将 IHandleEvent<EventThree>
转换为 IHandleEvent<IEvent>
,因为 IHandleEvent<T>
不是协变,您可以通过添加 out
修饰符来添加它。
public interface IHandleEvent<out TEvent>
where TEvent : IEvent
{ }
不幸的是Autofac不支持协变类型,只支持逆变类型。
顺便说一句,您可以创建一个自定义 IRegistrationSource
实现来获得请求的行为。像这样:
public class CovariantHandleEventRegistrationSource : IRegistrationSource
{
public bool IsAdapterForIndividualComponents
{
get
{
return false;
}
}
public IEnumerable<IComponentRegistration> RegistrationsFor(Service service,
Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
IServiceWithType typedService = service as IServiceWithType;
if (typedService == null)
{
yield break;
}
if (typedService.ServiceType.IsGenericType && typedService.ServiceType.GetGenericTypeDefinition() == typeof(IHandleEvent<>))
{
IEnumerable<IComponentRegistration> eventRegistrations = registrationAccessor(new TypedService(typeof(IEvent)));
foreach (IComponentRegistration eventRegistration in eventRegistrations)
{
Type handleEventType = typeof(IHandleEvent<>).MakeGenericType(eventRegistration.Activator.LimitType);
IComponentRegistration handleEventRegistration = RegistrationBuilder.ForDelegate((c, p) => c.Resolve(handleEventType, p))
.As(service)
.CreateRegistration();
yield return handleEventRegistration;
}
}
}
}
有了这个 IRegistrationSource
你可以拥有这个:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<EventOne>().As<IEvent>();
builder.RegisterType<EventTwo>().As<IEvent>();
builder.RegisterType<EventThree>().As<IEvent>();
builder.RegisterAssemblyTypes(typeof(Program).Assembly)
.AsClosedTypesOf(typeof(IHandleEvent<>));
builder.RegisterSource(new CovariantHandleEventRegistrationSource());
IContainer container = builder.Build();
var x = container.Resolve<IEnumerable<IHandleEvent<IEvent>>>();
正如评论中所解释的那样,你想要的东西在 C# 中是不可能实现的,而且有充分的理由。如果您能够将 IHandleEvent<EventOne>
转换为 IHandleEvent<IEvent>
,它也会允许传入 EventTwo
,这将在运行时失败。
因此,您需要的是一个中介抽象,它允许获取所有兼容的事件处理程序并调用它们。这种调解器通常称为 IEventPublisher
,可能看起来像这样:
public interface IEventPublisher {
void Publish(IEvent e);
}
您现在可以创建特定于容器的实现。例如,对于 Autofac,这将如下所示:
public class AutofacEventPublisher : IEventPublisher {
private readonly IComponentContext container;
public AutofacBusinessRuleValidator(IComponentContext container) {
this.container = container;
}
public void Publish(IEvent e) {
foreach (dynamic handler in this.GetHandlers(e.GetType())) {
handler.Handle((dynamic)e);
}
}
private IEnumerable GetHandlers(Type eventType) =>
(IEnumerable)this.container.Resolve(
typeof(IEnumerable<>).MakeGenericType(
typeof(IHandleEvent<>).MakeGenericType(eventType)));
}
消费者现在可以依赖这个新的抽象:
class AService : IAService
{
public AService(IEventPublisher publisher) {...}
}
我猜想没有办法用 Autofac 做类似下面的事情,以 ctor 注入开放泛型类型的可枚举集合?各种 Handle 类型具有依赖性,否则我会动态构建它们。
class EventOne : IEvent {...}
class EventTwo : IEvent {...}
class EventThree : IEvent {...}
interface IHandleEvent<T> where T : IEvent {...}
class HandleEventOne : IHandleEvent<EventOne> {...}
class HandleEventTwo : IHandleEvent<EventTwo> {...}
class HandleEventThree : IHandleEvent<EventThree> {...}
builder.RegisterAssemblyTypes(myAssembies).AsClosedTypesOf(typeof(IHandleEvent<>));
builder.RegisterType<AService>().As<IAService>();
class AService : IAService
{
public AService(IEnumerable<IHandleEvent<IEvent>> handles)
{...}
}
您将无法将 IHandleEvent<EventThree>
转换为 IHandleEvent<IEvent>
,因为 IHandleEvent<T>
不是协变,您可以通过添加 out
修饰符来添加它。
public interface IHandleEvent<out TEvent>
where TEvent : IEvent
{ }
不幸的是Autofac不支持协变类型,只支持逆变类型。
顺便说一句,您可以创建一个自定义 IRegistrationSource
实现来获得请求的行为。像这样:
public class CovariantHandleEventRegistrationSource : IRegistrationSource
{
public bool IsAdapterForIndividualComponents
{
get
{
return false;
}
}
public IEnumerable<IComponentRegistration> RegistrationsFor(Service service,
Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
IServiceWithType typedService = service as IServiceWithType;
if (typedService == null)
{
yield break;
}
if (typedService.ServiceType.IsGenericType && typedService.ServiceType.GetGenericTypeDefinition() == typeof(IHandleEvent<>))
{
IEnumerable<IComponentRegistration> eventRegistrations = registrationAccessor(new TypedService(typeof(IEvent)));
foreach (IComponentRegistration eventRegistration in eventRegistrations)
{
Type handleEventType = typeof(IHandleEvent<>).MakeGenericType(eventRegistration.Activator.LimitType);
IComponentRegistration handleEventRegistration = RegistrationBuilder.ForDelegate((c, p) => c.Resolve(handleEventType, p))
.As(service)
.CreateRegistration();
yield return handleEventRegistration;
}
}
}
}
有了这个 IRegistrationSource
你可以拥有这个:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<EventOne>().As<IEvent>();
builder.RegisterType<EventTwo>().As<IEvent>();
builder.RegisterType<EventThree>().As<IEvent>();
builder.RegisterAssemblyTypes(typeof(Program).Assembly)
.AsClosedTypesOf(typeof(IHandleEvent<>));
builder.RegisterSource(new CovariantHandleEventRegistrationSource());
IContainer container = builder.Build();
var x = container.Resolve<IEnumerable<IHandleEvent<IEvent>>>();
正如评论中所解释的那样,你想要的东西在 C# 中是不可能实现的,而且有充分的理由。如果您能够将 IHandleEvent<EventOne>
转换为 IHandleEvent<IEvent>
,它也会允许传入 EventTwo
,这将在运行时失败。
因此,您需要的是一个中介抽象,它允许获取所有兼容的事件处理程序并调用它们。这种调解器通常称为 IEventPublisher
,可能看起来像这样:
public interface IEventPublisher {
void Publish(IEvent e);
}
您现在可以创建特定于容器的实现。例如,对于 Autofac,这将如下所示:
public class AutofacEventPublisher : IEventPublisher {
private readonly IComponentContext container;
public AutofacBusinessRuleValidator(IComponentContext container) {
this.container = container;
}
public void Publish(IEvent e) {
foreach (dynamic handler in this.GetHandlers(e.GetType())) {
handler.Handle((dynamic)e);
}
}
private IEnumerable GetHandlers(Type eventType) =>
(IEnumerable)this.container.Resolve(
typeof(IEnumerable<>).MakeGenericType(
typeof(IHandleEvent<>).MakeGenericType(eventType)));
}
消费者现在可以依赖这个新的抽象:
class AService : IAService
{
public AService(IEventPublisher publisher) {...}
}