1

我有一个 autofac 注册的情况,其中服务相互关联。这是我现在拥有的:

var builder = new ContainerBuilder();

const string A = "A";
const string B = "B";

//Part 1
builder.RegisterType<ClassA1>().Named<IClass1>(A);
builder.RegisterType<ClassA2>().Named<IClass2>(A);

builder.RegisterType<ClassB1>().Named<IClass1>(B);
builder.RegisterType<ClassB2>().Named<IClass2>(B);

//Part 2
builder.Register(c => new ClassProcess(A, c.ResolveNamed<IClass1>(A), c.ResolveNamed<IClass2>(A))).Named<ClassProcess>(A).SingleInstance();
builder.Register(c => new ClassProcess(B, c.ResolveNamed<IClass1>(B), c.ResolveNamed<IClass2>(B))).Named<ClassProcess>(B).SingleInstance();

//Part 3
builder.Register(c => new ClassProcessDependency(c.ResolveNamed<ClassProcess>(B), c.ResolveNamed<ClassProcess>(A)));

在第 1 部分中,我定义了两个成对连接的基本类,IClass1 和 IClass2。

在第 2 部分中,我定义了一个连接这两个类的“过程”。这个过程实际上是传递给程序其余部分的,它需要一个 IEnumerable。

在第 3 部分中,我定义了这些进程之间存在的任何依赖关系,以便它们以正确的顺序执行。

我在这里寻找的是 Autofac 中的一些注册方法,它可以使用以前使用 name/key/meta 注册的服务,并使用该数据注册新服务。

编辑:请注意,我正在尝试找出是否有任何 Autofac 特定的方法来执行此操作。

4

1 回答 1

3

假设您将常量放在列表中,您可以枚举...

IEnumerable<string> constants

...你有什么理由不能这样做吗?

foreach(var constant in constants)
{
  // Careful of the closure...
  var loopItem = constant;
  builder
    .Register(c =>
      new ClassProcess(
        loopItem,
        c.ResolveNamed<IClass1>(loopItem),
        c.ResolveNamed<IClass2>(loopItem)))
    .Named<ClassProcess>(loopItem)
    .SingleInstance();
}

除非我弄错或误解了,否则唯一阻碍您的是标识符常量集不在列表中,这不是 Autofac 可以帮助您的。

如果您不喜欢在代码中使用该 foreach,您可以随时将其隐藏在模块中:

public class ProcessModule : Autofac.Module
{
  private IEnumerable<string> _constants;
  public ProcessModule(IEnumerable<string> constants)
  {
    this._constants = constants;
  }

  protected override void Load(ContainerBuilder builder)
  {
    // Put the foreach here and loop over this._constants.
  }
}

然后在您的主代码中注册该模块:

builder.RegisterModule(new ProcessModule(constants));

如果您不能像那样循环,那么您的自动化方法可能会受到限制。或者,至少,你会看到更多……复杂的东西。

ContainerBuilder确实不允许在注册项目集时直接访问它们,因此您必须做一些低级的诡计。您可能可以实现一个IRegistrationSource来定位某种类型的所有命名注册并进行自动化。

不幸的是,如上所述,这个过程有点复杂,所以我不打算写出完整的解决方案。不过,我基本上会告诉您我认为可行的方法,并为您提供一些参考,以便您查看源代码中的示例。

在你开始这条路之前Nick Blumhardt 在他的博客上有一篇很好的文章,解释了如何编写其中的一个,并使用一个内置的 Autofac 源作为示例。

首先,您需要实现一个Autofac.Core.IRegistrationSource. 这是用于执行添加隐式集合支持等操作的接口(因此,如果您注册五个IFoo实例,您可以解析IEnumerable<IFoo>并且它可以工作)。您可以在核心 Autofac 程序集中的 Autofac 代码中查看注册源示例:

注册源中的主要功能是RegistrationsFor方法,它返回您的源生成的注册列表:

IEnumerable<IComponentRegistration> RegistrationsFor(
  Service service,
  Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor);

您对此方法的实现将检查传入service是否...

  • 一个Autofac.Core.KeyedService(因为您要命名所有服务)并且
  • 您期望的类型。

如果所有行星都对齐,则返回一个IComponentRegistration为您ClassProcess的所有设置都已填写的行星。

现在,这将变得更加困难,因为您ClassProcess需要两个已命名的输入参数,并且您可能不想注册已命名ClassProcess的参数,除非您确定所有参数依赖项也已注册,因此您必须保留跟踪您看到的服务,并且只有在ClassProcess您看到它的所有依赖项后才注册它。(您可以使用KeyedService进入来确定服务的名称是什么。)

此外,您不再使用ContainerBuilder创建该组件注册,因此您必须找出与“生命周期范围”等内容相对应的注册的所有正确设置。

一旦你有了你的IRegistrationSource,你需要把它连接到ContainerBuilder.

builder.RegisterSource(new MyRegistrationSource());

同样,如果您对此感兴趣,我会查看 Autofac 树中的那些源文件。由于复杂性,这不是很多人走的路。

老实说...如果您可以遍历常量,我会这样做。为了避免 foreach 循环,这似乎需要做很多工作。

于 2012-06-13T16:23:30.810 回答