将范围服务的实例传递到单例

问题描述 投票:0回答:1

我有一个作用域服务

ScopedA
,我通过 DI 将其注入另一个作用域服务
ScopedB

我还注入了第三个单例服务
Singleton1
:

public class ScopedB
{
    ScopedA _scopedA;
    Singleton1 _singleton1;

    public ServiceB(ScopedA scopedA, Singleton1 singleton1)
    {
       _scopedA = scopedA;
       _singleton1 = singletonA;
    }
    
    public async Task DoSomethingAsync()
    {
       await _singleton1.PerformALongerOperationWichNeedsServiceAAsync(_scopedA);
    }   
}

Singleton1
在进程的整个生命周期中仅存在一次,而
ScopedA
ScopedB
仅在 HTTP 请求(范围内)期间存在。

如果我将实例

_scopedA
传递给
_singleton1
的函数(该函数在后台处理较长时间的某些内容)会发生什么?

假设我的请求已经结束并且

DoSomethingAsync
已经完成,但是单例服务仍在使用
_scopedA
做一些事情。

ASP.NET 是否会在函数调用中销毁我的

ScopedA
作用域实例,从而导致某些操作失败?或者像这样的事情可以毫无问题地完成吗?

我知道不建议将

_scopedA
直接注入
_singleton1
,因为这会违反范围

我的例子是否也违反了范围?

如果这是相关的,我正在使用 ASP.NET 6(作为 API),并使用内置 DI 容器 (Microsoft.Extensions.DependencyInjection)。

c# asp.net asp.net-web-api dependency-injection
1个回答
1
投票

ASP.NET 是否会在函数调用中销毁我的 ServiceA 作用域实例,从而导致某些操作失败?

单例将能够使用

ServiceA
,除非该类(或其依赖项之一)实现
IDisposable
(或
IAsyncDisposable
)。

当作用域结束时,DI 容器将取消引用

ServiceA
。但由于
ServiceS
仍保留对它的引用,因此
ServiceA
不会被垃圾收集。

但是,如果

ServiceA
或其依赖项之一被处置,它们将在
ServiceS
完成其后台操作之前(或者可以)被处置。当
ObjectDisposedException
尝试访问
ServiceS
时,这可能会导致
ServiceA
被抛出。

尽管当

ServiceA
的对象图不包含任何一次性对象时,这一切都可能起作用,但仍然有很多问题,例如:

  1. 虽然
    ServiceA
    可能无法实现
    IDisposable
    ,但通常很难跟踪这是否适用于其所有依赖项。因此,虽然
    ServiceS
    今天可能能够成功调用
    ServiceA
    ,但稍后将
    IDisposable
    添加到系统中的另一个类可能会导致应用程序崩溃。
  2. ServiceA
    已在范围内注册这一事实表明它不是线程安全的。将其传递给单例
    ServiceS
    ,后者在不同的线程上访问它(与主操作并行)可能不是一个好主意。
  3. 这种设计会让其他开发人员感到困惑,因为众所周知,作用域实例不是线程安全的。当我作为开发人员偶然发现这样的代码时,我可能会停止我正在做的事情,并开始调查这一点。我将开始与其他开发人员交谈。这意味着,即使您发现这实际上是一个正确的工作解决方案,其他开发人员也会在这段代码上花费(浪费)时间。

不要将作用域依赖项传递给单例,以在其作用域结束后使依赖项保持活动状态,而是让单例从其自己的作用域(在后台操作期间存在)解析其自己的作用域依赖项。

© www.soinside.com 2019 - 2024. All rights reserved.