Oktatóanyag: Függőséginjektálás használata a .NET-ben
Ez az oktatóanyag bemutatja, hogyan használható függőséginjektálás (DI) a .NET. A Microsoft-bővítmények segítségével a DI-t a szolgáltatások hozzáadásával és IServiceCollectionkonfigurálásával kezelik. A IHost felület elérhetővé teszi a IServiceProvider-példányt, amely az összes regisztrált szolgáltatás tárolójaként működik.
Ebben az oktatóanyagban a következőket sajátíthatja el:
- Függőséginjektálást használó .NET-konzolalkalmazás létrehozása
- Általános gazdagép létrehozása és konfigurálása
- Több felület és megfelelő implementáció írása
- A szolgáltatás élettartamának és hatókörének használata a DI-hez
Előfeltételek
- .NET Core 3.1 SDK vagy újabb.
- Az új .NET-alkalmazások létrehozásának és a NuGet-csomagok telepítésének ismerete.
Új konzolalkalmazás létrehozása
A dotnet új parancsával vagy egy IDE új projektvarázslóval hozzon létre egy új .NET-konzolalkalmazást ConsoleDI néven.Example. Adja hozzá a Microsoft.Extensions.Hosting NuGet-csomagot a projekthez.
Az új konzolalkalmazás projektfájljának az alábbiakhoz kell hasonlítania:
<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.Configuration.Binder" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.3" />
</ItemGroup>
</Project>
Fontos
Ebben a példában a Microsoft.Extensions.Hosting NuGet-csomag szükséges az alkalmazás létrehozásához és futtatásához. Egyes metacsomagok tartalmazhatják a Microsoft.Extensions.Hosting
csomagot, ebben az esetben nincs szükség explicit csomaghivatkozásra.
Interfészek hozzáadása
Ebben a mintaalkalmazásban megtanulhatja, hogyan kezeli a függőséginjektálás a szolgáltatások élettartamát. Több felületet fog létrehozni, amelyek különböző szolgáltatási élettartamokat jelölnek. Adja hozzá a következő felületeket a projekt gyökérkönyvtárához:
IReportServiceLifetime.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IReportServiceLifetime
{
Guid Id { get; }
ServiceLifetime Lifetime { get; }
}
A IReportServiceLifetime
felület a következőket határozza meg:
- Egy
Guid Id
tulajdonság, amely a szolgáltatás egyedi azonosítóját jelöli. - Egy ServiceLifetime tulajdonság, amely a szolgáltatás élettartamát jelöli.
IExampleTransientService.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IExampleTransientService : IReportServiceLifetime
{
ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Transient;
}
IExampleScopedService.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IExampleScopedService : IReportServiceLifetime
{
ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Scoped;
}
IExampleSingletonService.cs
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;
public interface IExampleSingletonService : IReportServiceLifetime
{
ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Singleton;
}
A IReportServiceLifetime
összes alinterfésze explicit módon implementálja a IReportServiceLifetime.Lifetime
alapértelmezett változatát. Például IExampleTransientService
explicit módon implementálja a IReportServiceLifetime.Lifetime
a ServiceLifetime.Transient
értékkel.
Alapértelmezett implementációk hozzáadása
A példaimplementációk mind inicializálják Id
tulajdonságukat a Guid.NewGuid()eredményével. Adja hozzá az alábbi alapértelmezett implementációs osztályokat a különböző szolgáltatásokhoz a projekt gyökérkönyvtárához:
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();
}
Minden implementáció internal sealed
ként van definiálva, és implementálja a megfelelő felületet. Nem szükséges, hogy ezek internal
vagy sealed
legyenek; azonban gyakori, hogy az implementációkat internal
-ként kezelik, hogy elkerüljék az implementációs típusok kiszivárgását a külső felhasználók felé. Továbbá, mivel az egyes típusokat nem bővítjük ki, sealed
lesz megjelölve. Például ExampleSingletonService
implementálja IExampleSingletonService
.
Di-t igénylő szolgáltatás hozzáadása
Adja hozzá a következő szolgáltatás-élettartamú riporteri osztályt, amely szolgáltatásként működik a konzolalkalmazásban:
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})");
}
A ServiceLifetimeReporter
olyan konstruktort határoz meg, amely a fent említett szolgáltatási felületek mindegyikét, azaz IExampleTransientService
, IExampleScopedService
és IExampleSingletonService
igényel. Az objektum egyetlen metódust tesz elérhetővé, amely lehetővé teszi a fogyasztó számára, hogy egy adott lifetimeDetails
paraméterrel jelentse a szolgáltatást. Meghíváskor a ReportServiceLifetimeDetails
metódus naplózza az egyes szolgáltatások egyedi azonosítóját a szolgáltatás élettartamára vonatkozó üzenettel. A naplóüzenetek segítenek a szolgáltatás élettartamának megjelenítésében.
Szolgáltatások regisztrálása a DI-hez
Frissítse Program.cs a következő kóddal:
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();
}
Minden services.Add{LIFETIME}<{SERVICE}>
bővítménymetódus szolgáltatásokat ad hozzá (és konfigurál). Javasoljuk, hogy az alkalmazások kövessék ezt az egyezményt. Ne helyezzen el kiterjesztési metódusokat a Microsoft.Extensions.DependencyInjection névtérben, hacsak nem egy hivatalos Microsoft-csomagot szerkeszt. A Microsoft.Extensions.DependencyInjection
névtérben definiált bővítménymetelyek:
- A IntelliSense jelennek meg további
using
irányelvek megkövetelése nélkül. - Csökkentse a szükséges
using
irányelvek számát azokban aProgram
vagyStartup
osztályokban, ahol ezeket a kiterjesztési módszereket általában használják.
Az alkalmazás:
- Létrehoz egy IHostBuilder-példányt a gazdaépítő beállításainak felhasználásával.
- Konfigurálja a szolgáltatásokat, és hozzáadja őket a hozzájuk tartozó szolgáltatási élettartammal.
- Meghívja Build(), és hozzárendeli a IHostegy példányát.
-
ExemplifyScoping
hívása, mely átadja a IHost.Services-et.
Következtetés
Ebben a mintaalkalmazásban számos felületet és megfelelő implementációt hozott létre. Minden egyes szolgáltatás egyedileg van azonosítva és összekapcsolva egy ServiceLifetime-ával. A mintaalkalmazás bemutatja, hogyan regisztrálhat szolgáltatás-implementációkat egy felületen, és hogyan regisztrálhat tiszta osztályokat háttérfelületek nélkül. A mintaalkalmazás ezután bemutatja, hogyan oldódnak fel a konstruktorparaméterekként definiált függőségek futásidőben.
Az alkalmazás futtatásakor az alábbihoz hasonló kimenet jelenik meg:
// 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)
Az alkalmazás kimenetében a következő látható:
- Transient szolgáltatások mindig eltérőek, a szolgáltatás minden lekérésével létrejön egy új példány.
- Scoped szolgáltatások csak akkor változnak, ha új hatókör kerül meghatározásra, de egy hatókörön belül ugyanaz a példány maradnak.
- Singleton szolgáltatások mindig azonosak, egy új példány csak egyszer jön létre.