Click here to Skip to main content
15,881,866 members
Articles / Web Development / ASP.NET

Dependency Injection Lifetimes in Blazor

Rate me:
Please Sign up or sign in to vote.
5.00/5 (8 votes)
18 Sep 2018CPOL2 min read 6.9K   6  
Dependency Injection Lifetimes in Blazor

I’ve been playing with Server-side Blazor for a few days and I’ve been really impressed. I can see that this will be an immensely powerful technology and I hope to make it my main web platform going forward. But its model does change some things. Since you’re running the app on the server and just using SignalR to update the browser’s UI, it changes the life time of Scoped objects in ASP.NET’s dependency injection.

For a great analysis of DI scopes and lifetimes, check out Michal Dudak’s post. I based my testing of scopes off of his simple scenario to see how the scopes differed in Server-side Blazor.

First, I created three simple services:

C#
public class SingletonService
{
    public int Counter;
}
 
public class ScopedService
{
    public int Counter;
}
 
public class TransientService
{
    public int Counter;
}

Then, I registered those services with ASP.NET in startup.cs.

C#
public void ConfigureServices(IServiceCollection services)
{
    // Since Blazor is running on the server, we can use an application service
    // to read the forecast data.
    services.AddSingleton<WeatherForecastService>();

    services.AddSingleton<SingletonService>();
    services.AddScoped<ScopedService>();
    services.AddTransient<TransientService>();
}

Then, in the Configure method of startup.cs, I requested an instance of the services and incremented the counters.

C#
public void Configure(IBlazorApplicationBuilder app)
{
    app.AddComponent<App>("app");

    app.Services.GetService<SingletonService>().Counter++;
    app.Services.GetService<ScopedService>().Counter++;
    app.Services.GetService<TransientService>().Counter++;
}

Then, I added a new page that gets those services injected, increments the counters and displays the results.

C#
@page "/dilifetimes"
@using Blazor.DI.Lifetimes.App.Services
@inject SingletonService singletonService
@inject ScopedService scopedService
@inject TransientService transientService

<table class="table">
    <thead>
        <tr>
            <td>Lifetime Type</td>
            <td>Count</td>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Singleton</td>
            <td>@singletonService.Counter</td>
        </tr>
        <tr>
            <td>Scoped</td>
            <td>@scopedService.Counter</td>
        </tr>
        <tr>
            <td>Transient</td>
            <td>@transientService.Counter</td>
        </tr>
    </tbody>
</table>

@functions
{
    protected override void OnInit()
    {
        singletonService.Counter++;
        scopedService.Counter++;
        transientService.Counter++;

        base.OnInit();
    }
}

As you would expect the values of counters are:

Singleton: 2
Scoped: 2
Transient: 1

And if you refresh, the values match what you see in a normal ASP.NET application:

Singleton: 4
Scoped: 2
Transient: 1

But if you then navigate to a different page, then back to the DILifetimes page, you get an odd result (literally):

Singleton: 5
Scoped: 3
Transient: 1

Transient works just as we’d expect. Every time we request an instance of that service, we get a new instance. The instance that is incremented in the Configure method is different from the instance we get injected in the DILifetimes page.

And when we refresh the page, both Singleton and Scoped work as we’d expect. The singleton service is the same instance that has already been incremented twice when we loaded the page. It gets incremented again in Configure and again in DILifetimes. We get a new Scoped service in the Configure method and increment it a second time in DILifetimes.

The odd thing happens. We navigate away from the page and back, and we only increment Singleton and Scoped once, from the DILifetimes page. This is because we haven’t actually done a full web request when we navigated. We actually sent a message through SignalR to tell Server-side Blazor that we navigated. Blazor then did the navigation at the server and sent back the changes to the DOM which are then updated on the screen. The Configure method is never run.

The message here is that Scoped services have a much longer lifetime in Server-side Blazor than they did in a traditional ASP.NET application. This can be good or bad, but it’s definitely good to be aware of.

The full source is available at https://github.com/hutchcodes/Blazor.DI.Lifetimes.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United States United States
I’m a Software Engineer at Microsoft working on the Azure Portal. Before that I spent about 20 years developed various business applications at a number of different companies. I have a passion for writing clean, scalable code and sharing what I’ve learned with others.

I also help run the Casco Bay .Net User Group

Comments and Discussions

 
-- There are no messages in this forum --