在我的 AspNetCore 应用程序中,我处理从队列到达的消息。为了处理消息,我需要解析一些服务。其中一些服务依赖于ITenantId
,我使用收到的消息中的信息进行绑定。为了解决这个问题,消息的处理从创建一个子容器开始,然后我用它来创建一个服务范围 https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.iservicescope?view=dotnet-plat-ext-3.1我从中解决了所有需要的依赖关系。
消息可以并行处理,因此作用域必须相互隔离。
我可以看到创建子容器的方法,但我不确定哪种方法在性能、内存更改等方面最好:
选项 A:每次消息到达时,将 IServiceCollection 克隆到新的 ServiceCollection 中,然后重新绑定ITenantId
在克隆实例上。
选项 B:当程序启动时,创建 IServiceCollection 的不可变副本(使用ImmutableList<ServiceDescriptor>
or ImmutableArray<ServiceDescriptor>
)。每次消息到达时,替换ITenantId
(导致一个新的实例ImmutableList<ServiceDescriptor>
)并致电CreateScope()
在新的不可变实例上。
我不喜欢选项 A 的一点是,每次消息到达时都需要克隆整个服务集合。我不确定选项 B 中的不可变集合是否以更智能的方式处理这个问题?
这两个选项都会导致为每个传入消息创建一个新的容器实例。尽管这允许每条消息在完全隔离的气泡中运行,但这对应用程序的性能和内存使用有严重影响。创建容器实例的成本很高,并且第一次(每个容器)解析注册实例会导致生成表达式树、编译委托以及 JIT 编译它们。这甚至可能导致内存泄漏。
此外,这还意味着任何注册的单例类都将具有与任何作用域类相同的生命周期。状态无法再共享。
因此,我建议选择 3:
- 只使用一个Container实例并且不调用
BuildProvider
不止一次
- 创建一个
ITenantId
允许设置的实现Id
实例化后
- 将该实现注册为
Scoped
- 在每一个新的开始
IServiceScope
,解析该实现并设置其 id。
这可能如下所示:
// Code
class TenentIdImpl : ITenantId
{
public Guid Id { get; set; } // settable
}
// Startup:
services.AddScoped<TenentIdImpl>();
services.AddScoped<ITenantId>(c => c.GetRequiredService<TenantIdImpl>());
// In message pipeline
using (var scope = provider.CreateScope())
{
var tenant = scope.ServiceProvider.GetRequiredService<TenantIdImpl>();
tenant.Id = messageEnvelope.TenantId;
var handler =
scope.ServiceProvider.GetRequiredService<IMessageHandler<TMessage>>();
handler.Handle(messageEnvelope.Message);
}
我在博客中解释了这个特定的模型,您将状态存储在对象图中,我将其称为闭包组合模型 https://blogs.cuttingedge.it/steven/posts/2019/closure-composition-model/.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)