Autofac:使用不同的构造函数将组件注册为不同的服务

时间:2018-06-27 16:29:08

标签: c# autofac

我想为多个服务注册一个单例组件,并根据在解析调用期间使用的服务来定义要使用的构造函数。

我尝试过:

_builder.RegisterType<TComponent>()
    .As<IService1>()
    .FindConstructorsWith(ConstructorFinder1)
    .SingleInstance();

_builder.RegisterType<TComponent>()
    .As<IService2>()
    .FindConstructorsWith(ConstructorFinder2)
    .SingleInstance();

但这会导致两个不同的“单例”实例,具体取决于所使用的服务。

所以我尝试了:

_builder.RegisterType<TComponent>()
    .As<IService1>()
    .FindConstructorsWith(ConstructorFinder1)
    .As<IService2>()
    .FindConstructorsWith(ConstructorFinder2)
    .SingleInstance();

这解决了单例问题,但遗憾的是第二个FindConstructorsWith调用会覆盖第一个调用,即两个服务都ConstructorFinder2被使用。

我曾经(希望)将针对服务存储ConstructorFinder,但显然并非如此。

我要实现的概念上是错误的,Autofac是否不支持它,或者我只是缺少某些东西?

编辑: 再次感谢特拉维斯的出色回应。显然我遗漏了一些细节,使事情变得混乱。让我现在添加一些。

这个问题实际上是对How to determine which constructor Autofac uses when resolving的一种跟进(Travis也帮助了我)。因此,反序列化时会出现问题,并且会影响许多不同的对象。

我得到有关组成,关注点分离以及如何将多个ctor经常视为代码气味的争论,但是在反序列化的情况下(至少对于我正在开发的应用程序而言),能够根据实例文件是新建的还是从项目文件反序列化的,以不同的方式创建实例。在反序列化时,不必初始化构建新实例时需要初始化的几个成员(因为反序列化期间它们的值都会被覆盖)。这将意味着额外的性能成本,并且(在这种情况下)会导致其他与一次性初始化有关的问题。

花了数天时间试图找到解决方案后(Newtonsoft Json方面也遇到了麻烦),我决定停止Autofac并实现我们自己的IOC容器。出于一般目的,它不能(显然!)以任何方式与Autofac竞争,但是由于我们实际上只使用了Autofac强大功能的一小部分,因此,我觉得我们可以尝试推出自己的产品。我花了很多时间才将头包裹在一个整体的黑匣子上。是的,Autofac是开源的,但是单步执行代码不会在公园里走动。

第一次测试非常有前途,重新获得对应用程序如此重要组成部分的完全控制感觉很好。

同样,离开Autofac的原因是无法(可行)根据构造单件组件的服务来定义其构造方式。从总体结构/概念的角度来看,我了解严格区分服务和构建方法是有意义的。但我相信,在反序列化期间,情况有所不同。而且,既然我已经独立于Autofac,我可能会决定更改机制,以使它们更直接地适应整个概念。

1 个答案:

答案 0 :(得分:3)

这是一个很难回答的问题,因为似乎您有一些基本目标正在试图实现,并且您有想要解决的方案,但是也许这是错误的解决方案,您应该提出一个[新]问题取决于此响应对您的效果如何。

让我逐步了解一下是否可以解释为什么很难回答。

  

我想为多个服务注册一个单例组件,并根据在解析调用期间使用的服务来定义要使用的构造函数。

如果它是一个 singleton ,则表示整个系统中有一个 ,对吗?实际上,这将是“获胜第一”。如果某事物将其解析为IService1,则将调用与之关联的构造函数,即使您尝试将其解析为IService2,也不会发生任何构造,因为创建了单例。反之亦然-IService2得到解析,并且遵循构造函数路径,然后要求IService1的事物将获得单例,并且不调用任何构造函数。

这引起了人们的关注:

  • 如果您知道肯定会首先解决的是什么,那么为什么需要两个不同的构造函数选择器?
  • 如果您不知道首先要解决的是什么,那么您是否在考虑系统的不可预测性?

我以前见过这类问题,通常它们表示的是两件事之一:

  1. 您正在尝试根据上下文进行某种选择或特殊逻辑。 There's an Autofac FAQ about this that may help.通常,解决此问题的方法是重构。我待会儿再讲。
  2. 您正在尝试在两个不同的应用程序之间“共享注册”。答案是use Autofac modules and reuse those;但是如果每种应用类型都有特殊的注册,那就让它发生。

这并不是说这些都是您要的,但这是我看到过此类问题的地方。通常,有一些潜意识中的目标是预先选择解决方案的,最好是问如何解决该目标,而不是如何实施一个非常具体的解决方案。再次,我可能是错的。

在上面第1项的重构说明中,基于对单例的需求,我可以进一步猜测是否存在某种资源,例如数据库连接需要共享或启动起来很昂贵。考虑将TComponent分为三个单独的类:

  • TCommonExpensiveComponent-这是旋转起来实际上很昂贵的东西,确实确实需要一个单例,但是在IService1IService2之间并没有区别。
  • TService1-仅使用所需的构造函数实现IService1,因此您不需要构造函数查找器。消耗TCommonExpensiveComponent
  • TService2-仅使用所需的构造函数实现IService2,因此您不需要构造函数查找器。消耗TCommonExpensiveComponent

该想法是避免注册的复杂性,保留所需的共享/单个,并且仍根据需要获得不同的构造函数用法。您可能还想抛出一些常见的基类/抽象类,如果确实存在很多常见逻辑,则TService类可以派生自该类。

  

我要实现的概念上是错误的,Autofac是否不支持它,或者我只是缺少某些东西?

从技术上讲,如果您愿意,可以在Autofac中做一些非常疯狂的事情,例如编写自定义registration source,等待某人查询IService1IService2注册,然后选择基于此的构造函数,根据需要动态提供注册服务。 但是,实际上,甚至不要走这条路。

相反,如果我的回答没有帮助,请弄清楚您要解决的问题以及解决上述挑战的计划是很好的。 在一个全新的问题中进行操作,该问题会更详细地介绍您的挑战和尝试过的事情。这不是一个论坛,而是通过对话进行尝试,以消除当前问题的实际帮助不可行另外,花点时间退后一步,也许重新构造问题听起来像在这里可能有所帮助。