Självstudie: Använda beroendeinmatning i .NET
Den här självstudien visar hur du använder beroendeinmatning (DI) i .NET. Med Microsoft-tillägg hanteras DI genom att lägga till tjänster och konfigurera dem i en IServiceCollection. Gränssnittet IHost exponerar instansen IServiceProvider , som fungerar som en container för alla registrerade tjänster.
I den här självstudien lär du dig att:
- Skapa en .NET-konsolapp som använder beroendeinmatning
- Skapa och konfigurera en allmän värd
- Skriva flera gränssnitt och motsvarande implementeringar
- Använda tjänstens livslängd och omfång för DI
Förutsättningar
- .NET Core 3.1 SDK eller senare.
- Kunskaper om att skapa nya .NET-program och installera NuGet-paket.
Skapa ett nytt konsolprogram
Skapa ett nytt .NET-konsolprogram med namnet ConsoleDI med hjälp av antingen det nya dotnet-kommandot eller en ny IDE-projektguide.Example Lägg till NuGet-paketet Microsoft.Extensions.Hosting i projektet.
Den nya konsolappens projektfil bör likna följande:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<RootNamespace>ConsoleDI.Example</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup>
</Project>
Viktigt!
I det här exemplet krävs NuGet-paketet Microsoft.Extensions.Hosting för att skapa och köra appen. Vissa metapaket kan innehålla Microsoft.Extensions.Hosting
paketet, i vilket fall en explicit paketreferens inte krävs.
Lägga till gränssnitt
I den här exempelappen får du lära dig hur beroendeinmatning hanterar tjänstens livslängd. Du skapar flera gränssnitt som representerar olika tjänstlivslängder. Lägg till följande gränssnitt i projektrotkatalogen:
IReportServiceLifetime.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IReportServiceLifetime
{
Guid Id { get; }
ServiceLifetime Lifetime { get; }
}
Gränssnittet IReportServiceLifetime
definierar:
- En
Guid Id
egenskap som representerar den unika identifieraren för tjänsten. - En ServiceLifetime egenskap som representerar tjänstens livslängd.
JagExampleTransientService.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IExampleTransientService : IReportServiceLifetime
{
ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Transient;
}
JagExampleScopedService.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IExampleScopedService : IReportServiceLifetime
{
ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Scoped;
}
JagExampleSingletonService.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IExampleSingletonService : IReportServiceLifetime
{
ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Singleton;
}
Alla undergränssnitt IReportServiceLifetime
för att uttryckligen implementera IReportServiceLifetime.Lifetime
med ett standardvärde. Implementerar IReportServiceLifetime.Lifetime
till exempel IExampleTransientService
uttryckligen med ServiceLifetime.Transient
värdet.
Lägga till standardimplementeringar
Exempelimplementeringarna initierar alla sin Id
egenskap med resultatet av Guid.NewGuid(). Lägg till följande standardimplementeringsklasser för de olika tjänsterna i projektrotkatalogen:
ExampleTransientService.cs
namespace ConsoleDI.Example;
internal sealed class ExampleTransientService : IExampleTransientService
{
Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}
ExampleScopedService.cs
namespace ConsoleDI.Example;
internal sealed class ExampleScopedService : IExampleScopedService
{
Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}
ExampleSingletonService.cs
namespace ConsoleDI.Example;
internal sealed class ExampleSingletonService : IExampleSingletonService
{
Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}
Varje implementering definieras som internal sealed
och implementerar motsvarande gränssnitt. Implementerar IExampleSingletonService
till exempel ExampleSingletonService
.
Lägga till en tjänst som kräver DI
Lägg till följande reporterklass för tjänstlivslängd, som fungerar som en tjänst i konsolappen:
ServiceLifetimeReporter.cs
namespace ConsoleDI.Example;
internal sealed class ServiceLifetimeReporter(
IExampleTransientService transientService,
IExampleScopedService scopedService,
IExampleSingletonService singletonService)
{
public void ReportServiceLifetimeDetails(string lifetimeDetails)
{
Console.WriteLine(lifetimeDetails);
LogService(transientService, "Always different");
LogService(scopedService, "Changes only with lifetime");
LogService(singletonService, "Always the same");
}
private static void LogService<T>(T service, string message)
where T : IReportServiceLifetime =>
Console.WriteLine(
$" {typeof(T).Name}: {service.Id} ({message})");
}
Definierar ServiceLifetimeReporter
en konstruktor som kräver vart och ett av de ovan nämnda tjänstgränssnitten, dvs. IExampleTransientService
, IExampleScopedService
och IExampleSingletonService
. Objektet exponerar en enda metod som gör att konsumenten kan rapportera om tjänsten med en viss lifetimeDetails
parameter. När den ReportServiceLifetimeDetails
anropas loggar metoden varje tjänsts unika identifierare med meddelandet om tjänstlivslängd. Loggmeddelandena hjälper till att visualisera tjänstens livslängd.
Registrera tjänster för DI
Uppdatera Program.cs med följande kod:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ConsoleDI.Example;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddTransient<IExampleTransientService, ExampleTransientService>();
builder.Services.AddScoped<IExampleScopedService, ExampleScopedService>();
builder.Services.AddSingleton<IExampleSingletonService, ExampleSingletonService>();
builder.Services.AddTransient<ServiceLifetimeReporter>();
using IHost host = builder.Build();
ExemplifyServiceLifetime(host.Services, "Lifetime 1");
ExemplifyServiceLifetime(host.Services, "Lifetime 2");
await host.RunAsync();
static void ExemplifyServiceLifetime(IServiceProvider hostProvider, string lifetime)
{
using IServiceScope serviceScope = hostProvider.CreateScope();
IServiceProvider provider = serviceScope.ServiceProvider;
ServiceLifetimeReporter logger = provider.GetRequiredService<ServiceLifetimeReporter>();
logger.ReportServiceLifetimeDetails(
$"{lifetime}: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()");
Console.WriteLine("...");
logger = provider.GetRequiredService<ServiceLifetimeReporter>();
logger.ReportServiceLifetimeDetails(
$"{lifetime}: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()");
Console.WriteLine();
}
Varje services.Add{LIFETIME}<{SERVICE}>
tilläggsmetod lägger till (och kan konfigurera) tjänster. Vi rekommenderar att appar följer den här konventionen. Placera inte tilläggsmetoder i Microsoft.Extensions.DependencyInjection namnområdet om du inte skapar ett officiellt Microsoft-paket. Tilläggsmetoder som definieras i Microsoft.Extensions.DependencyInjection
namnområdet:
- Visas i IntelliSense utan ytterligare
using
block. - Minska antalet obligatoriska
using
instruktioner i deProgram
ellerStartup
klasser där dessa tilläggsmetoder vanligtvis anropas.
Appen:
- Skapar en IHostBuilder instans med värdbyggarinställningar.
- Konfigurerar tjänster och lägger till dem med motsvarande tjänstlivslängd.
- Anropar Build() och tilldelar en instans av IHost.
- Anropar
ExemplifyScoping
och skickar in IHost.Services.
Slutsats
I den här exempelappen skapade du flera gränssnitt och motsvarande implementeringar. Var och en av dessa tjänster identifieras unikt och paras ihop med en ServiceLifetime. Exempelappen visar hur du registrerar tjänstimplementeringar mot ett gränssnitt och hur du registrerar rena klasser utan stödgränssnitt. Exempelappen visar sedan hur beroenden som definieras som konstruktorparametrar löses vid körning.
När du kör appen visas utdata som liknar följande:
// Sample output:
// Lifetime 1: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()
// IExampleTransientService: d08a27fa-87d2-4a06-98d7-2773af886125 (Always different)
// IExampleScopedService: 402c83c9-b4ed-4be1-b78c-86be1b1d908d (Changes only with lifetime)
// IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
// ...
// Lifetime 1: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()
// IExampleTransientService: b43d68fb-2c7b-4a9b-8f02-fc507c164326 (Always different)
// IExampleScopedService: 402c83c9-b4ed-4be1-b78c-86be1b1d908d (Changes only with lifetime)
// IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
//
// Lifetime 2: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()
// IExampleTransientService: f3856b59-ab3f-4bbd-876f-7bab0013d392 (Always different)
// IExampleScopedService: bba80089-1157-4041-936d-e96d81dd9d1c (Changes only with lifetime)
// IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
// ...
// Lifetime 2: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()
// IExampleTransientService: a8015c6a-08cd-4799-9ec3-2f2af9cbbfd2 (Always different)
// IExampleScopedService: bba80089-1157-4041-936d-e96d81dd9d1c (Changes only with lifetime)
// IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b (Always the same)
Från apputdata kan du se följande:
- Transient tjänster är alltid olika, skapas en ny instans med varje hämtning av tjänsten.
- Scoped tjänster ändras endast med ett nytt omfång, men är samma instans inom ett omfång.
- Singleton tjänster är alltid desamma, skapas en ny instans bara en gång.