Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
В этом быстром старте вы создадите консольное приложение .NET, которое вручную создает ServiceCollection и соответствующий ServiceProvider. Вы узнаете, как зарегистрировать службы и устранить их с помощью внедрения зависимостей (DI). В этой статье используется пакет NuGet Microsoft.Extensions.DependencyInjection, чтобы продемонстрировать основы DI в .NET.
Замечание
В этой статье не используются функции общего хоста. Более подробное руководство см. в статье Об использовании внедрения зависимостей в .NET.
Начало работы
Чтобы приступить к работе, создайте консольное приложение .NET с именем DI.Basics. В Visual Studio выберите файл > нового > проекта или с помощью .NET CLI, введите dotnet new console.
Затем добавьте ссылку на пакет Microsoft.Extensions.DependencyInjection в файл проекта. После добавления пакета убедитесь, что проект похож на следующий XML-файл 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>
Основы внедрения зависимостей
Внедрение зависимостей — это шаблон конструктора, который можно использовать для удаления жестко закодированных зависимостей и повышения удобства обслуживания и тестирования приложения. DI — это метод достижения инверсии управления (IoC) между классами и их зависимостями.
Пакет NuGet Microsoft.Extensions.DependencyInjection.Abstractions определяет абстракции для DI в .NET:
- IServiceCollection: определяет контракт для коллекции дескрипторов служб.
- IServiceProvider: определяет механизм получения объекта службы.
- ServiceDescriptor: описывает службу со своим типом службы, реализацией и временем существования.
В .NET вы управляете di, добавляя службы и настраивая их в IServiceCollection.NET. После регистрации служб вызовите метод BuildServiceProvider для создания экземпляра IServiceProvider. Он IServiceProvider выступает в качестве контейнера для всех зарегистрированных служб и используется для разрешения служб.
Создание примеров служб
Не все услуги создаются одинаково. Для некоторых служб требуется новый экземпляр каждый раз, когда контейнер службы запрашивает их (транзитный), в то время как другие должны быть общими для всех запросов (исполнение на уровне запроса) или в течение всего времени работы приложения (одиночный). Дополнительные сведения о времени существования службы см. в разделе "Время существования службы".
Аналогичным образом, некоторые службы предоставляют только конкретный тип, а другие выражаются как контракт между интерфейсом и типом реализации. Вы создаете несколько вариантов служб, которые помогут продемонстрировать эти понятия.
Создайте файл C# с именем IConsole.cs и добавьте следующий код:
public interface IConsole
{
void WriteLine(string message);
}
Этот файл определяет IConsole интерфейс, предоставляющий один метод WriteLine. Затем создайте новый файл C# с именем DefaultConsole.cs и добавьте следующий код:
internal sealed class DefaultConsole : IConsole
{
public bool IsEnabled { get; set; } = true;
void IConsole.WriteLine(string message)
{
if (IsEnabled is false)
{
return;
}
Console.WriteLine(message);
}
}
Предыдущий код представляет реализацию IConsole интерфейса по умолчанию. Метод WriteLine записывает в консоль в зависимости от свойства IsEnabled.
Подсказка
Выбор именования реализации — это то, о чем ваша команда разработчиков должна прийти к соглашению. Префикс Default — это общее соглашение, указывающее реализацию интерфейса по умолчанию , но это не обязательно.
Затем создайте файл IGreetingService.cs и добавьте следующий код C#:
public interface IGreetingService
{
string Greet(string name);
}
Затем добавьте новый файл C# с именем DefaultGreetingService.cs и добавьте следующий код:
internal sealed class DefaultGreetingService(
IConsole console) : IGreetingService
{
public string Greet(string name)
{
var greeting = $"Hello, {name}!";
console.WriteLine(greeting);
return greeting;
}
}
Предыдущий код представляет реализацию IGreetingService интерфейса по умолчанию. Для реализации службы требуется параметр IConsole в качестве основного конструктора. Метод Greet:
- Создает
greeting, основываясь наname. - Вызывает метод
WriteLineна экземпляреIConsole. - Возвращает
greetingвызывающему.
Класс DefaultGreetingService демонстрирует, что реализации службы позволяют seal предотвратить наследование и использовать internal для ограничения доступа к сборке.
Последний сервис, который нужно создать, — это файл FarewellService.cs. Добавьте следующий код C# перед продолжением:
public class FarewellService(IConsole console)
{
public string SayGoodbye(string name)
{
var farewell = $"Goodbye, {name}!";
console.WriteLine(farewell);
return farewell;
}
}
FarewellService представляет собой конкретный тип, а не интерфейс. Вы должны объявить его как public, чтобы сделать его доступным для потребителей. В отличие от других типов реализации служб, объявленных как internal и sealed, этот код демонстрирует, что не все службы должны быть интерфейсами.
Program Обновление класса
Откройте файл Program.cs и замените существующий код следующим кодом 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");
Приведенный выше код демонстрирует, как:
- Создайте новый
ServiceCollectionэкземпляр. - Регистрация и настройка служб в
ServiceCollection:- Служба
IConsoleс помощью перегрузки фабрики реализации. Вернуть типDefaultConsoleс установленным свойствомIsEnabledнаtrue. -
IGreetingServiceслужба с соответствующим типом реализацииDefaultGreetingService. -
FarewellServiceСервис как конкретный тип.
- Служба
- Сборка
ServiceProviderизServiceCollection. -
IGreetingServiceРазрешение служб иFarewellServiceслужб. - Используйте разрешенные службы для приветствия и прощания с человеком по имени
David.
Если вы обновите свойство IsEnabled объекта DefaultConsole на false, методы Greet и SayGoodbye не записывают полученные сообщения в консоль. Это изменение помогает продемонстрировать, что IConsole служба внедряется в IGreetingService и FarewellService службы как зависимость, определяющая поведение приложения.
Все эти службы регистрируются как одноэлементные. В этом примере он работает так же, как и при регистрации в качестве временных или ограниченных служб.
Это важно
В этом искусственно созданном примере время существования службы не имеет значения. В реальном приложении тщательно рассмотрите время существования каждой службы.
Запуск примера приложения
Чтобы запустить пример приложения, нажмите клавишу F5 в Visual Studio или Visual Studio Code или выполните dotnet run команду в терминале. После завершения приложения вы увидите следующие выходные данные:
Hello, David!
Goodbye, David!
Дескрипторы служб
Наиболее распространённые API для добавления служб в ServiceCollection — это обобщённые методы расширения, названные по времени жизни, такие как:
AddSingleton<TService>AddTransient<TService>AddScoped<TService>
Эти удобные методы создают экземпляр ServiceDescriptor и добавляют его в ServiceCollection. Это ServiceDescriptor простой класс, описывающий службу со своим типом службы, типом реализации и временем существования. Он также может описать фабрики и экземпляры реализации.
Для каждой службы, которую вы зарегистрировали в ServiceCollection, можно вместо этого напрямую вызвать метод Add с экземпляром ServiceDescriptor. Рассмотрим следующие примеры:
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(IConsole),
implementationFactory: static _ => new DefaultConsole
{
IsEnabled = true
},
lifetime: ServiceLifetime.Singleton));
Предыдущий код эквивалентен тому, как служба IConsole была зарегистрирована в ServiceCollection. Метод Add добавляет ServiceDescriptor экземпляр, описывающий IConsole службу. Статический метод ServiceDescriptor.Describe передает выполнение различным конструкторам ServiceDescriptor. Рассмотрим эквивалентный код для IGreetingService службы:
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(IGreetingService),
implementationType: typeof(DefaultGreetingService),
lifetime: ServiceLifetime.Singleton));
Приведенный IGreetingService выше код описывает службу со своим типом службы, типом реализации и временем существования. Наконец, рассмотрим эквивалентный код для FarewellService службы:
services.Add(ServiceDescriptor.Describe(
serviceType: typeof(FarewellService),
implementationType: typeof(FarewellService),
lifetime: ServiceLifetime.Singleton));
Приведенный выше код описывает конкретный FarewellService тип как службы, так и типы реализации. Служба зарегистрирована как одноэлементная служба.