首页 > 解决方案 > Autofac - 基于注册提供者的动态解析

问题描述

我在为我的问题找到合适的解决方案时遇到问题,即:

让我们考虑工作流程:

  1. 申请开始
  2. 主要组件在 Autofac 中注册
  3. 应用程序加载插件程序集并在其中注册模块
  4. 正在构建容器
  5. 插件处理逻辑运行

插件可以添加自己的控制器。为了正确处理这个问题,我必须准备接口,该接口将为我提供自定义控制器的类型:

interface ICustomControllerProvider
{
    IEnumerable<Type> GetControllerTypes();
}

基于上述,我的应用程序知道如何将指定类型集成为控制器。所有控制器也被定义为服务,所以 Autofac 处理它们的创建,所以......

问题:我想避免两次指定自定义控制器类型

public PluginControllerProvider : ICustomControllerProvider
{
    public IEnumerable<Type> GetControllerTypes()
    {
        // 1st type specification
        // controller types are specified here, so they could be integrated with app
        yield return typeof(ControllerX);
        yield return typeof(ControllerY); 
    }
}

public class PluginModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<PluginControllerProvider>().As<ICustomControllerProvider>();
        // 2nd type specification
        // controllers have to be register in module as well
        builder.RegisterType<ControllerX>();
        builder.RegisterType<ControllerY>();
    }
}

有什么方法可以由 Autofac 管理,我只在其中指定了ControllerX它们?我尝试通过提供自定义注册源和解析来实现这一点,但是我无法根据from提供的参数进行解析ControllerYPluginControllerProviderICustomControllerProviderICustomControllerProviderIEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<ServiceRegistration>> registrationAccessor)IRegistrationSource

标签: autofac

解决方案


Autofac 不提供注入使用 Autofac注册的类型列表的能力。您必须自己通过使用生命周期范围注册表来获得它。

在我讨论如何解决这个问题之前,我可能应该注意:

  • 从 DI 的角度来看,列出注册的类型并不是一件正常的事情。这就像列出 MVC 应用程序中的所有控制器一样——您通常不需要这样做,如果这样做,您可能需要在其上构建一个完整的元数据结构,就像构建的那样ApiExplorer在 ASP.NET Core 之上执行此操作。DI 系统不会支持或涉及该结构,因为您正在查询系统,而不是将实时实例注入系统。
  • 如果您依赖 DI 来解析控制器,您可能不需要一个完全独立的控制器提供程序。一旦你知道你需要什么类型的请求,你就可以解决它。

这就是说,虽然我会回答这个问题,但这里的设计方式可能是你想看的。您在做什么,试图让 DI 与列出有关应用程序的元数据...似乎有些倒退(您将根据类型列表提供 DI 容器,而不是从 DI 容器中获取类型列表)。

但是,让我们随它去吧。鉴于:

  • 有向 Autofac 注册的控制器
  • 控制器没有通用的基类或接口
  • 您可以查询的控制器上没有属性

我要做的是用一些元数据注册所有控制器。您需要一些东西才能在容器中所有类型的列表中找到控制器。

builder.RegisterType<ControllerX>().WithMetadata("controller", true);
builder.RegisterType<ControllerY>().WithMetadata("controller", true);

现在在插件控制器中,您需要注入一个,ILifetimeScope因为您必须查询已注册的内容列表。ILifetimeScope注入控制器的 将与解析插件控制器本身的范围相同。

您可以使用注入的范围来查询组件注册表中标记有元数据的内容。

public class PluginControllerProvider : ICustomControllerProvider
{
    private readonly Type[] _controllerTypes;
    public PluginController(ILifetimeScope scope)
    {
        _controllerTypes = scope
            .ComponentRegistry
            .Registrations
            .Where(r => r.Metadata.ContainsKey("controller"))
            .Select(r => r.Activator.LimitType)
            .ToArray();
    }

    public IEnumerable<Type> GetControllerTypes()
    {
        return _controllerTypes;
    }
}

现在,免责声明:

  • 如果您在子生命周期范围内注册更多控制器(例如,在BeginLifetimeScope()调用期间),您将需要该范围内的控制器提供程序,否则它将无法获得所有控制器类型。提供者需要来自具有所有注册的范围。
  • 如果您正在使用注册源(如AnyConcreteTypeNotAlreadyRegisteredSource),则不会捕获来自注册源的内容。它只会捕获来自ContainerBuilder.

但它应该工作。


推荐阅读