Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym artykule utworzysz aplikację konsolową w .NET, w której ręcznie utworzysz ServiceCollection i odpowiadający mu 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
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).
Rozpocznij
Aby rozpocząć, utwórz nową aplikację konsolową platformy .NET o nazwie DI.Basics. Niektóre z najbardziej typowych podejść do tworzenia projektu konsoli znajdują się na poniższej liście:
- Visual Studio: Plik > Nowy > Projekt меню.
- Program Visual Studio Code i rozszerzenie C# Dev Kit: opcja menu Eksploratora rozwiązań.
-
.NET CLI:
dotnet new console
polecenie w terminalu.
Należy dodać odwołanie do pakietu Microsoft.Extensions.DependencyInjection w pliku projektu. Niezależnie od podejścia upewnij się, że projekt przypomina następujący kod XML pliku 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="9.0.6" />
</ItemGroup>
</Project>
Podstawy wstrzykiwania zależności
Wstrzykiwanie zależności to wzorzec projektowy, który umożliwia usuwanie trwale zakodowanych zależności i zwiększenie możliwości konserwacji i testowania aplikacji. Di jest techniką osiągnięcia inwersji kontroli (IoC) między klasami i ich zależnościami.
Abstrakcje dla DI na platformie .NET są zdefiniowane w pakiecie NuGet Microsoft.Extensions.DependencyInjection.Abstractions.
- IServiceCollection: definiuje kontrakt dla kolekcji deskryptorów usług.
- IServiceProvider: definiuje mechanizm pobierania obiektu usługi.
- ServiceDescriptor: Opisuje usługę z jej typem usługi, implementacją i okresem istnienia.
Na platformie .NET DI jest zarządzane przez dodawanie usług i konfigurowanie ich w programie IServiceCollection
. Po zarejestrowaniu usług instancja IServiceProvider
jest tworzona poprzez wywołanie metody BuildServiceProvider. Działa IServiceProvider
jako kontener wszystkich zarejestrowanych usług i służy do rozwiązywania problemów z usługami.
Tworzenie przykładowych usług
Nie wszystkie usługi są sobie równe. Niektóre usługi wymagają nowego wystąpienia za każdym razem, gdy kontener usługi je pobiera (przejściowe), podczas gdy inne powinny być współużytkowane między żądaniami (o określonym zakresie) lub przez cały czas trwania aplikacji (pojedyncze). 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 języka 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 wyświetla na konsoli w oparciu o właściwość IsEnabled
.
Napiwek
Nazewnictwo implementacji jest wyborem, w jaki 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
na podstawiename
. - Wywołuje metodę
WriteLine
w wystąpieniuIConsole
. - Zwraca element
greeting
do wywołującego.
Ostatnią usługą do utworzenia jest plik FarewellService.cs , przed kontynuowaniem dodaj następujący kod języka 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 je zadeklarować jako public
, aby było dostępne dla konsumentów. W przeciwieństwie do innych typów implementacji usług, które zostały zadeklarowane jako internal
i sealed
, ten kod pokazuje, że nie wszystkie usługi muszą być interfejsami. Pokazuje również, że implementacje usług mogą sealed
uniemożliwić dziedziczenie i internal
ograniczać dostęp do zestawu.
Program
Aktualizuj 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 zaktualizowany kod przedstawia instrukcje:
- Utwórz nowe
ServiceCollection
wystąpienie. - Rejestrowanie i konfigurowanie usług w programie
ServiceCollection
:-
IConsole
przy użyciu przeciążenia fabryki implementacji zwraca typDefaultConsole
zIsEnabled
ustawionym natrue
. - Element
IGreetingService
jest dodawany z odpowiednim typem implementacjiDefaultGreetingService
typu. - Element
FarewellService
jest dodawany jako konkretny typ.
-
- Skompiluj element
ServiceProvider
z plikuServiceCollection
. - Rozwiąż problemy z usługami
IGreetingService
iFarewellService
. - Skorzystaj z dostępnych usług, aby powitać i pożegnać osobę o imieniu
David
.
Jeśli zaktualizujesz IsEnabled
właściwość DefaultConsole
na false
, metody Greet
i SayGoodbye
pomijają zapisywanie wynikowych komunikatów do konsoli. Taka zmiana pomaga pokazać, ż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, chociaż w tym przykładzie działają identycznie, jeśli zostały zarejestrowane jako przejściowe lub o określonym zakresie.
Ważne
W tym spisanym przykładzie okresy istnienia usługi nie mają znaczenia, ale 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, Visual Studio Code lub uruchom dotnet run
polecenie w terminalu. Po zakończeniu pracy aplikacji powinny zostać wyświetlone następujące dane wyjściowe:
Hello, David!
Goodbye, David!
Opis usług
Najczęściej używane interfejsy API do dodawania usług do ServiceCollection
to generyczne metody rozszerzenia związane z okresem istnienia, takie jak:
AddSingleton<TService>
AddTransient<TService>
AddScoped<TService>
Te metody są wygodne, ponieważ tworzą instancję ServiceDescriptor i dodają ją do ServiceCollection
. Jest ServiceDescriptor
to prosta klasa, która opisuje usługę z jej typem usługi, typem implementacji i okresem istnienia. Może również opisywać fabryki i egzemplarze implementacji.
Dla każdej z usług zarejestrowanej w ServiceCollection
, można zamiast tego bezpośrednio wywołać metodę Add
z wystąpieniem ServiceDescriptor
. 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 odpowiednikiem sposobu zarejestrowania usługi IConsole
w kontekście ServiceCollection
. Metoda Add
służy do dodawania wystąpienia ServiceDescriptor
opisującego 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.