Transient dependency example (#23490)

pull/23491/head
Luke Latham 2021-10-08 06:00:05 -05:00 committed by GitHub
parent 0a16f190d0
commit f00589b442
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 57 additions and 41 deletions

View File

@ -179,9 +179,13 @@ Prerequisites for constructor injection:
In ASP.NET Core apps, scoped services are typically scoped to the current request. After the request completes, any scoped or transient services are disposed by the DI system. In Blazor Server apps, the request scope lasts for the duration of the client connection, which can result in transient and scoped services living much longer than expected. In Blazor WebAssembly apps, services registered with a scoped lifetime are treated as singletons, so they live longer than scoped services in typical ASP.NET Core apps.
<!--
> [!NOTE]
> To detect disposable transient services in an app, see the [Detect transient disposables](#detect-transient-disposables) section.
-->
An approach that limits a service lifetime in Blazor apps is use of the <xref:Microsoft.AspNetCore.Components.OwningComponentBase> type. <xref:Microsoft.AspNetCore.Components.OwningComponentBase> is an abstract type derived from <xref:Microsoft.AspNetCore.Components.ComponentBase> that creates a DI scope corresponding to the lifetime of the component. Using this scope, it's possible to use DI 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 that:
* Should be reused within a component, as the transient lifetime is inappropriate.
@ -216,9 +220,11 @@ Two versions of the <xref:Microsoft.AspNetCore.Components.OwningComponentBase> t
For more information, see <xref:blazor/blazor-server-ef-core>.
<!--
## Detect transient disposables
The following examples show how to detect disposable transient services in an app that should use <xref:Microsoft.AspNetCore.Components.OwningComponentBase>. For more information, see the [Utility base component classes to manage a DI scope](#utility-base-component-classes-to-manage-a-di-scope) section.
The following example shows how to detect disposable transient services in an app that should use <xref:Microsoft.AspNetCore.Components.OwningComponentBase>. For more information, see the [Utility base component classes to manage a DI scope](#utility-base-component-classes-to-manage-a-di-scope) section.
::: zone pivot="webassembly"
@ -226,25 +232,6 @@ The following examples show how to detect disposable transient services in an ap
[!code-csharp[](~/blazor/samples/6.0/BlazorSample_WebAssembly/dependency-injection/DetectIncorrectUsagesOfTransientDisposables.cs)]
The `TransientDisposable` in the following example is detected (`Program.cs`):
```csharp
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.DetectIncorrectUsageOfTransients();
builder.RootComponents.Add<App>("#app");
builder.Services.AddTransient<TransientDisposable>();
builder.Services.AddScoped(sp =>
new HttpClient
{
BaseAddress = new(builder.HostEnvironment.BaseAddress)
});
var host = builder.Build();
host.EnableTransientDisposableDetection();
await host.RunAsync();
```
`TransientDisposable.cs`:
```csharp
@ -254,6 +241,32 @@ public class TransientDisposable : IDisposable
}
```
The `TransientDisposable` in the following example is detected.
`Program.cs`:
```csharp
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using BlazorWebAssemblyTransientDisposable;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.DetectIncorrectUsageOfTransients();
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddTransient<TransientDisposable>();
builder.Services.AddScoped(sp =>
new HttpClient
{
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});
var host = builder.Build();
host.EnableTransientDisposableDetection();
await host.RunAsync();
```
::: zone-end
::: zone pivot="server"
@ -262,12 +275,6 @@ public class TransientDisposable : IDisposable
[!code-csharp[](~/blazor/samples/6.0/BlazorSample_Server/dependency-injection/DetectIncorrectUsagesOfTransientDisposables.cs)]
In `Program.cs`:
```csharp
app.DetectIncorrectUsageOfTransients();
```
`TransitiveTransientDisposableDependency.cs`:
```csharp
@ -295,34 +302,43 @@ public class TransientDependency
}
```
The `TransientDependency` in the following example is detected (`Program.cs`):
The `TransientDependency` in the following example is detected.
In `Program.cs`:
```csharp
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
...
builder.Services.AddTransient<TransientDependency>();
builder.Services.AddTransient<ITransitiveTransientDisposableDependency,
TransitiveTransientDisposableDependency>();
...
app.DetectIncorrectUsageOfTransients();
...
```
::: zone-end
The app can register transient disposables without throwing an exception. However, attempting to resolve a transient disposable results in an <xref:System.InvalidOperationException>, as the following example shows.
`Pages/TransientDisposable.razor`:
`Pages/TransientExample.razor`:
```razor
@page "/transient-disposable"
@page "/transient-example"
@inject TransientDisposable TransientDisposable
<h1>Transient Disposable Detection</h1>
```
Navigate to the `TransientDisposable` component at `/transient-disposable` and an <xref:System.InvalidOperationException> is thrown when the framework attempts to construct an instance of `TransientDisposable`:
Navigate to the `TransientExample` component at `/transient-example` and an <xref:System.InvalidOperationException> is thrown when the framework attempts to construct an instance of `TransientDisposable`:
> System.InvalidOperationException: Trying to resolve transient disposable service TransientDisposable in the wrong scope. Use an 'OwningComponentBase\<T>' component base class for the service 'T' you are trying to resolve.
-->
## Additional resources
* <xref:fundamentals/dependency-injection>
@ -559,7 +575,7 @@ For more information, see <xref:blazor/blazor-server-ef-core>.
## Detect transient disposables
The following examples show how to detect disposable transient services in an app that should use <xref:Microsoft.AspNetCore.Components.OwningComponentBase>. For more information, see the [Utility base component classes to manage a DI scope](#utility-base-component-classes-to-manage-a-di-scope) section.
The following example shows how to detect disposable transient services in an app that should use <xref:Microsoft.AspNetCore.Components.OwningComponentBase>. For more information, see the [Utility base component classes to manage a DI scope](#utility-base-component-classes-to-manage-a-di-scope) section.
::: zone pivot="webassembly"
@ -664,16 +680,16 @@ public class TransientDependency
The app can register transient disposables without throwing an exception. However, attempting to resolve a transient disposable results in an <xref:System.InvalidOperationException>, as the following example shows.
`Pages/TransientDisposable.razor`:
`Pages/TransientExample.razor`:
```razor
@page "/transient-disposable"
@page "/transient-example"
@inject TransientDisposable TransientDisposable
<h1>Transient Disposable Detection</h1>
```
Navigate to the `TransientDisposable` component at `/transient-disposable` and an <xref:System.InvalidOperationException> is thrown when the framework attempts to construct an instance of `TransientDisposable`:
Navigate to the `TransientExample` component at `/transient-example` and an <xref:System.InvalidOperationException> is thrown when the framework attempts to construct an instance of `TransientDisposable`:
> System.InvalidOperationException: Trying to resolve transient disposable service TransientDisposable in the wrong scope. Use an 'OwningComponentBase\<T>' component base class for the service 'T' you are trying to resolve.
@ -913,7 +929,7 @@ For more information, see <xref:blazor/blazor-server-ef-core>.
## Detect transient disposables
The following examples show how to detect disposable transient services in an app that should use <xref:Microsoft.AspNetCore.Components.OwningComponentBase>. For more information, see the [Utility base component classes to manage a DI scope](#utility-base-component-classes-to-manage-a-di-scope) section.
The following example shows how to detect disposable transient services in an app that should use <xref:Microsoft.AspNetCore.Components.OwningComponentBase>. For more information, see the [Utility base component classes to manage a DI scope](#utility-base-component-classes-to-manage-a-di-scope) section.
::: zone pivot="webassembly"
@ -1018,16 +1034,16 @@ public class TransientDependency
The app can register transient disposables without throwing an exception. However, attempting to resolve a transient disposable results in an <xref:System.InvalidOperationException>, as the following example shows.
`Pages/TransientDisposable.razor`:
`Pages/TransientExample.razor`:
```razor
@page "/transient-disposable"
@page "/transient-example"
@inject TransientDisposable TransientDisposable
<h1>Transient Disposable Detection</h1>
```
Navigate to the `TransientDisposable` component at `/transient-disposable` and an <xref:System.InvalidOperationException> is thrown when the framework attempts to construct an instance of `TransientDisposable`:
Navigate to the `TransientExample` component at `/transient-example` and an <xref:System.InvalidOperationException> is thrown when the framework attempts to construct an instance of `TransientDisposable`:
> System.InvalidOperationException: Trying to resolve transient disposable service TransientDisposable in the wrong scope. Use an 'OwningComponentBase\<T>' component base class for the service 'T' you are trying to resolve.