Rychlý start: Základy injektáže závislostí v .NET

V tomto rychlém startu vytvoříte konzolovou aplikaci .NET, která ručně vytvoří ServiceCollection a odpovídající 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á obecné funkce 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. V sadě Visual Studio zvolte Soubor > nový > projekt nebo pomocí .NET CLI zadejte dotnet new console.

Dále do souboru projektu přidejte odkaz na balíček Microsoft.Extensions.DependencyInjection . Po přidání balíčku se ujistěte, že projekt vypadá podobně jako následující XML souboru DI.Basics.csproj :

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

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

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

</Project>

Základy injektování závislostí

Injektáž závislostí je vzor návrhu, který můžete použít k odebrání pevně zakódovaných závislostí a zajištění větší údržby a testování aplikace. DI je technika pro dosažení inverze řízení (IoC) mezi třídami a jejich závislostmi.

Balíček NuGet Microsoft.Extensions.DependencyInjection.Abstractions definuje abstrakce pro DI v .NET:

V .NET spravujete DI přidáním služeb a jejich konfigurací v objektu IServiceCollection. Po registraci služeb zavolejte metodu BuildServiceProviderIServiceProvider pro sestavení instance. Funguje IServiceProvider jako kontejner pro všechny registrované služby a používáte ho k řešení služeb.

Vytvoření ukázkových 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á (přechodné), zatímco jiné by měly být sdíleny napříč požadavky (s omezeným rozsahem) 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 jazyka 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 konzole na základě vlastnosti IsEnabled.

Návod

Pojmenování implementace je volba, na které by váš vývojový tým měl souhlasit. 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ří greeting na základě name.
  • Volá metodu WriteLine na instanci IConsole.
  • Vrátí greeting volajícímu.

Třída DefaultGreetingService ukazuje, že můžete seal implementace služeb, aby se zabránilo dědičnosti, a použít internal k omezení přístupu k sestavě.

Poslední službou, která se má vytvořit, je soubor FarewellService.cs . 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ěli byste ji public deklarovat tak, aby byla přístupná uživatelům. Na rozdíl od jiných typů implementace služby, které deklarujete jako internal a sealed, tento kód ukazuje, že ne všechny služby musí být rozhraní.

Aktualizujte třídu Program

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í kód ukazuje, jak:

  • Vytvořte novou ServiceCollection instanci.
  • Registrace a konfigurace služeb v ServiceCollection:
    • Služba IConsole pomocí přetížení implementační továrny. Vrátit typ DefaultConsole s vlastností IsEnabled nastavenou na true.
    • Služba IGreetingService s odpovídajícím typem implementace DefaultGreetingService.
    • Služba FarewellService jako konkrétní typ.
  • Sestavte ServiceProvider z ServiceCollection.
  • Vyřešte služby IGreetingService a FarewellService.
  • Pomocí vyřešených služeb pozdravte a rozlučte se s osobou jménem David.

Pokud aktualizujete IsEnabled vlastnost DefaultConsole na false, Greet a SayGoodbye metody vynechají zápis výsledných zpráv do konzoly. Tato změna pomáhá prokázat, že IConsole služba je injektována do IGreetingService a FarewellService služeb jako závislost, která ovlivňuje chování aplikace.

Všechny tyto služby jsou zaregistrovány jako singletony. Pro tuto ukázku funguje stejně, pokud je zaregistrujete jako přechodné nebo omezené služby.

Důležité

V tomto příkladu nezáleží na životnosti služeb. V reálné aplikaci pečlivě zvažte ž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 nebo editoru Visual Studio Code nebo spuštěním dotnet run příkazu v terminálu. Po dokončení aplikace se zobrazí 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 ServiceCollection jsou obecné metody s názvy životního cyklu, jako například:

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

Tyto metody jsou usnadňující, které vytvářejí instanci ServiceDescriptor 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é popsat implementační továrny a instance.

Pro každou službu, kterou jste zaregistrovali v ServiceCollection, můžete místo toho přímo volat metodu Add s instancí ServiceDescriptor. 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í způsobu, jakým byla služba IConsole zaregistrována v ServiceCollection souboru. Metoda Add přidá ServiceDescriptor instanci, 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á jako jednoúčelová služba.

Viz také