繁体   English   中英

Autofac - 如何将打开的 generics 注册为命名或键控类型,以便在工厂中使用或使用 IIndex<,>

[英]Autofac - how to register open generics as named or keyed types for use in a factory or using IIndex<,>

我正在使用 Net Core 5,除了内置的依赖注入之外,还不需要使用任何东西。 我现在需要能够根据从 UI 中的服务下拉列表中选择的值来解析特定服务。

在所有的 DI 容器中,我决定进一步研究 Autofac 并认为使用命名服务和工厂模式或键控服务将是通往 go 的方式,但我遇到了一些困难,这里是一个例子;

我有一个看起来像这样的界面

public interface IChartDataService<TSource, TTarget>

我有服务必须像这样实现这个接口

public class TreantService : IChartDataService<SourceDto, TargetDto>

我正在努力弄清楚我应该如何在 Autofac 容器中注册这些服务。 对于命名服务,我尝试像这样注册

containerBuilder.RegisterGeneric(typeof(MarkOneService)).As(typeof(IChartDataService<,>)).Named<MarkOneService>("markone");

containerBuilder.RegisterGeneric(typeof(MarkTwoService)).As(typeof(IChartDataService<,>)).Named<MarkTwoService>("marktwo");

我试着像这样解决它们

var service = scope.ResolveNamed("markone", typeof(IChartDataService<,>));

也试过这样

var service = scope.ResolveNamed("markone", typeof(IChartDataService<SourceDto,TargetDto>));

我不能正确注册它们,因为这个错误说服务没有注册。

我的首选方法是使用 Keyed 服务,这样我就可以使用“IIndex”,但这又让我不知道如何注册或者我想要做的事情是否可行。

我正在尝试注册这样的键控服务

      containerBuilder.RegisterGeneric(typeof(MarkOneService)).As(typeof(IChartDataService<,>)).Keyed("markone", typeof(IChartDataService<,>));

OR 

containerBuilder.RegisterType<MarkOneService>().As<IChartDataService<,>>().Keyed<IChartDataService<,>>("markone");

我显然遗漏了一些东西,因为我似乎无法正确使用语法来正确注册。 当我确实让注册工作时,由于开放的界面,我无法解决该服务。

另外,因为我更喜欢使用键控服务,所以我不知道如何获得注入的服务字典。 这显然行不通,因为它与我的界面不匹配

IIndex<string, IChartDataService> serviceDic

根据VS,这在语法上是无效的

IIndex<string, typeof(IChartDataService<,>)> serviceDic

OR

IIndex<string, IChartDataService<,>> serviceDic

我现在在这方面花了太多时间,而且我正在兜圈子,所以可以对我可能出错的地方进行一些其他的见解,有人有什么建议吗? 你能指出我想要实现的任何例子吗?

这里有一点要解开,虽然我可以解释为什么事情没有按照你想要的方式工作,但简短的回答是你可能不得不重新设计你正在做的事情。 这可能不是您想要的答案,但是……这就是答案。

要了解原因,我们需要暂时完全忽略 Autofac,只看一下您正在使用的类型。

你有一个界面:

public interface IChartDataService<TSource, TTarget>

作为一个开放的泛型,这可以是IChartDataService<string, Exception>或者它可以是IChartDataService<object, object>或其他任何东西。 (是的,我知道可能会有限制,但请坚持我 - 作为一个开放的泛型,这些类型可能会在整个地方发生变化。)所以当你以这种形式谈论IChartDataService<,>时,这就是你所说的: “开放的泛型,其中任何一个类型参数点都可能存在任何内容。”

现在,有时人们会创建一个同样通用的实现,例如:

public class ChartDataService<TSource, TTarget>
  : IChartDataService<TSource, TTarget>

回到 Autofac, 这就是RegisterGeneric的用途——处理开放的通用实现 如果你有这种事情,你会像这样注册:

builder.RegisterGeneric(typeof(ChartDataService<,>))
       .As(typeof(IChartDataService<,>));

在这种情况下,您实际上是在注册一个开放的泛型。

但是,这不是您所拥有的 您有实现接口的类型 - 一个非常具体的接口。 让我们看看你有什么:

public class TreantService : IChartDataService<SourceDto, TargetDto>

基于此,我们看到:

  • TreantService根本不是通用的。 class 本身没有泛型类型参数。
  • TreantService实现了一个封闭的通用接口 换句话说,您可以TreantService转换为IChartDataService<SourceDto, TargetDto>不能将其转换为IChartDataService<string, Exception>或其他任何内容。

出于所有意图和目的,在这种情况下, IChartDataService<SourceDto, TargetDto>与任何其他标准接口(如IComparable或其他东西)没有什么不同。 一旦它是像这样的封闭泛型,它就只是一个 type

所以假设你有一堆这样的,有一堆不同的 DTO 类型:

public class FirstService
  : IChartDataService<FirstSource, FirstTarget> { }
public class SecondService
  : IChartDataService<SecondSource, SecondTarget> { }
public class ThirdService
  : IChartDataService<ThirdSource, ThirdTarget> { }

当然,它们都实现IChartDataService<TSource, TTarget>但它们已关闭 generics。 您不能将它们转换为相同的底层接口。

现在假设您要将它们全部存储在List<T>中。 有什么T可以使这项工作发挥作用?

它将是object - 他们拥有的唯一共同基础 class。 您必须使用List<object> 是的,这很糟糕,但是正如您发现的那样,没有List<IChartDataService<,>>之类的东西,也没有将开放泛型作为值的字典。 如果您考虑一下,这是有道理的,因为假设您想将它们拉回来:

// Pretend this is a thing.
var list = new List<IChartDataService<,>>();
foreach(var item in  list)
{
  // What type is `item` in this loop?
  // How does the compiler know what types
  // to fill in for the open generic? It's
  // not a dynamic language, so you can't
  // "switch types" on every loop iteration.
}

希望此时您可以开始看到您正在尝试做的事情的一些问题,这不是 Autofac 的问题 - 这是接口的设计方式以及您希望如何使用它们的问题。

这就是为什么您有时会看到像System.IEnumerable这样的非泛型接口System.Generic.IEnumerable<T>这样的泛型接口。 IEnumerable用于通用的东西,不管泛型类型参数和泛型使东西强类型。

我不能真正告诉你如何解决这个问题,因为你如何处理它在很大程度上取决于你的应用程序代码以及你到底在做什么。 但是,如果是我,我会从这里开始回顾一下我所介绍的内容:

  • 忽略 Autofac 尝试设计你可以在单元测试中完全模拟出来的东西(比如有一个具有正确类型的构造函数参数,可以模拟你实际看到的东西)。 如果在没有 Autofac 的情况下你不能让它编译或使用你所有的类型,Autofac 就不会神奇地让它工作。
  • 考虑一个通用的非通用接口 有点像IEnumerableIEnumerable<T>的区别。 出于实现目的,拥有该泛型可能很好,但实际调用的常用方法不需要泛型(或者可能需要object ?)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM