Получить несколько экземпляров DbContext из IServiceProvider

Вопрос или проблема

Я пытаюсь упростить свои области внедрения зависимостей в проекте интеграционных тестов ASP.NET Core. В данный момент я создаю три области: одну для только для чтения DbContext, одну для только для записи DbContext и одну для всего остального. Мне действительно хотелось бы упростить это до одной области, но проблема в том, что мне нужны два отдельных экземпляра DbContext, чтобы я мог выполнять независимые операции чтения и записи в рамках тестов.

Возможно ли запрашивать разные экземпляры сервиса, используя IServiceProvider, при использовании GetRequiredServices<T>()?

Ответ или решение

Чтобы ответить на ваш вопрос о получении нескольких экземпляров DbContext из IServiceProvider в проекте интеграционных тестов ASP.NET Core, важно понимать, как работает внедрение зависимостей в .NET и как корректно настроить области видимости.

Формулировка Проблемы

Вы сталкиваетесь с необходимостью в одном скоупе иметь два различных экземпляра DbContext: один для операций чтения и другой для операций записи. Это связано с тем, что в некоторых ситуациях необходимо производить независимые операции без накладок на состояние контекста базы данных.

Решение через IServiceProvider

В ASP.NET Core внедрение зависимостей реализовано таким образом, что по умолчанию запросы к контейнеру возвращают единственный экземпляр каждого сервиса, если он зарегистрирован как синглтон или транзиент. Однако, вы можете изменить это поведение для получения нескольких экземпляров DbContext.

  1. Регистрация DbContext: Убедитесь, что ваш DbContext зарегистрирован как Scoped в вашем Startup.cs. Это наиболее распространенная практика, которая позволяет создавать новый экземпляр для каждого запроса.

    services.AddDbContext<MyDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
  2. Использование IServiceScope: В интеграционных тестах можно создать отдельные области для управления жизненным циклом ваших DbContext. Для этого вам необходимо создать скоуп и запрашивать экземпляры DbContext из него.

    using (var scope = serviceProvider.CreateScope())
    {
        var dbContextForRead = scope.ServiceProvider.GetRequiredService<MyDbContext>();
    
        using (var writeScope = serviceProvider.CreateScope())
        {
            var dbContextForWrite = writeScope.ServiceProvider.GetRequiredService<MyDbContext>();
    
            // Здесь вы можете использовать dbContextForRead для операций чтения
            // и dbContextForWrite для операций записи
        }
    }

Пример Кода

Следующий пример иллюстрирует, как можно реализовать это в коде ваших интеграционных тестов:

public class MyIntegrationTests
{
    private readonly IServiceProvider _serviceProvider;

    public MyIntegrationTests(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    [Fact]
    public void TestDatabaseOperations()
    {
        using (var readScope = _serviceProvider.CreateScope())
        {
            var readDbContext = readScope.ServiceProvider.GetRequiredService<MyDbContext>();
            // Выполнение операций чтения
        }

        using (var writeScope = _serviceProvider.CreateScope())
        {
            var writeDbContext = writeScope.ServiceProvider.GetRequiredService<MyDbContext>();
            // Выполнение операций записи
        }
    }
}

Заключение

Данный подход позволяет вам организовать тестирование с использованием двух отдельных экземпляров DbContext в одном скоупе, что значительно упростит вашу архитектуру и уберёт необходимость в управлении несколькими скоупами. Используя IServiceProvider и создавая новые скоупы, вы получаете необходимую гибкость для проведения независимых операций ввода-вывода, что важно для качественных интеграционных тестов.

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

Оцените материал
Добавить комментарий

Капча загружается...