Как мне добавить экземпляр DbContext в службу IHostedService?

Вопрос

Как я должен вводить (используя стандартную инъекцию зависимостей) экземпляр DbContext в IHostedService?

Что я пробовал

В настоящее время у меня класс IHostedService принимает экземпляр MainContext (вывод из DbContext) в конструкторе.

Когда я запускаю приложение, я получаю:

Нельзя использовать ограниченную службу "Microsoft.EntityFrameworkCore.DbContextOptions" из singleton "Microsoft.Extensions.Hosting.IHostedService".

Итак, я попытался сделать переходный период DbContextOptions, указав:

services.AddDbContext<MainContext>(options => 
                options.UseSqlite("Data Source=development.db"), ServiceLifetime.Transient);

в моем классе Startup.

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

Я не могу сделать контекст базы данных singleton, в противном случае одновременные вызовы к нему приведут к исключениям concurrency (из-за того, что контекст базы данных не гарантируется потоком).

Ответ 1

Хорошим способом использования сервисов внутри размещенных сервисов является создание области, когда это необходимо. Это позволяет использовать контексты службы /db и т.д. С конфигурацией жизненного цикла, с которой они настроены. Не создавая области, теоретически может привести к созданию одноэлементных DbContexts и неправильному использованию контекста (EF Core 2.0 с пулами DbContext).

Чтобы сделать это, введите IServiceScopeFactory и используйте его для создания области, когда это необходимо. Затем разрешите любые зависимости, которые вам нужны из этой области. Это также позволяет регистрировать настраиваемые сервисы в виде зависимых от области действия, если вы хотите переместить логику из размещенной службы и использовать размещенную службу только для запуска некоторой работы (например, для запуска задачи регулярно, это будет регулярно создавать области, создавать службу задач в эта область, которая также получает контекст db, введенный).

public class MyHostedService : IHostedService
{
    private readonly IServiceScopeFactory scopeFactory;

    public MyHostedService(IServiceScopeFactory scopeFactory)
    {
        this.scopeFactory = scopeFactory;
    }

    public void DoWork()
    {
        using (var scope = scopeFactory.CreateScope())
        {
            var dbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();
            …
        }
    }
    …
}

Ответ 2

Спасибо Мартину за ответ - просто небольшое изменение в приведенном выше коде, если появляется эта ошибка:

InvalidOperationException: ни один сервис для типа 'x' не был зарегистрирован

Меняться от:

var dbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();

Для того, чтобы:

var dbContext = (MyDbContext)scope.ServiceProvider.GetRequiredService<IMyDbContext>();