This information relates to a pre-release product that may be substantially modified before it's commercially released. Microsoft makes no warranties, express or implied, with respect to the information provided here.
Provides methods for sending HTTP requests and receiving HTTP responses from a resource identified by a URI.
Client-side, an instance of HttpClient is registered by the app in the Program file and uses the browser for handling the HTTP traffic in the background.
Server-side, an HttpClient isn't configured as a service by default. In server-side code, provide an HttpClient.
Additional services registered by the Blazor framework are described in the documentation where they're used to describe Blazor features, such as configuration and logging.
A custom service provider doesn't automatically provide the default services listed in the table. If you use a custom service provider and require any of the services shown in the table, add the required services to the new service provider.
Add client-side services
Configure services for the app's service collection in the Program file. In the following example, the ExampleDependency implementation is registered for IExampleDependency:
var builder = WebAssemblyHostBuilder.CreateDefault(args);
...
builder.Services.AddSingleton<IExampleDependency, ExampleDependency>();
...
await builder.Build().RunAsync();
After the host is built, services are available from the root DI scope before any components are rendered. This can be useful for running initialization logic before rendering content:
var builder = WebAssemblyHostBuilder.CreateDefault(args);
...
builder.Services.AddSingleton<WeatherService>();
...
var host = builder.Build();
var weatherService = host.Services.GetRequiredService<WeatherService>();
await weatherService.InitializeWeatherAsync();
await host.RunAsync();
The host provides a central configuration instance for the app. Building on the preceding example, the weather service's URL is passed from a default configuration source (for example, appsettings.json) to InitializeWeatherAsync:
var builder = WebAssemblyHostBuilder.CreateDefault(args);
...
builder.Services.AddSingleton<WeatherService>();
...
var host = builder.Build();
var weatherService = host.Services.GetRequiredService<WeatherService>();
await weatherService.InitializeWeatherAsync(
host.Configuration["WeatherServiceUrl"]);
await host.RunAsync();
Add server-side services
After creating a new app, examine part of the Program file:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
The builder variable represents a WebApplicationBuilder with an IServiceCollection, which is a list of service descriptor objects. Services are added by providing service descriptors to the service collection. The following example demonstrates the concept with the IDataAccess interface and its concrete implementation DataAccess:
After creating a new app, examine the Startup.ConfigureServices method in Startup.cs:
using Microsoft.Extensions.DependencyInjection;
...
public void ConfigureServices(IServiceCollection services)
{
...
}
The ConfigureServices method is passed an IServiceCollection, which is a list of service descriptor objects. Services are added in the ConfigureServices method by providing service descriptors to the service collection. The following example demonstrates the concept with the IDataAccess interface and its concrete implementation DataAccess:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IDataAccess, DataAccess>();
}
Register common services
If one or more common services are required client- and server-side, you can place the common service registrations in a method client-side and call the method to register the services in both projects.
First, factor common service registrations into a separate method. For example, create a ConfigureCommonServices method client-side:
public static void ConfigureCommonServices(IServiceCollection services)
{
services.Add...;
}
For the client-side Program file, call ConfigureCommonServices to register the common services:
var builder = WebAssemblyHostBuilder.CreateDefault(args);
...
ConfigureCommonServices(builder.Services);
In the server-side Program file, call ConfigureCommonServices to register the common services:
var builder = WebApplication.CreateBuilder(args);
...
Client.Program.ConfigureCommonServices(builder.Services);
Client-side services that fail during prerendering
This section only applies to WebAssembly components in Blazor Web Apps.
Blazor Web Apps normally prerender client-side WebAssembly components. If an app is run with a required service only registered in the .Client project, executing the app results in a runtime error similar to the following when a component attempts to use the required service during prerendering:
InvalidOperationException: Cannot provide a value for {PROPERTY} on type '{ASSEMBLY}}.Client.Pages.{COMPONENT NAME}'. There is no registered service of type '{SERVICE}'.
Use either of the following approaches to resolve this problem:
Register the service in the main project to make it available during component prerendering.
If prerendering isn't required for the component, disable prerendering by following the guidance in ASP.NET Core Blazor render modes. If you adopt this approach, you don't need to register the service in the main project.
Client-side doesn't currently have a concept of DI scopes. Scoped-registered services behave like Singleton services.
Server-side development supports the Scoped lifetime across HTTP requests but not across SignalR connection/circuit messages among components that are loaded on the client. The Razor Pages or MVC portion of the app treats scoped services normally and recreates the services on each HTTP request when navigating among pages or views or from a page or view to a component. Scoped services aren't reconstructed when navigating among components on the client, where the communication to the server takes place over the SignalR connection of the user's circuit, not via HTTP requests. In the following component scenarios on the client, scoped services are reconstructed because a new circuit is created for the user:
The user closes the browser's window. The user opens a new window and navigates back to the app.
The user closes a tab of the app in a browser window. The user opens a new tab and navigates back to the app.
The user selects the browser's reload/refresh button.
After services are added to the service collection, inject one or more services into components with constructor injection. The following example injects the NavigationManager service.
ConstructorInjection.razor:
@page "/constructor-injection"
<button @onclick="HandleClick">
Take me to the Counter component
</button>
ConstructorInjection.razor.cs:
using Microsoft.AspNetCore.Components;
public partial class ConstructorInjection(NavigationManager navigation)
{
private void HandleClick()
{
navigation.NavigateTo("/counter");
}
}
Property injection
After services are added to the service collection, inject one or more services into components with the @inject Razor directive, which has two parameters:
Type: The type of the service to inject.
Property: The name of the property receiving the injected app service. The property doesn't require manual creation. The compiler creates the property.
Use multiple @inject statements to inject different services.
The following example demonstrates shows how to use the @inject directive. The service implementing Services.NavigationManager is injected into the component's property Navigation. Note how the code is only using the NavigationManager abstraction.
PropertyInjection.razor:
@page "/property-injection"
@inject NavigationManager Navigation
<button @onclick="@(() => Navigation.NavigateTo("/counter"))">
Take me to the Counter component
</button>
Internally, the generated property (Navigation) uses the [Inject] attribute. Typically, this attribute isn't used directly. If a base class is required for components and injected properties are also required for the base class, manually add the [Inject] attribute:
using Microsoft.AspNetCore.Components;
public class ComponentBase : IComponent
{
[Inject]
protected NavigationManager Navigation { get; set; } = default!;
...
}
In components derived from a base class, the @inject directive isn't required. The InjectAttribute of the base class is sufficient. The component only requires the @inherits directive. In the following example, any injected services of CustomComponentBase are available to the Demo component:
@page "/demo"
@inherits CustomComponentBase
Use DI in services
Complex services might require additional services. In the following example, DataAccess requires the HttpClient default service. @inject (or the [Inject] attribute) isn't available for use in services. Constructor injection must be used instead. Required services are added by adding parameters to the service's constructor. When DI creates the service, it recognizes the services it requires in the constructor and provides them accordingly. In the following example, the constructor receives an HttpClient via DI. HttpClient is a default service.
using System.Net.Http;
public class DataAccess : IDataAccess
{
public DataAccess(HttpClient http)
{
...
}
...
}
Constructor injection is supported with primary constructors in C# 12 (.NET 8) or later:
using System.Net.Http;
public class DataAccess(HttpClient http) : IDataAccess
{
...
}
Prerequisites for constructor injection:
One constructor must exist whose arguments can all be fulfilled by DI. Additional parameters not covered by DI are allowed if they specify default values.
The applicable constructor must be public.
One applicable constructor must exist. In case of an ambiguity, DI throws an exception.
Inject keyed services into components
Blazor supports injecting keyed services using the [Inject] attribute. Keys allow for scoping of registration and consumption of services when using dependency injection. Use the InjectAttribute.Key property to specify the key for the service to inject:
[Inject(Key = "my-service")]
public IMyService MyService { get; set; }
Utility base component classes to manage a DI scope
In non-Blazor ASP.NET Core apps, scoped and transient services are typically scoped to the current request. After the request completes, scoped and transient services are disposed by the DI system.
In interactive server-side Blazor apps, the DI scope lasts for the duration of the circuit (the SignalR connection between the client and server), which can result in scoped and disposable transient services living much longer than the lifetime of a single component. Therefore, don't directly inject a scoped service into a component if you intend the service lifetime to match the lifetime of the component. Transient services injected into a component that don't implement IDisposable are garbage collected when the component is disposed. However, injected transient services that implement IDisposable are maintained by the DI container for the lifetime of the circuit, which prevents service garbage collection when the component is disposed and results in a memory leak. An alternative approach for scoped services based on the OwningComponentBase type is described later in this section, and disposable transient services shouldn't be used at all. For more information, see Design for solving transient disposables on Blazor Server (dotnet/aspnetcore #26676).
Even in client-side Blazor apps that don't operate over a circuit, services registered with a scoped lifetime are treated as singletons, so they live longer than scoped services in typical ASP.NET Core apps. Client-side disposable transient services also live longer than the components where they're injected because the DI container, which holds references to disposable services, persists for the lifetime of the app, preventing garbage collection on the services. Although long-lived disposable transient services are of greater concern on the server, they should be avoided as client service registrations as well. Use of the OwningComponentBase type is also recommended for client-side scoped services to control service lifetime, and disposable transient services shouldn't be used at all.
An approach that limits a service lifetime is use of the OwningComponentBase type. OwningComponentBase is an abstract type derived from ComponentBase that creates a DI scope corresponding to the lifetime of the component. Using this scope, a component can inject services with a scoped lifetime and have them live as long as the component. When the component is destroyed, services from the component's scoped service provider are disposed as well. This can be useful for services reused within a component but not shared across components.
Two versions of OwningComponentBase type are available and described in the next two sections:
DI services injected into the component using @inject or the [Inject] attribute aren't created in the component's scope. To use the component's scope, services must be resolved using ScopedServices with either GetRequiredService or GetService. Any services resolved using the ScopedServices provider have their dependencies provided in the component's scope.
The following example demonstrates the difference between injecting a scoped service directly and resolving a service using ScopedServices on the server. The following interface and implementation for a time travel class include a DT property to hold a DateTime value. The implementation calls DateTime.Now to set DT when the TimeTravel class is instantiated.
ITimeTravel.cs:
public interface ITimeTravel
{
public DateTime DT { get; set; }
}
TimeTravel.cs:
public class TimeTravel : ITimeTravel
{
public DateTime DT { get; set; } = DateTime.Now;
}
The service is registered as scoped in the server-side Program file. Server-side, scoped services have a lifetime equal to the duration of the circuit.
Initially navigating to the TimeTravel component, the time travel service is instantiated twice when the component loads, and TimeTravel1 and TimeTravel2 have the same initial value:
TimeTravel1 is tied to the user's circuit, which remains intact and isn't disposed until the underlying circuit is deconstructed. For example, the service is disposed if the circuit is disconnected for the disconnected circuit retention period.
In spite of the scoped service registration in the Program file and the longevity of the user's circuit, TimeTravel2 receives a new ITimeTravel service instance each time the component is initialized.
OwningComponentBase<TService>
OwningComponentBase<TService> derives from OwningComponentBase and adds a Service property that returns an instance of T from the scoped DI provider. This type is a convenient way to access scoped services without using an instance of IServiceProvider when there's one primary service the app requires from the DI container using the component's scope. The ScopedServices property is available, so the app can get services of other types, if necessary.
@page "/users"
@attribute [Authorize]
@inherits OwningComponentBase<AppDbContext>
<h1>Users (@Service.Users.Count())</h1>
<ul>
@foreach (var user in Service.Users)
{
<li>@user.UserName</li>
}
</ul>
Detect client-side transient disposables
Custom code can be added to a client-side Blazor app to detect disposable transient services in an app that should use OwningComponentBase. This approach is useful if you're concerned that code added to the app in the future consumes one or more transient disposable services, including services added by libraries. Demonstration code is available in the Blazor samples GitHub repository (how to download).
Inspect the following in .NET 6 or later versions of the BlazorSample_WebAssembly sample:
DetectIncorrectUsagesOfTransientDisposables.cs
Services/TransientDisposableService.cs
In Program.cs:
The app's Services namespace is provided at the top of the file (using BlazorSample.Services;).
The TransientDisposableService is registered (builder.Services.AddTransient<TransientDisposableService>();).
EnableTransientDisposableDetection is called on the built host in the processing pipeline of the app (host.EnableTransientDisposableDetection();).
The app registers the TransientDisposableService service without throwing an exception. However, attempting to resolve the service in TransientService.razor throws an InvalidOperationException when the framework attempts to construct an instance of TransientDisposableService.
Detect server-side transient disposables
Custom code can be added to a server-side Blazor app to detect server-side disposable transient services in an app that should use OwningComponentBase. This approach is useful if you're concerned that code added to the app in the future consumes one or more transient disposable services, including services added by libraries. Demonstration code is available in the Blazor samples GitHub repository (how to download).
Inspect the following in .NET 8 or later versions of the BlazorSample_BlazorWebApp sample:
Inspect the following in .NET 6 or .NET 7 versions of the BlazorSample_Server sample:
The app's Services namespace is provided at the top of the file (using BlazorSample.Services;).
DetectIncorrectUsageOfTransients is called on the host builder (builder.DetectIncorrectUsageOfTransients();).
The TransientDependency service is registered (builder.Services.AddTransient<TransientDependency>();).
The TransitiveTransientDisposableDependency is registered for ITransitiveTransientDisposableDependency (builder.Services.AddTransient<ITransitiveTransientDisposableDependency, TransitiveTransientDisposableDependency>();).
The app registers the TransientDependency service without throwing an exception. However, attempting to resolve the service in TransientService.razor throws an InvalidOperationException when the framework attempts to construct an instance of TransientDependency.
Transient service registrations for IHttpClientFactory/HttpClient handlers
Access server-side Blazor services from a different DI scope
Circuit activity handlers provide an approach for accessing scoped Blazor services from other non-Blazor dependency injection (DI) scopes, such as scopes created using IHttpClientFactory.
Prior to the release of ASP.NET Core in .NET 8, accessing circuit-scoped services from other dependency injection scopes required using a custom base component type. With circuit activity handlers, a custom base component type isn't required, as the following example demonstrates:
public class CircuitServicesAccessor
{
static readonly AsyncLocal<IServiceProvider> blazorServices = new();
public IServiceProvider? Services
{
get => blazorServices.Value;
set => blazorServices.Value = value;
}
}
public class ServicesAccessorCircuitHandler(
IServiceProvider services, CircuitServicesAccessor servicesAccessor)
: CircuitHandler
{
public override Func<CircuitInboundActivityContext, Task> CreateInboundActivityHandler(
Func<CircuitInboundActivityContext, Task> next) =>
async context =>
{
servicesAccessor.Services = services;
await next(context);
servicesAccessor.Services = null;
};
}
public static class CircuitServicesServiceCollectionExtensions
{
public static IServiceCollection AddCircuitServicesAccessor(
this IServiceCollection services)
{
services.AddScoped<CircuitServicesAccessor>();
services.AddScoped<CircuitHandler, ServicesAccessorCircuitHandler>();
return services;
}
}
Access the circuit-scoped services by injecting the CircuitServicesAccessor where it's needed.
Create a class BlazorServiceAccessor that defines an AsyncLocal, which stores the Blazor IServiceProvider for the current asynchronous context. A BlazorServiceAccessor instance can be acquired from within a different DI service scope to access Blazor services.
BlazorServiceAccessor.cs:
internal sealed class BlazorServiceAccessor
{
private static readonly AsyncLocal<BlazorServiceHolder> s_currentServiceHolder = new();
public IServiceProvider? Services
{
get => s_currentServiceHolder.Value?.Services;
set
{
if (s_currentServiceHolder.Value is { } holder)
{
// Clear the current IServiceProvider trapped in the AsyncLocal.
holder.Services = null;
}
if (value is not null)
{
// Use object indirection to hold the IServiceProvider in an AsyncLocal
// so it can be cleared in all ExecutionContexts when it's cleared.
s_currentServiceHolder.Value = new() { Services = value };
}
}
}
private sealed class BlazorServiceHolder
{
public IServiceProvider? Services { get; set; }
}
}
To set the value of BlazorServiceAccessor.Services automatically when an async component method is invoked, create a custom base component that re-implements the three primary asynchronous entry points into Razor component code:
Any components extending CustomComponentBase automatically have BlazorServiceAccessor.Services set to the IServiceProvider in the current Blazor DI scope.
Finally, in the Program file, add the BlazorServiceAccessor as a scoped service:
The source for this content can be found on GitHub, where you can also create and review issues and pull requests. For more information, see our contributor guide.
ASP.NET Core feedback
ASP.NET Core is an open source project. Select a link to provide feedback:
Understand and implement dependency injection in an ASP.NET Core app. Use ASP.NET Core's built-in service container to manage dependencies. Register services with the service container.