gpt4 book ai didi

c# - 为什么Autofac的用于动态实例化的隐式关系类型(Func )遵守生存期范围?

转载 作者:行者123 更新时间:2023-11-30 22:52:15 25 4
gpt4 key购买 nike

为什么dynamic instantiation的Autofac隐式关系类型会考虑生存期范围?



关于这个主题的文档指出:


  使用此关系类型会尊重生命周期范围。如果将对象注册为InstancePerDependency()并多次调用Func<B>,则每次都会获得一个新实例。但是,如果将一个对象注册为SingleInstance()并调用Func<B>多次解析该对象,则每次都将获得相同的对象实例。


文档还声称此隐式关系类型的目的是出于以下两个目的:


在运行时解析实例,而不依赖于Autofac本身
解决给定服务的多个实例


Delayed instantiationLazy<B>)以我理解的方式实现了目的1,并且我同意其设计要尊重生命周期的范围。我不理解或不同意Autofac针对Func<B>的设计,因为无法使用动态实例化隐式关系类型来实现目标2。

要解决此问题并实际完成#2,我们使用与注册Func<B>和闭包相同的工厂方法手动注册B

void RegisterDependencies(ContainerBuilder builder)
{
builder.Register<B>(bFactory).InstancePerLifetimeScope();
builder.Register<Func<B>>(context => () => bFactory(context)).SingleInstance();

B bFactory(IComponentContext context) => new B(context.Resolve<A>(), ...);
}


这可以实现动态实例化声称要实现的两个目的,但要求我们对许多注册非常详细。



有人可以阐明为什么Autofac为动态实例化设计其隐式关系类型以遵守生存期作用域吗?

先感谢您 :)

最佳答案

知道Func<B>基本上是包装在非Autofac特定对象中的myCurrentLifetimeScope.Resolve<B>的“捷径”,它可能有助于扭转这个问题,并询问为什么Func<B>不尊重生命周期范围,以及其后果是什么如果没有的话。

利用生命周期作用域的绝大多数应用程序都是用来隔离工作单元的。例如,具有每个请求生存期范围的Web应用程序。有很多很好的理由说明为什么这是一个有趣的模式,例如只能创建给定请求所需的资源,然后在请求完成后清理它们-仅使用所需的内存。

鉴于此,通常来说,生存期范围很有趣,即使对于跟踪和清理分配的资源而言,这只是一种不错的方式。

现在,假设您已连接到某个Web服务或诸如此类的东西。也许这是WCF服务。关于WCF服务客户端,您可能会记住一些事情:


一旦通道出现故障,客户就一文不值。您不能只创建一个客户端,因为如果该服务使您的频道出现故障,则必须丢弃该客户端实例并创建一个新的客户端实例。
完成后,您需要处置客户端。如果您不这样做,他们可以保持渠道开放并消耗资源。


鉴于此,Func<IServiceClient>的各种关系变得非常有趣。

假设您有一个MVC控制器。这是一种伪代码,我不会通过编译器运行它。

public class MyController : Controller
{
private readonly Func<IServiceClient> clientFactory;
public MyController(Func<IServiceClient> clientFactory)
{
this._clientFactory = clientFactory;
}

public string GetSomethingReallyCool()
{
var retryCount = 0;
while(retryCount < 5)
{
var client = this._clientFactory();
try
{
return client.GetSomethingCool();
}
catch
{
retryCount++;
}
}

return "We failed to get the cool data.";
}
}


我们在这里建立了一个穷人重试机制,因为该服务不稳定。我不建议您使用确切的代码,因为它可以快速轻松地演示该概念。

舞台准备好了!现在,为便于讨论,让我们假装这种关系只是暂时片刻不尊重生命周期范围。

Web请求期间会发生什么?


您的MVC控制器可以从每个请求的生存期范围中解决。
控制器采用 Func<IServiceClient>函数动态创建WCF服务客户端,以在需要时使用。
控制器操作将创建服务客户端并打开一个通道。该服务客户端位于根容器中,这意味着直到您的整个应用程序关闭,它才会被丢弃。
服务故障。通道有故障,没有关闭。它只是有点闲逛...但是您不能使用相同的服务客户端实例。那个人的烤面包。
控制器操作将创建另一个服务客户端并打开一个通道。该服务客户端也位于根容器中,并将被永久分配。
服务调用成功并返回值。但是当客户超出范围时,它们是一次性的-容器正在处理废物,对吗?因此,当地人不在范围内,但仍被分配。


这是一个非常可怕的内存泄漏。

重新打开正常行为!我们假装完成了!

如果它遵循生存期范围,则其工作方式如下:


您的MVC控制器可以从每个请求的生存期范围中解决。
控制器采用 Func<IServiceClient>函数动态创建WCF服务客户端,以在需要时使用。
控制器操作将创建服务客户端并打开一个通道。该服务客户端位于请求生存期范围内,这意味着将在请求结束后将其处置。
服务故障。通道有故障,没有关闭。它只是有点闲逛...但是您不能使用相同的服务客户端实例。那个人的烤面包。
控制器操作将创建另一个服务客户端并打开一个通道。该服务客户端也位于请求范围内,并将在请求结束时处理。
服务调用成功并返回值。
请求生存期范围结束并处置客户端-关闭通道,分配资源。


没有内存泄漏,清理很好。

另一种情况-假设您在创建生存期范围时进行注册。

var builder = new ContainerBuilder();
builder.RegisterType<Alpha>();
var container = builder.Build();
using(var scope = container.BeginLifetimeScope(b => b.RegisterType<Beta>())
{
var f = scope.Resolve<Func<Beta>>();

// f is a Func<Beta> - call it, and you should get a B.
// If Func<Beta> doesn't respect lifetime scopes, what
// does this yield?
var beta = f();
}


实际上,这是WebAPI集成之类的常见模式-出现请求消息,并且在创建请求生存期范围时,请求消息会动态注册到请求范围中。它在全球范围内不存在。

您可能从其中的某些中可以看出, Func<B>行为的唯一逻辑方法是服从生命周期范围。如果没有,那就行不通了。

关于c# - 为什么Autofac的用于动态实例化的隐式关系类型(Func <B>)遵守生存期范围?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58330487/

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