Бекенд на C# Web Api

To Kaiten

Внедрение зависимостей

В C# WebApi внедрение зависимостей позволяет управлять жизненным циклом сервисов и делает код более гибким и модульным. В ASP.NET Core внедрение зависимостей встроено в фреймворк. Вот основные шаги для внедрения зависимостей:

  • Создание интерфейса и реализации: Создайте интерфейс для сервиса и его реализацию.

public interface IRegistrationService
{
    async Task Register(RegistrationDto data);
}

public class EmailRegistrationService : IRegistrationService
{
    public async Task Register(RegistrationDto data)
    { ... }
}
  • Регистрация зависимостей: Зарегистрируйте сервисы в контейнере зависимостей в методе ConfigureServices в Program.cs.

var builder = WebApplication.CreateBuilder(args);

// Регистрация сервиса с жизненным циклом Singleton, Scoped или Transient
builder.Services.AddSingleton<IMyService, MyService>();
// или
builder.Services.AddScoped<IMyService, MyService>();
// или
builder.Services.AddTransient<IMyService, MyService>();

var app = builder.Build();
  • Использование зависимостей: Теперь вы можете внедрять зависимости в контроллеры через конструктор.

[ApiController]
[Route("api/registration")]
public class RegistrationController : ControllerBase
{
    private readonly IRegistrationService _registrationService;

    // внедрение зависимости в конструктор контроллера
    public MyController(IRegistrationService registrationService)
    {
        _registrationService = registrationService;
    }

    [HttpPost]
    public async Task<IActionResult> Register([FromBody] RegistrationDto data)
    {
        var result = _registrationService.Register(data);
        return Ok(result);
    }
}

public class LoginService
{
    private readonly IRegistrationService _registrationService;

    // внедрение зависимости в конструктор другого сервиса
    public LoginServive(IRegistrationService registrationService)
    {
        _registrationService = registrationService;
    }
}

Отличия Singleton, Scoped и Transient

В ASP.NET Core внедрение зависимостей поддерживает три основных жизненных цикла (время жизни объектов): Singleton, Scoped и Transient. Они определяют, как и когда создаются и уничтожаются объекты, зарегистрированные в контейнере зависимостей.

1. Singleton

  • Экземпляр создается один раз при первом запросе и существует на протяжении всего жизненного цикла приложения. Все последующие запросы к этому сервису будут получать тот же экземпляр.

  • Использование: Когда сервис должен быть доступен во всём приложении и сохранять состояние (например, кэш, общие данные или настройки).

  • Пример использования: Сервис кэширования, логирования или любой другой сервис, который должен быть создан только один раз и использоваться всеми компонентами приложения.

Минусы:

  • Так как объект создается один раз и сохраняется в памяти, он может занимать ресурсы долгое время. Если экземпляр использует много памяти, это может привести к утечкам памяти.

  • Если сервис не потокобезопасен, его использование в нескольких потоках одновременно может вызвать проблемы.

2. Scoped

Экземпляр создается один раз на каждый HTTP-запрос. Это значит, что каждый раз при обработке нового HTTP-запроса создается новый экземпляр зависимости, но в пределах одного запроса все инъекции этой зависимости будут получать один и тот же экземпляр.

  • Использование: Для сервисов, которые должны быть уникальны для каждого запроса, но могут повторно использоваться в рамках одного запроса.

  • Пример использования: Чаще всего используется для сервисов, которые работают с базами данных. Каждый запрос работает с отдельной транзакцией и набором данных, изолированным от других запросов.

Минусы:

  • Не подходит для долгоживущих сервисов, поскольку экземпляры создаются на каждый запрос и могут занимать больше ресурсов при высоком числе одновременных запросов.

3. Transient

Экземпляр создается каждый раз, когда он запрашивается. Это означает, что на каждый вызов зависимости будет создан новый объект.

  • Использование: Для сервисов, которые легковесны и не сохраняют состояние между вызовами. Такие сервисы могут быть быстро созданы и уничтожены.

  • Пример использования: В сценариях, где важна изоляция и каждый запрос должен работать с новым экземпляром (например, для сервисов, выполняющих простые операции, такие как преобразование данных, валидация и т.д.).

Минусы:

  • Если объект создается часто и его создание занимает много ресурсов, это может привести к увеличению времени отклика приложения.

Когда использовать:

  • Singleton:

    • Когда сервис должен существовать в одном экземпляре для всего приложения (например, кэш, логирование, статические данные).

    • Пример: глобальные сервисы кэширования или работы с конфигурацией приложения.

  • Scoped:

    • Когда сервис должен быть уникальным для каждого запроса, но одинаковым в рамках одного запроса.

    • Пример: любые сервисы, связанные с бизнес-логикой, зависящие от текущего запроса.

  • Transient:

    • Когда необходимо создавать новые экземпляры для каждой операции, чтобы не сохранять состояние между вызовами (например, сервисы для форматирования данных или отправки сообщений).

    • Пример: легковесные и stateless сервисы, работающие с краткосрочными задачами, такие как парсеры, валидация и др.

Сравнение:

Жизненный цикл

Время жизни

Общий для всех запросов

Общий для одного запроса

Потокобезопасность требуется?

Singleton

На протяжении всего приложения

Да

Да

Да

Scoped

На протяжении одного запроса

Нет

Да

Не требуется, если запрос изолирован

Transient

Новый экземпляр для каждого вызова

Нет

Нет

Не требуется


Ссылки

Microsoft — Dependency Injection in ASP.NET Web API 2

Code Maze — Dependency Injection in ASP.NET Core

Code Maze — Dependency Injection Lifetimes in ASP.NET Core


Автор документа: Артём Ветик