Royi Namir
Royi Namir

Reputation: 148524

Validate scope for transient inside singleton in .net core?

I have 3 interfaces ( for singleton/scoped/transient) :

    public interface ISingleton
    {
    }

    class Singlet : ISingleton
    {
    }

    public interface IScoped
    {
    }

    class Scoped : IScoped
    {
    }


    public interface Itransient
    {
    }

    class Transient : Itransient
    {
    }

I register them as :

 services.AddScoped<IScoped, Scoped>();
 services.AddTransient<Itransient, Transient>();
 services.AddSingleton<ISingleton, Singlet>();

If I try to inject scoped service to singleton , I should (and do) get an exception (I know the reason for this):

    public interface ISingleton
    {
    }

    class Singlet : ISingleton
    {

        public Singlet( IScoped sc)
        {
            
        }
    }

Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: WebApplication2.ISingleton Lifetime: Singleton ImplementationType: WebApplication2.Singlet': Cannot consume scoped service 'WebApplication2.IScoped' from singleton 'WebApplication2.ISingleton'.)

But if I try to inject transient , I do not get an exception :

public interface ISingleton
    {
    }

    class Singlet : ISingleton
    {

        public Singlet( Itransient tr)
        {
            
        }
    }

Question:

Why does .net core forbids injecting a shorter lifetime (scoped) services raising an exception, while allowing transient to be injected to a singleton?

Upvotes: 7

Views: 2174

Answers (1)

Dai
Dai

Reputation: 155015

I suggest reading this first: When are .NET Core dependency injected instances disposed?

(Preface: My answer assumes that service implementations never dispose of any injected services themselves.)

Why does .net core forbids injecting a shorter lifetime (scoped) services raising an exception, while allowing transient to be injected to a singleton?

  • The lifetime of a transient service is the same as the container it's instantiated inside.
    • Transient services can be instantiated in either the root-scope/root-container (where singletons are) or inside a scoped container.
    • The lifetime of a new transient service instance injected into a singleton is the lifetime of the singleton's container (usually the root container or root-scope).
      • That is, the transient instance will be disposed-of only when the root container is disposed, usually during application shutdown (IHost.Dispose())
    • The lifetime of a new transient service instance injected into a scoped service is the lifetime of that scope container.
      • That is, the transient instance will be disposed-of only when the scope is disposed (in ASP.NET Core, that's at the end of the HTTP request lifetime).
    • The lifetime of a new transient service injected into another transient service is the lifetime of the ultimate consumer which is constructed by the DI provider.

In short: the lifetime of a transient service is the same as the service it's injected into - not longer, not shorter. That's why it's allowed.

Whereas the lifetime of a scoped service is shorter than the root-scope/root-container's lifetime, and the root-scope/root-container is used for holding singleton instances, therefore a scoped service cannot be consumed by a singleton (more importantly it's because child scopes don't exist in the context of the root scope).

Upvotes: 11

Related Questions