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) {...}
}