.net CORE 中的范围服务 DI 和 GC - 澄清?

Scoped service DI & GC in .net CORE - Clarification?

我的 Startup.cs 文件是:

 public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddHttpContextAccessor();
            services.AddScoped<IFoo,Foo>();   // Notice Scoped service
        }

请注意 IFoo 是一个范围内的服务。

IFoo 是一项简单的服务:

 public interface IFoo
    {
        public int Get(int id)
        {
            return id;
        }
    }

    public class Foo : IFoo
    {
    }

这是简单的控制器:

public class WeatherForecastController : ControllerBase
{
    private readonly IHttpContextAccessor _accessor;


    private readonly IFoo _fooService;

    public WeatherForecastController(IFoo fooService, IHttpContextAccessor accessor)
    {
        _fooService = fooService;
        _accessor = accessor;
    }

    [HttpGet]
    public async Task<int[]> Get()
    {
        async Task Foo()
        {
            try
            {
                await Task.Delay(4000);
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                Console.WriteLine("***Service******" + _fooService.Get(4));
            }
            catch (Exception e)
            {
                Console.Write(e);
            }
        }

        Foo(); // non awaitable
        return new[] { 0 };
    }
}

请注意 Foo() 没有 await

现在,当我 运行 代码时,我确实看到了在请求完成后访问范围服务的结果(因为没有等待 Foo):

我期待看到 Null 引用异常,因为它是一个作用域服务,在主请求线程执行后很长时间才执行。

MSDN:

问题:

为什么我没有收到空引用异常?
范围服务仅按请求提供。并且请求在被任务访问之前已经完成了很长时间。

作用域服务在释放范围时也被释放,这并不意味着对作用域服务的所有引用都变为空。这意味着作用域对在作用域内创建的所有一次性对象调用 Dispose() 方法。当您在已处置的对象上调用方法时,该对象应该引发 ObjectDisposedException,您不应该得到 NullReferenceException.

尝试以下操作,您会看到 ObjectDisposedException 被抛出:

public interface IFoo
{
    public int Get(int id)
    {
        return id;
    }
}

public class Foo: IFoo, IDisposable
{

    public bool IsDisposed { get; private set; }
    public void Dispose()
    {
        if (!IsDisposed)
        {
            IsDisposed = true;
        }
    }

    private void CheckDisposed()
    {
        if (IsDisposed) throw new ObjectDisposedException(nameof(Foo));
    }

    public int Get(int id)
    {
        CheckDisposed();
        return id;
    }
    
}