gpt4 book ai didi

c# - 构造函数中的Func 参数是否会减慢我的IoC解析速度?

转载 作者:行者123 更新时间:2023-12-04 17:09:25 25 4
gpt4 key购买 nike

我正在尝试提高IoC容器的性能。我们正在使用Unity和SimpleInjector,并且有一个带有此构造函数的类:

public AuditFacade(
IIocContainer container,
Func<IAuditManager> auditManagerFactory,
Func<ValidatorFactory> validatorCreatorFactory,
IUserContext userContext,
Func<ITenantManager> tenantManagerFactory,
Func<IMonitoringComponent> monitoringComponentFactory)
: base(container, auditManagerFactory, GlobalContext.CurrentTenant,
validatorCreatorFactory, userContext, tenantManagerFactory)
{
_monitoringComponent = new Lazy<IMonitoringComponent>(monitoringComponentFactory);
}


我也有这个构造函数的另一个类:

public AuditTenantComponent(Func<IAuditTenantRepository> auditTenantRepository)
{
_auditTenantRepository = new Lazy<IAuditTenantRepository>(auditTenantRepository);
}


我看到大多数情况下,第二个问题在1毫秒内得到解决,而第一个问题平均需要50-60毫秒。我敢肯定,速度较慢的原因是由于参数,它具有更多的参数。但是,如何改善这种速度较慢的服务器的性能呢?我们使用 Func<T>作为参数是事实吗?如果它导致速度缓慢,该怎么办?

最佳答案

您当前的设计可能还有很多改进之处。这些改进可以分为五个不同的类别,即:


可能滥用基类
使用服务定位器反模式
使用环境上下文反模式
泄漏的抽象
在注入构造器中做得太多


可能滥用基类

普遍的共识是您应该选择composition over inheritance。与使用组合相比,继承经常被过度使用,并且通常会增加更多的复杂性。通过继承,派生类与基类实现紧密耦合。我经常看到一个基类被用作实用工具类,其中包含各种辅助方法,这些方法可以解决某些派生类可能需要的横切关注点和其他行为。

通常更好的方法是一起删除所有基类,然后将服务注入到只暴露服务所需功能的实现中(在您的情况下为AuditFacade类)。或者,如果涉及到横切关注点,则根本不要注入该行为,而应使用decorator包装实现,以扩展具有横切关注点的类的行为。

在您的情况下,我认为复杂性显然正在发生,因为实现中未使用7个注入的依赖中的6个,而是仅将其传递给基类。换句话说,这6个依赖项是基类的实现细节,而实现仍然被迫了解它们。通过在服务之后抽象该基类(的一部分),可以将AuditFacade需要的依赖关系数量减少到两个依赖关系:Func<IMonitoringComponent>和新抽象。该抽象背后的实现将有6个构造函数依赖项,但是AuditFacade(和其他实现)对此没有注意。

使用服务定位器反模式

AuditFacade依赖于IIocContainer抽象,这非常类似于Service Locator pattern的实现。 Service Locator should be considered an anti-pattern因为:


它隐藏了一个类的依赖关系,从而导致运行时错误而不是
编译时错误,以及使代码更难处理
维护,因为不清楚何时会引入
突破性的变化。


总有更好的选择,可以将容器或容器上的抽象注入到应用程序代码中。请注意,有时您可能希望将容器注入工厂实现中,但是只要将它们放置在Composition Root内,就不会有任何危害,因为Service Locator is about roles, not mechanics

使用环境上下文反模式

静态GlobalContext.CurrentTenant属性是Ambient Context反模式的实现。 Mark Seemann,我在our book中写到这种模式:


“环境上下文”的问题与“服务”的问题有关
定位器。主要问题是:


DEPENDENCY隐藏。
测试变得更加困难。
根据其上下文更改DEPENDENCY变得非常困难。 [第5.3.3段]



这种情况下的使用确实是很奇怪的IMO,因为您是从构造函数内部的某个静态属性中获取当前租户的,并将其传递给基类。为什么基类本身不调用该属性?

但是没有人应该称其为静态属性。这些静态属性的使用使您的代码难以阅读和维护。它使单元测试更加困难,并且由于您的代码库通常会被此类静态调用所困扰,因此它成为隐藏的依赖项;它具有与使用Service Locator相同的缺点。

泄漏的抽象

Leaky AbstractionDependency Inversion Principle违反,其中抽象违反了原则的第二部分,即:


B.抽象不应依赖细节。细节应取决于
抽象。


尽管Lazy<T>本身不是抽象(Lazy<T>是具体类型),但是当用作构造函数参数时,它可能会成为泄漏抽象。例如,如果您直接注入Lazy<IMonitoringComponent>而不是IMonitoringComponent(这基本上是您在代码中所做的事情),则新的Lazy<IMonitoringComponent>依赖项会泄漏实现细节。此Lazy<IMonitoringComponent>向消费者传达所使用的IMonitoringComponent实现创建起来昂贵或耗时。但是消费者为什么要关心这个呢?

但是与此有关的还有更多问题。如果在某个时间点使用的IUserContext实现的创建成本很高,则必须开始在整个应用程序中进行全面更改(违反Open/Closed Principle),因为所有IUserContext依赖项都需要更改为Lazy<IUserContext>并且必须将该IUserContext的所有使用者更改为使用userContext.Value.。而且,您还必须更改所有单元测试。如果您忘记将一个IUserContext引用更改为Lazy<IUserContext>,或者在创建新类时不小心依赖了IUserContext,会发生什么情况?您的代码中有一个错误,因为这时立即创建了用户上下文实现,这会导致性能问题(这会引起问题,因为这就是您首先使用Lazy<T>的原因)。

那么,为什么我们要对代码库进行彻底的更改,并用额外的间接层污染它呢?没有任何理由。创建依赖项成本很高的事实是实现细节。您应该将其隐藏在抽象后面。这是一个例子:

public class LazyMonitoringComponentProxy : IMonitoringComponent {
private Lazy<IMonitoringComponent> component;

public LazyMonitoringComponentProxy(Lazy<IMonitoringComponent> component) {
this.component = component;
}

void IMonitoringComponent.MonitoringMethod(string someVar) {
this.component.Value.MonitoringMethod(someVar);
}
}


在此示例中,我们将 Lazy<IMonitoringComponent>隐藏在 proxy class后面。这使我们可以用此 IMonitoringComponent替换原始的 LazyMonitoringComponentProxy实现,而不必对其余应用程序进行任何更改。使用Simple Injector,我们可以按以下方式注册此类型:

container.Register<IMonitoringComponent>(() => new LazyMonitoringComponentProxy(
new Lazy<IMonitoringComponent>(container.GetInstance<CostlyMonitoringComp>));


正如 Lazy<T>可能被用作泄漏抽象一样, Func<T>也是如此,尤其是出于性能原因而这样做时。正确应用DI时,大多数时候不需要将工厂抽象注入代码中,例如 Func<T>

请注意,如果要在各处注入 Lazy<T>Func<T>,则会使代码库变得不必要。

在注入构造器中做得太多

但是,除了 Lazy<T>Func<T>是泄漏抽象之外,您实际上非常需要它们的事实表明您的应用程序存在问题,因为 Injection Constructors should be simple。如果构造函数需要很长时间才能运行,则您的构造函数会做太多事情。构造器逻辑通常很难测试,并且如果这样的构造器调用数据库或从HttpContext请求数据,则 verification of your object graphs变得更加困难,以至于您可能一起跳过验证。跳过对对象图的验证是一件很糟糕的事情,因为这迫使您单击整个应用程序以了解您的DI容器是否配置正确。

我希望这能给您一些有关改进班级设计的想法。

关于c# - 构造函数中的Func <T>参数是否会减慢我的IoC解析速度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26125371/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com