Course contentsShow
← C#
Lesson 1428 of 1,90812. Modern Frontend with Razor and BlazorFree lesson

Advanced Dependency Injection Scoping

Manage object lifetimes in complex component trees to prevent memory leaks.

In this lesson, you will learn how to prevent memory leaks in complex Blazor component trees by strategically controlling the lifetime of your injected services.

Understanding Component Scoping

In Blazor, the standard Dependency Injection (DI) container manages service lifetimes as Transient, Scoped, or Singleton. While a Scoped service is typically shared across a user's circuit, problems arise in deep component trees where child components hold onto references longer than intended. If a long-lived service captures a component's state, that component becomes ineligible for Garbage Collection, leading to "Lapsed Listener" memory leaks.

Managing Lifetimes with IDisposable

The most critical tool for managing object lifetime in Blazor is the IDisposable (or IAsyncDisposable) interface. When you inject a service into a component, you must ensure that if that service subscribes to events or maintains a cache, the component explicitly cleans up its resources.

Consider a dashboard that injects a DataService that tracks active subscribers:

@implements IDisposable
@inject IDataService DataService

@code {
    protected override void OnInitialized()
    {
        DataService.Subscribe(this);
    }

    public void Dispose()
    {
        // Without this, DataService holds a reference to this 
        // component forever, preventing memory reclamation.
        DataService.Unsubscribe(this);
    }
}

The Factory Pattern for Isolation

When you have a highly complex tree where specific branches need their own isolated state, don't rely on the global DI container's scope. Instead, use a ServiceFactory. By injecting a factory rather than the service itself, you can manually instantiate and dispose of specific service instances within a component's lifecycle:

@inject IServiceFactory<MyTransientService> Factory

@code {
    private MyTransientService _myService;

    protected override void OnInitialized() => _myService = Factory.Create();

    public void Dispose() => _myService?.Dispose();
}

By shifting from "injecting the service" to "managing the service instance," you regain control over the component tree's memory footprint. This prevents the DI container from holding onto references that your UI no longer requires, keeping your application lean as it scales.

Key Takeaway: Always pair complex service injection with IDisposable cleanup or factory-based instantiation to ensure components are correctly released from memory when they are removed from the UI tree.