Срок службы службы ядра Entity Framework по умолчанию

В приложении ASP.NET Core я могу зарегистрировать DbContext через DI, как это

services.AddDbContext<Models.ShellDbContext>(options => options.UseNpgsql(connection));

И интересно знать, какова его продолжительность жизни?

Отсюда https://github.com/aspnet/EntityFramework/blob/f33b76c0a070d08a191d67c09650f52c26e34052/src/Microsoft.EntityFrameworkCore/EntityFrameworkServiceCollectionExtensions.cs#L140 выглядит так, как будто он создан, выглядит так, как будто он настроен на DB.

Итак, первая часть вопроса: правда ли это, и если да, то насколько это дорого?

И вторая часть: если я создаю сервис, который использует DbContext и предназначен для использования контроллерами, и будет иметь API для управления некоторыми объектами в БД, должен ли он быть также зарегистрирован как Scoped?

2 ответа

Решение

Да, время жизни по умолчанию для DbContext находится в области видимости Это предназначено таким образом.

Инстанцирование DbContext это довольно дешево, и это гарантирует, что вы не используете много ресурсов. Если бы у вас был DbContext с единственным временем жизни, все записи, которые вы прочитали один раз, будут отслеживаться DbContext, если вы специально не отключите отслеживание. Это потребует гораздо большего использования памяти и будет продолжать расти.

И чем больше тем DbContext треки, тем ниже будет производительность. Вот почему вы часто видите DbContext используется только в using(var context = new AppDbContext()) блок.

Однако в веб-приложениях, используя using Блок плох, потому что время жизни управляется платформой, и если вы настроите его на ранние вызовы, после этого произойдет сбой с исключением.

Если вы используете временное время жизни на другой стороне, вы потеряете функциональность "транзакции". С областью, DbContext имеет объем транзакции, равный запросу.

Если вам нужен более детальный контроль, вы должны использовать шаблон единиц работы (который DbContext уже вроде как использую).

На ваш второй вопрос:

Если вы создаете сервис, у него должен быть срок службы, равный сроку службы или меньше (читай: Scoped или transient).

Если вам явно требуется более длительный срок службы, вы должны ввести DbContext Фабричный сервис или фабричный метод к вашим услугам.

Вы можете сделать это с помощью чего-то вроде

services.AddTransient<Func<AppDbContext>>( (provider) => new Func<MyDbContext>( () => new AppDbContext()));
services.AddSingleton<IMySingletonService, MySingletonService>();

И ваш сервис может выглядеть так:

public class MySingletonService : IMySingletonService, IDisposable
{
    private readonly AppDbContext context;

    public MySingletonService(Func<AppDbContext> contextFactory)
    {
        if(contextFactory == null)
            throw new ArgumentNullException(nameof(contextFactory));

        // it creates an transient factory, make sure to dispose it in `Dispose()` method.
        // Since it's member of the MySingletonService, it's lifetime
        // is effectively bound to it. 
        context = contextFactory();
    }
}

Примечание: в EF Core 2 появился новый метод AddDbContextPool который создает пул контекстов, которые можно использовать повторно. Область действия остается прежней, но экземпляр будет "сброшен" и возвращен в пул. Я бы подумал, что издержки "сброса" будут такими же, как и просто создание нового, но я думаю, что это не так.

Если этот метод используется, в то время, когда контроллер запрашивает экземпляр DbContext, мы сначала проверим, есть ли экземпляр в пуле. Как только обработка запроса завершается, любое состояние экземпляра сбрасывается, а сам экземпляр возвращается в пул.

Концептуально это похоже на то, как работает пул соединений в провайдерах ADO.NET, и имеет преимущество, заключающееся в экономии некоторых затрат на инициализацию экземпляра DbContext.

https://docs.microsoft.com/en-us/ef/core/what-is-new/

По общему признанию, это - то, что я изучил. И это от ленивой преданности KISS. Я избежал других сложностей и решил свою проблему преждевременного EF Core. DbContext избавляется от проблем с пулами, областями и т. Д. Я просто собирал POC без учета асинхронных возвращаемых значений, прикованный цепью от контроллера к репозиторию и т. Д. на котором все по сути вернули "пустоту", т. е. буквально в единственном числе, "задачу". Это заставило итерации среди моих членов DbContext в более низких подпрограммах необъяснимым образом избавиться от базового DbContext. Все, что мне нужно было сделать, это заставить каждый асинхронный метод возвращать Task<whatever return> ценность и все работало. EF Core не любит асинхронные возвращаемые значения.

Другие вопросы по тегам