dotnet / extensions

This repository contains a suite of libraries that provide facilities commonly needed when creating production-ready applications.
MIT License
2.62k stars 751 forks source link

Question about scopes and providers #3127

Closed sungam3r closed 4 years ago

sungam3r commented 4 years ago

I have been working with DI for quite some time but still don’t understand some things.

Given that example:

namespace test
{
    [TestFixture]
    internal class ScopeTest
    {
        private IServiceProvider _provider;
        private IServiceProvider _temp;

        interface IAaa
        {

        }

        class AAA : IAaa
        {

        }

        interface Ibbb
        {

        }

        class BBB : Ibbb
        {

        }

        interface Iccc
        {

        }

        class CCC : Iccc
        {

        }

        [OneTimeSetUp]
        public void Setup()
        {
            var services = new ServiceCollection()
                .AddScoped<AAA>()
                .AddSingleton<BBB>()
                .AddTransient<CCC>()
                .AddScoped<IAaa>(prov =>
                {
                    if (ReferenceEquals(prov, _provider))
                        Console.WriteLine("rootA");
                    else if (ReferenceEquals(prov, _temp))
                        Console.WriteLine("scopeA");
                    else
                        Console.WriteLine("unknownA");
                    return prov.GetRequiredService<AAA>();
                })
                .AddSingleton<Ibbb>(prov =>
                {
                    if (ReferenceEquals(prov, _provider))
                        Console.WriteLine("rootB");
                    else if (ReferenceEquals(prov, _temp))
                        Console.WriteLine("scopeB");
                    else
                        Console.WriteLine("unknownB");
                    return prov.GetRequiredService<BBB>();
                })
                .AddTransient<Iccc>(prov =>
                {
                    if (ReferenceEquals(prov, _provider))
                        Console.WriteLine("rootC");
                    else if (ReferenceEquals(prov, _temp))
                        Console.WriteLine("scopeC");
                    else
                        Console.WriteLine("unknownC");
                    return prov.GetRequiredService<CCC>();
                });
            _provider = services.BuildServiceProvider(validateScopes: true);
        }

        [Test]
        public void Hhh()
        {
            using (var fff = _provider.CreateScope())
            {
                _temp = fff.ServiceProvider;
                var ttt1 = fff.ServiceProvider.GetRequiredService<IAaa>();
                var ttt2 = fff.ServiceProvider.GetRequiredService<Ibbb>();
                var ttt3 = fff.ServiceProvider.GetRequiredService<Iccc>();
            }
        }
    }
}

I get this result:

scopeA
unknownB      <-- ???
scopeC

What is the reason for this behavior?

javiercn commented 4 years ago

@sungam3r thanks for contacting us.

@anurse is platform the right area for these types of things?

davidfowl commented 4 years ago

@javiercn it belongs in extensions and eventually runtime.

What is the reason for this behavior?

It's an internal implementation detail of the service provider. The top level service provider instance isn't the same instance given to you when you resolve singleton services but it is backed by the same data.

We hand the call back an ServiceProviderEngineScope vs the top level ServiceProvider instance.

sungam3r commented 4 years ago

but it is backed by the same data

Do you mean that both instances share the same set of resolved services?

davidfowl commented 4 years ago

Yes

analogrelay commented 4 years ago

Transferred to dotnet/extensions.

analogrelay commented 4 years ago

Sounds like the question has been answered. Closing.