Udostępnij za pośrednictwem


Szybki start: podstawy iniekcji zależności na platformie .NET

W tym przewodniku szybkiego startu utworzysz aplikację konsolową na platformie .NET, która ręcznie utworzy ServiceCollection i odpowiadający ServiceProvider. Dowiesz się, jak zarejestrować usługi i rozwiązać je przy użyciu wstrzykiwania zależności (DI). W tym artykule użyto pakietu NuGet Microsoft.Extensions.DependencyInjection , aby zademonstrować podstawy di na platformie .NET.

Uwaga / Notatka

Ten artykuł nie korzysta z funkcji hosta ogólnego . Aby uzyskać bardziej kompleksowy przewodnik, zobacz Use dependency injection in .NET (Używanie wstrzykiwania zależności na platformie .NET).

Wprowadzenie

Aby rozpocząć, utwórz nową aplikację konsolową platformy .NET o nazwie DI.Basics. W programie Visual Studio wybierz pozycję Plik > Nowy > Projekt lub przy użyciu CLI platformy .NET wprowadź .dotnet new console

Następnie dodaj odwołanie do pakietu Microsoft.Extensions.DependencyInjection w pliku projektu. Po dodaniu pakietu upewnij się, że projekt przypomina następujący kod XML pliku 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>

Podstawy wstrzykiwania zależności

Wstrzykiwanie zależności to wzorzec projektowy, którego można użyć do usuwania trwale zakodowanych zależności i zwiększenia możliwości utrzymania i testowania aplikacji. Di jest techniką osiągnięcia inwersji kontroli (IoC) między klasami i ich zależnościami.

Pakiet NuGet Microsoft.Extensions.DependencyInjection.Abstractions definiuje abstrakcje dla di na platformie .NET:

Na platformie .NET zarządzasz di, dodając usługi i konfigurując je w programie IServiceCollection. Po zarejestrowaniu usług wywołaj metodę BuildServiceProvider, aby zbudować wystąpienie IServiceProvider. IServiceProvider działa jako kontener dla wszystkich zarejestrowanych usług i używasz go do rozwiązywania usług.

Tworzenie przykładowych usług

Nie wszystkie usługi mają tę samą jakość. Niektóre usługi wymagają nowego wystąpienia za każdym razem, gdy kontener usługi je pobiera (tymczasowy), podczas gdy inne powinny być współużytkowane między żądaniami (zasięgu) lub przez cały okres istnienia aplikacji (singleton). Aby uzyskać więcej informacji na temat okresów istnienia usług, zobacz Okresy istnienia usługi.

Podobnie niektóre usługi uwidaczniają tylko konkretny typ, podczas gdy inne są wyrażane jako kontrakt między interfejsem a typem implementacji. Utworzysz kilka odmian usług, aby zademonstrować te pojęcia.

Utwórz nowy plik C# o nazwie IConsole.cs i dodaj następujący kod:

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

Ten plik definiuje IConsole interfejs, który uwidacznia jedną metodę . WriteLine Następnie utwórz nowy plik C# o nazwie DefaultConsole.cs i dodaj następujący kod:

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

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

        Console.WriteLine(message);
    }
}

Powyższy kod reprezentuje domyślną implementację interfejsu IConsole . Metoda WriteLine warunkowo zapisuje do konsoli na podstawie właściwości IsEnabled.

Wskazówka

Nazewnictwo implementacji to wybór, na który zespół deweloperów powinien się zgodzić. Prefiks Default to wspólna konwencja wskazująca domyślną implementację interfejsu, ale nie jest wymagana.

Następnie utwórz plik IGreetingService.cs i dodaj następujący kod w języku C#:

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

Następnie dodaj nowy plik C# o nazwie DefaultGreetingService.cs i dodaj następujący kod:

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

        console.WriteLine(greeting);

        return greeting;
    }
}

Powyższy kod reprezentuje domyślną implementację interfejsu IGreetingService . Implementacja usługi wymaga IConsole jako podstawowego parametru konstruktora. Metoda Greet:

  • Tworzy greeting dany name.
  • Wywołuje metodę WriteLine w wystąpieniu IConsole .
  • Zwraca element greeting do elementu wywołującego.

Klasa DefaultGreetingService pokazuje, że można zaimplementować seal usługi, aby zapobiec dziedziczeniu i użyciu internal w celu ograniczenia dostępu do zestawu.

Ostatnią usługą do utworzenia jest plik FarewellService.cs . Przed kontynuowaniem dodaj następujący kod w języku C#:

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

        console.WriteLine(farewell);

        return farewell;
    }
}

Obiekt FarewellService reprezentuje konkretny typ, a nie interfejs. Należy zadeklarować je jako public, aby było dostępne dla konsumentów. W przeciwieństwie do innych typów implementacji usług zadeklarowanych jako internal i sealedten kod pokazuje, że nie wszystkie usługi muszą być interfejsami.

Program Zaktualizuj klasę

Otwórz plik Program.cs i zastąp istniejący kod następującym kodem 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");

Powyższy kod pokazuje, jak:

  • Utwórz nowe ServiceCollection wystąpienie.
  • Rejestrowanie i konfigurowanie usług w programie ServiceCollection:
    • Usługa IConsole za pomocą przeciążenia fabryki podczas implementacji. Zwróć typ DefaultConsole z właściwością IsEnabled ustawioną na true.
    • Usługa IGreetingService odpowiadająca typowi implementacji DefaultGreetingService.
    • Usługa FarewellService jako konkretny typ.
  • Skompiluj element ServiceProvider z pliku ServiceCollection.
  • Rozwiąż problemy z usługami IGreetingService i FarewellService .
  • Skorzystaj ze świadczonych usług, aby powitać i pożegnać się z osobą o imieniu David.

Jeśli zaktualizujesz właściwość IsEnabled elementu DefaultConsole do false, metody Greet i SayGoodbye pomijają zapisywanie wynikowych komunikatów w konsoli. Ta zmiana pomaga wykazać, że IConsole usługa jest wprowadzana do IGreetingService usług i FarewellService jako zależność , która wpływa na zachowanie aplikacji.

Wszystkie te usługi są rejestrowane jako singletony. W przypadku tego przykładu działa on identycznie, jeśli zarejestrujesz je jako usługi przejściowe lub o określonym zakresie .

Ważne

W tym spisanym przykładzie okresy istnienia usługi nie mają znaczenia. W rzeczywistej aplikacji należy dokładnie rozważyć okres istnienia każdej usługi.

Uruchamianie przykładowej aplikacji

Aby uruchomić przykładową aplikację, naciśnij F5 w programie Visual Studio lub Visual Studio Code albo uruchom dotnet run polecenie w terminalu. Po zakończeniu pracy aplikacji zobaczysz następujące dane wyjściowe:

Hello, David!
Goodbye, David!

Deskryptory usług

Najczęściej używane interfejsy API do dodawania usług do ServiceCollection to ogólne metody rozszerzeń określające czas życia, takie jak:

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

Te metody są wygodnymi funkcjami, które tworzą instancję ServiceDescriptor i dodają ją do ServiceCollection. To ServiceDescriptor jest prostą klasą, która opisuje usługę z jej typem usługi, typem implementacji i okresem istnienia. Może również opisywać fabryki i instancje implementacji.

Dla każdej usługi zarejestrowanej w programie ServiceCollection, można zamiast tego wywołać metodę Add używając wystąpienia ServiceDescriptor bezpośrednio. Rozważ następujące przykłady:

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

Powyższy kod jest równoważny temu, jak IConsole usługa została zarejestrowana w ServiceCollection. Metoda Add dodaje wystąpienie ServiceDescriptor, które opisuje usługę IConsole. Metoda statyczna ServiceDescriptor.Describe deleguje do różnych ServiceDescriptor konstruktorów. Rozważ odpowiedni kod dla IGreetingService usługi:

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

Powyższy kod opisuje usługę IGreetingService z jej typem usługi, typem implementacji i okresem istnienia. Na koniec rozważ odpowiedni kod dla FarewellService usługi:

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

Powyższy kod opisuje konkretny FarewellService typ zarówno usługi, jak i typów implementacji. Usługa jest zarejestrowana jako pojedyncza usługa.

Zobacz także