Подскажите, а является ли нормальной практикой плодить контексты using при работе с Enitity Framework или правильнее передавать объекты сущностей в методы в качестве аргументов?
Вот это я подразумеваю под "Плодить контексты":
DoWork()
{
using (var db = new Entity())
{
// какая-то работа с сущностью
...
DoWork2();
...
// какая-то работа с сущностью
}
}
DoWork2()
{
using (var db = new Entity())
{
// какая-то работа с сущностью
...
}
}
У многих, кто использует EF, возникает вопрос, как часто мы должны создавать DbContext
, как определить оптимальное время жизни контекста.
Возможные варианты времени жизни контекста:
Чтобы сделать правильный выбор, предлагаю рассмотреть несколько аспектов:
Правильное разрушение контекста (вызов метода Dispose()
контекста)
Вызов метода
Dispose является важным шагом при работе с классом DbContext
. Используя
конструкцию using
, гарантируетcя своевременный вызов метода Dispose
и тем самым
освобождение неуправляемых ресурсов.
using (MyContext ctx = new MyContext())
{
…
}
Если забыть вызвать Dispose
, то это может привести к утечке открытых соединений к БД и к неосвобождению неуправляемых ресурсов.
do-i-have-to-call-dispose-on-dbcontext
Стоимость операции создания нового класса контекста
Операция создания нового контекста недорогая, потому что это, в основном, копирование ссылок метаданных из глобального кеша. Поэтому за стоимость этой операции не стоит беспокоиться.
Использование памяти
Чем дольше вы используете объект DbContext
, тем больше памяти он занимает. Потому что он будет содержать все сущности (entity), о которых он знает, которые были запрошены через запросы, были добавлены либо присоединены. Поэтому держать долго контекст не рекомендуется.
DbContext
не является потокобезопасным. Если мы используем контекст из нескольких потоков, то мы сами должны обеспечить сихронизацию доступа к контексту. Вот основные аспекты, которые пришли в голову. Поэтому контексты с коротким временем жизни при работе с EF являются нормальной практикой и часто используются при работе с EF.
Context в EF, это, по сути, готовая реализация сразу нескольких паттернов:
Последние два существенно ограничивают возможные варианты времени жизни контекста:
Unit of Work
Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.
Identity Map
An Identity Map keeps a record of all objects that have been read from the database in a single business transaction. Whenever you want an object, you check the Identity Map first to see if you already have it.
Из двух цитат выше напрямую следует что контект должен жить ровно одну бизнес-операцию:
Что такое бизнес-операция (бизнес-транзакция) - зависит от конкретной предметной области. Это не транзакция в терминах баз данных, а более широкое понятие - некое целостное действие в терминах предметной области.
Обычно это вызов кода сервиса / фасада в BL - DoWork
, где Work - это какая-то цельная операция, действие. Т.е. обычно это вызов функции верхнего уровня в вашем приложении.
Соображения использования памяти, потокобезопасности, стоимости создания, синхронизации с базой данных - полностью вторичны, и не должны служить основанием для выбора времени жизни контекста.
Контекст - это попытка отразить в коде цельную операцию из предметной области. Предметной области все равно, сделаете вы это в одном потоке или в разных, потратите при этом много памяти или мало, придется ли вам при этом писать огромные методы, или обойдеться парой строк - это никак не влияет на целостность операции. Соответственно, и на выбор времени жизни контекста влиять не должно.
Выбор контейнера в коде, в рамках которого живет контектст - функция, форма, поток, запрос, приложение - тоже вторичен. Он должне быть следствием, а не причиной времени жизни контекста.
DoWork
) соответствует бизнес-операции - она должна контролировать время жизни контекста. Если нет (DoWork2
) - то нет.Ну а с другой стороны: Если загрузка каких-то объектов из базы и их изменение с сохранением происходит в разных местах приложения и еще и по инициативе пользователя, т.е. может быть значительно разнесено по времени - то придётся каждый раз при создании контекста снова загружать нужные объекты, например используя метод Find с сохраненным ранее ключом и работать уже с обновленными объектами. Если же контекст держать открытым, то этого не потребуется. Только нужно будет поработать над гарантированным закрытием этого долгоживущего контекста как только он перестанет быть нужным. Наверно тут нужен компромис - может быть кэш с выгрузкой контекста (контекстов) по времени, хотя это не проще, чем многократная загрузка объектов, но производительнее.
Есть задача сортировать пользователей по дате последнего входа в систему
Я получаю данные из JSON-файла в файле actionsjs и устанавливаю их в reducer sections: