top of page

Difference between AddScoped and AddTransient in .NET 6



To access the .NET Core DI functionality, your app must have references to Microsoft.Extensions.DependencyInjection NuGet package.

Services can be registered with one of the following lifetimes:

  • Transient - new instance is created every time it is requested from provider, method AddTransient<T>()

  • Scoped - new instance is created in a specific scope in the IServiceScopeinterface, method AddScoped<T>()

  • Singleton - new instance is created once and used during the whole process, method AddSingleton<T>()


Let’s break down Scoped option.

Instances are specified via scope will live in the scope of IServiceScope and will be destroyed via IDisposablewhen the scope itself is destroyed. A different instance will be created and live in each scope.

It works in ASP.NET similarly. One scope is created for one http request.


Code example The application feeds people

Code in Program.cs

using HumanFood.Food;
using HumanFood.Human;
using Microsoft.Extensions.DependencyInjection;

var provider = new ServiceCollection()
    .AddTransient<List<IFood>>()
    .AddScoped<Human>()
    .AddTransient<IFeed>(s => s.GetRequiredService<Human>())
    .AddTransient<IHuman>(s => s.GetRequiredService<Human>())
    .AddTransient<Cook>()
    .BuildServiceProvider();

var firstCook = provider.GetRequiredService<Cook>();
firstCook.Feed(new Orange());

firstCook.Ask();

using var humanFeedScope = provider.CreateScope();
var secondCook = humanFeedScope.ServiceProvider.GetRequiredService<Cook>();
secondCook.Feed(new Peach());
secondCook.Ask();

var thirdCook = humanFeedScope.ServiceProvider.GetRequiredService<Cook>();
thirdCook.Feed(new Watermelon());
thirdCook.Ask();

firstCook.Feed(new Orange());
firstCook.Ask();

Console Output #1. Orange { } #2. Peach { } #2. Peach { }, Watermelon { } #1. Orange { }, Orange { } Disposed:#2. Count: 2

Each human lives in their own scope. We can create a cook for a human, but only within certain scope. There are three cooks in total. firstCookfeeds the human without defined scope (in the default scope). The other two are in the same scope and feed same human of their own.

The fact that a person will implement the IFeedand IHumaninterfaces — we set through the factory method s => s.GetRequiredService()otherwise, .NET will create a new instance for each interface type.

var provider = new ServiceCollection()
    .AddTransient<List<IFood>>()
    .AddScoped<Human>()
    .AddTransient<IFeed>(s => s.GetRequiredService<Human>())
    .AddTransient<IHuman>(s => s.GetRequiredService<Human>())
    .AddTransient<Cook>()
    .BuildServiceProvider();

Next, we request firstCook from the provider. Because scope is not defined, will be used by default created.

var firstCook = provider.GetRequiredService<Cook>();
firstCook.Feed(new Orange());

firstCook.Ask();

Next, we create a new scope, and in it we get our own separate provider, which will re-create all objects added via AddScoped<T>(). So secondCook will create a new human and thirdCook will feed the same human created in that area. firstCook will continue to feed the human from his area.

using var humanFeedScope = provider.CreateScope();
var secondCook = humanFeedScope.ServiceProvider.GetRequiredService<Cook>();
secondCook.Feed(new Peach());
secondCook.Ask();

var thirdCook = humanFeedScope.ServiceProvider.GetRequiredService<Cook>();
thirdCook.Feed(new Watermelon());
thirdCook.Ask();

firstCook.Feed(new Orange());
firstCook.Ask();

Code in Human.cs

IServiceScopeinherits IDisposable. And all objects that were created in this scope are destroyed along with it. Therefore, Humaninherits IDisposable, when the scope is destroyed, the human is destroyed with it.

using HumanFood.Food;

namespace HumanFood.Human;

internal record Human(List<IFood> Foods) : IHuman, IFeed, IDisposable
{
    private bool _disposed;
    private static byte Id { get; set; }
    private string Name { get; } = ++Id + ".";
    public void Answer() => Console.WriteLine("#{0} {1}", Name, string.Join(", ", Foods));
    public void Eat(IFood food) => Foods.Add(food);
    public void Dispose()
    {
        if (_disposed) 
        { 
            return;
        }

        Console.WriteLine("Disposed:#{0} Count: {1}", Name, Foods.Count);
        Foods.Clear();
        _disposed = true;
    }
}

The example source code is available here



Source: Medium - Aleksandr Maslovskii


The Tech Platform




0 comments

Comments


bottom of page