验证 .net core 中单例内部瞬态的范围?

Roy*_*mir 6 c# dependency-injection .net-core asp.net-core

我有 3 个接口(用于单例/作用域/瞬态):

    public interface ISingleton
    {
    }

    class Singlet : ISingleton
    {
    }

    public interface IScoped
    {
    }

    class Scoped : IScoped
    {
    }


    public interface Itransient
    {
    }

    class Transient : Itransient
    {
    }
Run Code Online (Sandbox Code Playgroud)

我将它们注册为:

 services.AddScoped<IScoped, Scoped>();
 services.AddTransient<Itransient, Transient>();
 services.AddSingleton<ISingleton, Singlet>();
Run Code Online (Sandbox Code Playgroud)

如果我尝试将作用域服务注入 singleton ,我应该(并且确实)得到一个异常(我知道其原因):

    public interface ISingleton
    {
    }

    class Singlet : ISingleton
    {

        public Singlet( IScoped sc)
        {
            
        }
    }
Run Code Online (Sandbox Code Playgroud)

某些服务无法构建(验证服务描述符“ServiceType:WebApplication2.ISingleton Lifetime:Singleton ImplementType:WebApplication2.Singlet”时出错:无法使用单例“WebApplication2.ISingleton”中的作用域服务“WebApplication2.IScoped”。)

如果我尝试注入瞬态,我不会得到异常:

public interface ISingleton
    {
    }

    class Singlet : ISingleton
    {

        public Singlet( Itransient tr)
        {
            
        }
    }
Run Code Online (Sandbox Code Playgroud)

问题:

为什么 .net core 禁止注入较短生命周期(作用域)的服务引发异常,同时允许将瞬态注入到单例中?

Dai*_*Dai 10

我建议先阅读以下内容:When are .NET Core dependency injectionInstances Dispose?

(前言:我的回答假设服务实现永远不会自行处置任何注入的服务。)

为什么 .net core 禁止注入较短生命周期(作用域)的服务引发异常,同时允许将瞬态注入到单例中?

  • 瞬态服务的生命周期与其在其中实例化的容器相同。
    • 瞬态服务可以在根范围/根容器(单例所在的位置)或作用域容器内实例
    • 注入单例 的新瞬态服务实例的生命周期是单例容器(通常是根容器或根范围)的生命周期。
      • 也就是说,只有当根容器被释放时,瞬态实例才会被释放,通常是在应用程序关闭期间(IHost.Dispose()
    • 注入到作用域服务中的 新瞬态服务实例的生命周期就是该作用域容器的生命周期。
      • 也就是说,只有当范围被释放时(在 ASP.NET Core 中,这是在 HTTP 请求生命周期结束时),瞬态实例才会被释放。
    • 注入另一个瞬态服务的新瞬态服务的生命周期是由 DI 提供者构建的最终消费者的生命周期。

简而言之:瞬态服务的生命周期与其注入的服务相同——不长,也不短。这就是为什么它是被允许的。

鉴于作用域服务的生命周期比根作用域/根容器的生命周期短,并且根作用域/根容器用于保存单例实例,因此作用域服务不能被单例使用(更重要的是它是因为子作用域不存在于根作用域的上下文中)。