Vysvětlení základů injektáže závislostí v .NET

V tomto článku vytvoříte konzolovou aplikaci .NET, která ručně vytvoří ServiceCollection a odpovídá ServiceProvider. Naučíte se registrovat služby a řešit je pomocí injektáže závislostí (DI). Tento článek používá balíček NuGet Microsoft.Extensions.DependencyInjection k předvedení základů DI v .NET.

Poznámka:

Tento článek nevyužívá funkce obecného hostitele . Podrobnější průvodce najdete v tématu Použití injektáže závislostí v .NET.

Začínáme

Začněte vytvořením nové konzolové aplikace .NET s názvem DI.Basics. Na některé z nejběžnějších přístupů k vytvoření projektu konzoly se odkazuje v následujícím seznamu:

Do souboru projektu je potřeba přidat odkaz na balíček Microsoft.Extensions.DependencyInjection . Bez ohledu na přístup se ujistěte, že se projekt podobá následujícímu kódu XML souboru DI.Basics.csproj :

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
  </ItemGroup>

</Project>

Základy injektáže závislostí

Injektáž závislostí je vzor návrhu, který umožňuje odebrat pevně zakódované závislosti a zajistit, aby byla vaše aplikace lépe udržovatelná a testovatelná. DI je technika pro dosažení inverze řízení (IoC) mezi třídami a jejich závislostmi.

Abstrakce di di v .NET jsou definovány v balíčku NuGet Microsoft.Extensions.DependencyInjection.Abstractions :

V .NET se di spravuje přidáním služeb a jejich konfigurací v objektu IServiceCollection. Po registraci IServiceProvider služeb je instance vytvořena voláním BuildServiceProvider metody. Funguje IServiceProvider jako kontejner všech registrovaných služeb a slouží k řešení služeb.

Vytváření example služeb

Ne všechny služby se vytvářejí stejně. Některé služby vyžadují novou instanci pokaždé, když je kontejner služby získá (transient), zatímco jiné by se měly sdílet mezi požadavky (scoped) nebo po celou dobu životnosti aplikace (singleton). Další informace o životnostech služeb najdete v tématu Životnost služby.

Podobně některé služby zpřístupňují pouze konkrétní typ, zatímco jiné jsou vyjádřeny jako kontrakt mezi rozhraním a typem implementace. Vytvoříte několik variant služeb, které vám pomůžou tyto koncepty předvést.

Vytvořte nový soubor C# s názvem IConsole.cs a přidejte následující kód:

public interface IConsole
{
    void WriteLine(string message);
}

Tento soubor definuje IConsole rozhraní, které zveřejňuje jednu metodu , WriteLine. Dále vytvořte nový soubor C# s názvem DefaultConsole.cs a přidejte následující kód:

internal sealed class DefaultConsole : IConsole
{
    public bool IsEnabled { get; set; } = true;

    void IConsole.WriteLine(string message)
    {
        if (IsEnabled is false)
        {
            return;
        }

        Console.WriteLine(message);
    }
}

Předchozí kód představuje výchozí implementaci IConsole rozhraní. Metoda WriteLine podmíněně zapisuje do konzoly na IsEnabled základě vlastnosti.

Tip

Pojmenování implementace je volba, na které by se měl váš vývojový tým shodnout. Předpona Default je běžnou konvencí označující výchozí implementaci rozhraní, ale nevyžaduje se.

Dále vytvořte soubor IGreetingService.cs a přidejte následující kód jazyka C#:

public interface IGreetingService
{
    string Greet(string name);
}

Pak přidejte nový soubor C# s názvem DefaultGreetingService.cs a přidejte následující kód:

internal sealed class DefaultGreetingService(
    IConsole console) : IGreetingService
{
    public string Greet(string name)
    {
        var greeting = $"Hello, {name}!";

        console.WriteLine(greeting);

        return greeting;
    }
}

Předchozí kód představuje výchozí implementaci IGreetingService rozhraní. Implementace služby vyžaduje IConsole jako primární parametr konstruktoru. Metoda Greet:

  • Vytvoří danou greeting .name
  • Volá metodu WriteLine v IConsole instanci.
  • greeting Vrátí volajícímu.

Poslední službou , která se má vytvořit, je soubor FarewellService.cs a před pokračováním přidejte následující kód jazyka C#:

public class FarewellService(IConsole console)
{
    public string SayGoodbye(string name)
    {
        var farewell = $"Goodbye, {name}!";

        console.WriteLine(farewell);

        return farewell;
    }
}

Představuje FarewellService konkrétní typ, nikoli rozhraní. Měla by být deklarována tak public , aby byla přístupná uživatelům. Na rozdíl od jiných typů implementace služby, které byly deklarovány jako internal a sealed, tento kód ukazuje, že ne všechny služby musí být rozhraní. Ukazuje také, že implementace služeb mohou bránit sealed dědičnosti a internal omezit přístup k sestavení.

Program Aktualizace třídy

Otevřete soubor Program.cs a nahraďte stávající kód následujícím kódem jazyka C#:

using Microsoft.Extensions.DependencyInjection;

// 1. Create the service collection.
var services = new ServiceCollection();

// 2. Register (add and configure) the services.
services.AddSingleton<IConsole>(
    implementationFactory: static _ => new DefaultConsole
    {
        IsEnabled = true
    });
services.AddSingleton<IGreetingService, DefaultGreetingService>();
services.AddSingleton<FarewellService>();

// 3. Build the service provider from the service collection.
var serviceProvider = services.BuildServiceProvider();

// 4. Resolve the services that you need.
var greetingService = serviceProvider.GetRequiredService<IGreetingService>();
var farewellService = serviceProvider.GetRequiredService<FarewellService>();

// 5. Use the services
var greeting = greetingService.Greet("David");
var farewell = farewellService.SayGoodbye("David");

Předchozí aktualizovaný kód ukazuje postupy:

  • Vytvořte novou ServiceCollection instanci.
  • Registrace a konfigurace služeb v :ServiceCollection
    • Použití IConsole přetížení implementační továrny vrátí DefaultConsole typ s nastavenou IsEnabled na hodnotu true.
    • Přidá se IGreetingService s odpovídajícím typem DefaultGreetingService implementace.
    • Je FarewellService přidán jako betonový typ.
  • Sestavte z objektu ServiceProvider ServiceCollection.
  • Řešení problémů IGreetingService a FarewellService služeb
  • Pomocí vyřešených služeb pozdravte a rozloučíte se s osobou s názvem David.

Pokud aktualizujete IsEnabled vlastnost DefaultConsole to false, Greet a SayGoodbye metody vynechat zápis do výsledných zpráv do konzoly. Taková změna vám pomůže předvést, že IConsole se služba vloží do IGreetingService FarewellService služeb jako závislost , která ovlivňuje chování aplikací.

Všechny tyto služby jsou registrovány jako singletony, i když pro tuto ukázku, funguje stejně, pokud byly zaregistrovány jako transient nebo scoped služby.

Důležité

V této contrived example, životnosti služby nezáleží, ale v reálné aplikaci byste měli pečlivě zvážit životnost každé služby.

Spuštění ukázkové aplikace

Ukázkovou aplikaci spustíte stisknutím klávesy F5 v sadě Visual Studio, editoru Visual Studio Code nebo spuštěním dotnet run příkazu v terminálu. Po dokončení aplikace by se měl zobrazit následující výstup:

Hello, David!
Goodbye, David!

Popisovače služeb

Nejčastěji používaná rozhraní API pro přidávání služeb do obecných ServiceCollection rozšiřujících metod, jako jsou:

  • AddSingleton<TService>
  • AddTransient<TService>
  • AddScoped<TService>

Tyto metody jsou pohodlné metody, které vytvářejí ServiceDescriptor instanci a přidávají ji do .ServiceCollection Jedná se ServiceDescriptor o jednoduchou třídu, která popisuje službu s jejím typem služby, typem implementace a životností. Může také popisovat implementační továrny a instance.

Pro každou službu, kterou jste zaregistrovali v nástroji ServiceCollection, můžete místo toho volat Add metodu ServiceDescriptor s instancí přímo. Zvažte následující příklady:

services.Add(ServiceDescriptor.Describe(
    serviceType: typeof(IConsole),
    implementationFactory: static _ => new DefaultConsole
    {
        IsEnabled = true
    },
    lifetime: ServiceLifetime.Singleton));

Předchozí kód je ekvivalentní tomu, jak IConsole byla služba zaregistrována v ServiceCollectionsouboru . Metoda Add se používá k přidání ServiceDescriptor instance, která popisuje IConsole službu. Statická metoda ServiceDescriptor.Describe deleguje na různé ServiceDescriptor konstruktory. Vezměte v úvahu ekvivalentní kód pro IGreetingService službu:

services.Add(ServiceDescriptor.Describe(
    serviceType: typeof(IGreetingService),
    implementationType: typeof(DefaultGreetingService),
    lifetime: ServiceLifetime.Singleton));

Předchozí kód popisuje IGreetingService službu s jejím typem služby, typem implementace a životností. Nakonec zvažte ekvivalentní kód pro FarewellService službu:

services.Add(ServiceDescriptor.Describe(
    serviceType: typeof(FarewellService),
    implementationType: typeof(FarewellService),
    lifetime: ServiceLifetime.Singleton));

Předchozí kód popisuje konkrétní FarewellService typ jako typ služby i implementace. Služba je zaregistrovaná singleton jako služba.

Viz také