使用 RegisterAssemblyTypes 时通过短 class 名称解析服务

Resolve service by short class name while using RegisterAssemblyTypes

我想通过 class 短名称获取组件。 示例:

public interface ITransactionDef{}
public class Transaction1 : ITransactionDef {}

我将有数百笔交易 classes。所以我用它来注册所有 ITransactionDef-s:

builder.RegisterAssemblyTypes(typeof(MyType).Assembly)
    .AssignableTo<ITransactionDef>().As<ITransactionDef>()
    .AsSelf().InstancePerLifetimeScope();

但是在我的控制器中我有这个:

public IActionResult GetTransaction([FromQuery]GetTransactionRequestDto actionRequestDto) 
{
    ITransactionDef trx = (ITransactionDef)serviceProvider
        .GetServices(typeof(ITransactionDef))
        .FirstOrDefault(s => s.GetType().Name == actionRequestDto.TransactionId);
    ...
}

我不想要这个,因为它会根据每个请求构建所有交易。我想了三个解决方案:

  1. actionRequestDto.TransactionId 中接收完整的 class 名称,获取类型并使用该类型代替 typeof(ITransactionDef)。我想跳过这个。在前端某处使用完整的 class 名称似乎有点奇怪。
  2. 使用命名组件,但是我没有找到通过其短 class 名称注册组件的方法。此外,我需要在 RegisterAssemblyTypes.
  3. 中执行此操作
  4. 使用TransactionDefRepository并用它注册组件:
builder.Register(c => new TransactionDefRepository());

builder.RegisterAssemblyTypes(typeof(MyType).Assembly).AssignableTo<ITransactionDef>().
            OnActivating(e => {
                TransactionDefRepository repository =
                    e.Context.Resolve<TransactionDefRepository>();
                ITransactionDef transactionDef = ((ITransactionDef)e.Instance);
                repository.Register(transactionDef);
            })
            .AsSelf().InstancePerLifetimeScope();

然后在控制器方法中使用它:

Type trxType = trxRepository.GetTransactionType(actionRequestDto.TransactionId);
ITransactionDef trx = (ITransactionDef)serviceProvider.GetServices(trxType)

这将不起作用,因为在请求组件时会调用 OnActivating,因此 TransactionDefRepository 为空。

我该如何实施?最好不用解1.

当您说“短 class 名称”时,我假设您的意思是 Type.Name (SomeTransaction) 而不是 Type.FullName (MyNamespace.SomeTransaction)。

在这里您不会看到任何特定于 Autofac 的内容来帮助您,因为类型确实需要限定。如果您考虑一下,您可能有 FirstNamespace.SomeTypeSecondNamespace.SomeType,所以 Type.Name 将是同一件事。 (实际上,从技术上讲,您可以在 中同时加载两个不同的程序集 中的相同 Type.FullName,这样您就可以在那个时候进入 Type.AssemblyQualifiedName,但我们不要在那里的杂草中。)

因此,如果你想解决它,可能你必须自己写一些东西。

但是,有一种方法我可以 可以 仅使用 Autofac 来完成,但是 IServiceProvider 无法使用它,您必须使用 Autofac:元数据

注册类型时,您可以注册元数据。在注册时添加 SimpleName 元数据值。

builder
  .RegisterAssemblyTypes(typeof(MyType).Assembly)
  .AssignableTo<ITransactionDef>()
  .WithMetadata("SimpleName", t => t.Name)
  .As<ITransactionDef>()
  .InstancePerLifetimeScope();

请注意,我将其切换为将它们注册为 ITransactionDef 这会派上用场,因为您可以将类型注入控制器 而无需使用 [=27= 解析它们 ].它们是可组合的,因此:

public class MyController
{
  private readonly IEnumerable<Meta<Lazy<ITransactionDef>>> _transactionDefs;

  public MyController(IEnumerable<Meta<Lazy<ITransactionDef>>> transactionDefs)
  {
    this._transactionDefs = transactionDefs;
  }

  public string SomeAction(RequestObject request)
  {
    var simpleName = request.GetTheSimpleName();

    // Find the right Meta<T> from the list
    var meta = this._transactionDefs.First(m => m.Metadata["SimpleName"].Equals(simpleName));
    // Get the Lazy<T> out of the Meta<T>
    var lazy = meta.Value;
    // Get the T out of the Lazy<T>
    var transactionDef = lazy.Value;

    // Now you have the right thing resolved
    transactionDef.DoWork(request);
  }
}

您可以将其压缩为 one-liner LINQ 查询,但我将其分解,以便您了解发生了什么。

无论如何,这是一种方法。如果需要,您可以在技术上将其包装在工厂中,然后注入工厂。

public class DefFactory
{
  private readonly IEnumerable<Meta<Lazy<ITransactionDef>>> _transactionDefs;

  public DefFactory(IEnumerable<Meta<Lazy<ITransactionDef>>> transactionDefs)
  {
    this._transactionDefs = transactionDefs;
  }

  public ITransactionDef GetDef(string simpleName)
  {
    return this._transactionDefs
      .First(m => m.Metadata["SimpleName"].Equals(simpleName))
      .Value
      .Value;
  }
}

您可以根据需要扩展概念。但是,无论如何,这可能是获得所需内容的最简单方法。