Kurz: Použití injektáže závislostí v .NET
V tomto kurzu se dozvíte, jak používat injektáž závislostí (DI) v .NET. Pomocí rozšíření Microsoftu se DI spravuje přidáním služeb a jejich konfigurací v objektu IServiceCollection. Rozhraní IHost zveřejňuje IServiceProvider instanci, která funguje jako kontejner všech registrovaných služeb.
V tomto kurzu se naučíte:
- Vytvoření konzolové aplikace .NET, která používá injektáž závislostí
- Sestavení a konfigurace obecného hostitele
- Zápis několika rozhraní a odpovídajících implementací
- Použití doby životnosti a rozsahu služby pro DI
Požadavky
- .NET Core 3.1 SDK nebo novější.
- Znalost vytváření nových aplikací .NET a instalace balíčků NuGet
Vytvoření nové konzolové aplikace
Pomocí příkazu dotnet new nebo průvodce novým projektem IDE vytvořte novou konzolovou aplikaci .NET s názvem ConsoleDI.Example. Přidejte do projektu balíček NuGet Microsoft.Extensions.Hosting .
Nový soubor projektu aplikace konzoly by měl vypadat přibližně takto:
<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>
Důležité
V tomto příkladu se k sestavení a spuštění aplikace vyžaduje balíček NuGet Microsoft.Extensions.Hosting . Některé metabalíky můžou balíček obsahovat Microsoft.Extensions.Hosting
, v takovém případě se nevyžaduje explicitní odkaz na balíček.
Přidání rozhraní
V této ukázkové aplikaci se dozvíte, jak injektáž závislostí zpracovává životnost služby. Vytvoříte několik rozhraní, která představují různé životnosti služeb. Do kořenového adresáře projektu přidejte následující rozhraní:
IReportServiceLifetime.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IReportServiceLifetime
{
Guid Id { get; }
ServiceLifetime Lifetime { get; }
}
Rozhraní IReportServiceLifetime
definuje:
- Vlastnost
Guid Id
, která představuje jedinečný identifikátor služby. - Vlastnost ServiceLifetime , která představuje životnost služby.
Service.csExampleTransient
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IExampleTransientService : IReportServiceLifetime
{
ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Transient;
}
Service.csExampleScoped
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IExampleScopedService : IReportServiceLifetime
{
ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Scoped;
}
Service.csExampleSingleton
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IExampleSingletonService : IReportServiceLifetime
{
ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Singleton;
}
Všechny podinterfaces IReportServiceLifetime
explicitně implementují IReportServiceLifetime.Lifetime
s výchozím nastavením. Například IExampleTransientService
explicitně implementuje IReportServiceLifetime.Lifetime
s ServiceLifetime.Transient
hodnotou.
Přidání výchozích implementací
Příklad implementace všechny inicializovat své Id
vlastnosti s výsledkem .Guid.NewGuid() Do kořenového adresáře projektu přidejte následující výchozí třídy implementace pro různé služby:
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();
}
Každá implementace je definována jako internal sealed
a implementuje své odpovídající rozhraní. Například ExampleSingletonService
implementuje IExampleSingletonService
.
Přidání služby, která vyžaduje DI
Přidejte do konzolové aplikace následující třídu reporteru doby životnosti služby, která funguje jako služba:
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})");
}
Definuje ServiceLifetimeReporter
konstruktor, který vyžaduje všechna výše uvedená rozhraní služby, tj. IExampleTransientService
, , IExampleScopedService
a IExampleSingletonService
. Objekt zveřejňuje jednu metodu, která příjemci umožňuje hlásit službu s daným lifetimeDetails
parametrem. Při vyvolání ReportServiceLifetimeDetails
metoda zaznamená jedinečný identifikátor každé služby se zprávou životnosti služby. Zprávy protokolu pomáhají vizualizovat životnost služby.
Registrace služeb pro DI
Aktualizujte Program.cs následujícím kódem:
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();
}
Každá services.Add{LIFETIME}<{SERVICE}>
metoda rozšíření přidává (a potenciálně konfiguruje) služby. Doporučujeme, aby aplikace dodržovaly tuto konvenci. Metody rozšíření neumisťujte do Microsoft.Extensions.DependencyInjection oboru názvů, pokud nevytváření oficiálního balíčku Microsoftu. Rozšiřující metody definované v rámci Microsoft.Extensions.DependencyInjection
oboru názvů:
- Zobrazují se v IntelliSense bez nutnosti dalších
using
bloků. - Snižte počet požadovaných
using
příkazů vProgram
neboStartup
třídách, ve kterých se tyto rozšiřující metody obvykle volají.
Aplikace:
- IHostBuilder Vytvoří instanci s nastavením tvůrce hostitelů.
- Nakonfiguruje služby a přidá je s odpovídající životností služby.
- Volá Build() a přiřazuje instanci IHost.
- Volání
ExemplifyScoping
, předávání .IHost.Services
Závěr
V této ukázkové aplikaci jste vytvořili několik rozhraní a odpovídající implementace. Každá z těchto služeb je jedinečně identifikována a spárována s .ServiceLifetime Ukázková aplikace demonstruje registraci implementací služby na rozhraní a postup registrace čistých tříd bez rozhraní. Ukázková aplikace pak ukazuje, jak se závislosti definované jako parametry konstruktoru přeloží za běhu.
Při spuštění aplikace se zobrazí výstup podobný následujícímu:
// 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)
Ve výstupu aplikace vidíte, že:
- Transient služby se vždy liší, vytvoří se nová instance s každým načtením služby.
- Scoped služby se mění pouze s novým oborem, ale jsou stejnou instancí v rámci oboru.
- Singleton služby jsou vždy stejné, nová instance se vytvoří pouze jednou.