C# 使用Autofac将多种类型之一注册到接口

C# 使用Autofac将多种类型之一注册到接口,c#,autofac,C#,Autofac,Autofac有大量的注册技术,其中大部分我很难理解。阅读了一段时间的文档,我没有看到任何允许我这样做的东西 我有一个我无法控制的系统,它基于一些命令行参数实例化某个类。这个类是从公共基类派生的许多类之一。例如: 抽象类BaseCommand{} 类CommandOne:BaseCommand{} 类CommandTwo:BaseCommand{} 在组件注册之后,我才知道该库将构造哪个派生类(无论是CommandOne还是CommandTwo)。下面是代码的大致轮廓: static void

Autofac有大量的注册技术,其中大部分我很难理解。阅读了一段时间的文档,我没有看到任何允许我这样做的东西

我有一个我无法控制的系统,它基于一些命令行参数实例化某个类。这个类是从公共基类派生的许多类之一。例如:

抽象类BaseCommand{}
类CommandOne:BaseCommand{}
类CommandTwo:BaseCommand{}
在组件注册之后,我才知道该库将构造哪个派生类(无论是
CommandOne
还是
CommandTwo
)。下面是代码的大致轮廓:

static void Main(){
//完成所有autofac注册
IContainer容器=CompositionRoot.Setup();
//解析命令行参数
ParseCommandLine(类型=>container.Resolve(类型))
}
ParseCommandLine()
方法是构建我前面提到的两个类的“黑盒”代码。它通过调用我传递给它的lambda来实现。它永远不会实例化这两个类,而只实例化其中一个

我需要的是Autofac允许我将BaseCommand作为服务“惰性注册”。也就是说,在注册时,我们不知道将选择哪个特定上下文,但我们知道它将始终从
BaseCommand
派生。大致如下:

var builder=newcontainerbuilder();
Register(ctx=>/*访问传递给Resolve()的类型,并实例化该类型*/).As().AsSelf().SingleInstance();
所以我想要的基本上是:

  • 我们只能为
    BaseCommand
  • 在解决之前,我们不知道该实现是什么
  • 我们使用提供给
    Resolve()
    的类型来决定
  • 我们将只有一个这种类型的实例
  • 尝试解析任何其他具体类型都应该失败(例如,一旦解析类型
    CommandOne
    ,解析
    CommandTwo
    应该抛出异常)

  • 我如何在Autofac中实现这一点?

    我不确定您是否能够获得100%的所需内容,但我认为您可以通过一点间接操作来实现

    首先,您需要一种可以在lambda表达式中使用的中介程序,以便延迟实际的注册内容。它有几个工作:

    • 存储黑盒拾取的选定类型。通过这种方式,可以在lambda中使用它来“延迟”确定选择了哪些命令
    • 注册可用的命令,但使用随机“键”,这样它们就不会意外地得到解析。容器是不可变的,因此以后不能“注销”内容,需要解决(或可能解决)的任何内容都需要注册
    • 解析可用的命令,但使用筛选器以获取选定的命令
    它看起来像这样:

    公共静态类CommandFilter
    {
    私有静态只读Guid _id=Guid.NewGuid();
    公共静态类型SelectedCommand{get;set;}
    公共静态无效寄存器(ContainerBuilder生成器,类型)
    {
    //解决这些问题需要“密钥”,
    //所以container.Resolve()不起作用-它会
    //是容器。已解析密钥(_id)以获取它。
    builder.RegisterType(type).Keyed(type,_id);
    }
    公共静态BaseCommand解析(IComponentContext上下文)
    {
    //仅解析所选的实现,其他一切
    //保持“隐藏”
    返回context.resolvedkeyed(_id,CommandFilter.SelectedCommand)作为BaseCommand;
    }
    }
    
    现在您需要注册所有的
    BaseCommand
    实现,但要使用这个过滤器。这意味着您需要进行一些手动装配扫描

    //警告:我永远记不起正确的方向
    //这是可识别的,所以请通过测试进行检查。
    var asm=typeof(BaseCommand).Assembly;
    foreach(asm.GetTypes()中的变量类型,其中(t=>typeof(BaseCommand).IsAssignableFrom(t)))
    {
    //注册您的“机密”密钥注册。
    CommandFilter.Register(生成器,类型);
    }
    //注册BaseCommand本身。
    builder.Register(ctx=>CommandFilter.Resolve(ctx))
    .As()
    .SingleInstance();
    
    在黑盒中,关键是首先设置过滤器类型,然后解析

    ParseCommandLine(类型=>{
    CommandFilter.SelectedCommand=类型;
    container.Resolve();
    });
    
    注册中的lambda允许您推迟选择;传递给
    ParseCommandLine
    的lambda可以进行选择;只有你想解决的问题才会解决。其他所有内容都已注册,但实际上无法访问,对
    container.Resolve()
    的全权委托调用将失败,并出现解析异常,因为它们已注册为密钥,需要密钥才能解析


    我实际上还没有通过编译器来运行它,但是看起来这应该可以做到。根据“真实”系统的不同,您可能需要在设置
    SelectedCommand
    的位置设置一个锁,因为
    container.Resolve()
    是线程安全的,而选择机制在其外部运行。在设置
    SelectedCommand
    之前,可能还需要一些错误处理,以便尝试从命令过滤器中解决问题的人员进行此类操作。

    您不能在解决问题的同时注册。你什么时候知道这种类型?它是命令行上的参数吗?配置选项?更新问题,将其包括在内。我想我已经说得够清楚了。有两个命令。解析这些命令的库根据传递的选项创建这两种类型中的一种。在启动时我们知道我的服务有两种可能的实现,但只有传递给lambda的类型才是应该