Просто чтобы добавить некоторый дополнительный контекст на случай, если кто-нибудь столкнется с этим вопросом (включая вашего инструктора :)
Преподаватель может связать единственное время «разогрева», необходимое для первого доступа к DbContext, кразрешить его сопоставления со стоимостью для всех экземпляров DbContext, но EF выполняет эту операцию один раз для процесса. (статический) Как только это произойдет, создание и уничтожение DbContexts будут дешевыми.
Я не могу думать ни о причине, ни о реальном сценарии, который я бы когда-либо рассматривал или рекомендовал статический или долгоживущий DbContext. Кто-то может поспорить, что DbContext с его кеш-запросом будет занимать больше ресурсов на веб-сервере, чем один общий экземпляр, но недолговечные экземпляры должны только кэшировать / отслеживать объекты, необходимые для обслуживания этого запроса, а затем утилизировать. (много маленьких против одного большого аргумента)
Одиночные DbContexts приносят значительные проблемы, особенно в ASP.Net. DbContexts не являются поточно-ориентированными, что приводит к проблемам отслеживания сущностей между потоками, и ASP.Net будет использовать потоки для ответа на запросы. Чем дольше контекст жив, тем больше становится его кеш. Некоторые могут возразить, что cache = fast, но чем больше становится ваш кеш, тем больше времени EF тратит на проверку ссылок на результаты поиска. Это связывает ваш сервер приложений, а не делит нагрузку с сервером базы данных для получения свежих данных по требованию. (это может быть асинхронным / ожидаемым сервером приложений для возврата к обработке запросов) EF не выполняет устаревшие проверки своего кэша, поэтому долгоживущие кэши обслуживают все большие объемы устаревших данных, которые могут быть обновлены другими способами. Это также может привести к непоследовательному поведению при доступе к связанным коллекциям сущностей. Для сущностей, которые загружены без загруженных коллекций, EF все равно будет проходить через все кэшированные сущности и связывать любые, о которых он знает. Это требует времени и может привести к возвращению неполных данных.
Например, если у нас был объектный граф, состоящий из:
Parent_1
- Child_1
- Child_2
- Child_3
, то мы сделали что-то подобное из относительно свежего состояния данных:
var child = _staticContext.Children.Single(x => x.ChildId == 1);
затем позже сделал:
var parent = _staticContext.Parents.Single(x => x.ParentId == 1);
Пока ленивая загрузка включена (и коллекция Parent.Children является виртуальной), тогда это должно быть хорошо и parent.Children. Счет будет равен 3 после ленивого вызова нагрузки. Однако, если отложенная загрузка отключена или коллекция не является виртуальной, то в этом случае parent.Children.Count вернется с «1», а не с ожидаемым «0», где не было указано или ожидается отложенная или отложенная загрузка, а не«3», которая представляет реальные данные. Это может привести к разным результатам, появляющимся между запусками, или между отладкой и производством, и ошибками, если код настроен на загрузку с нетерпением (отложенная загрузка отключена), и кто-то забывает Include
.
Этот последнийможет быть немного странным при работе с обновлениями из разных процессов. Например, если у меня есть тест, который делает это:
// Short-lived contexts for demonstration /w lazy loading implemented:
using (var context = new TestDbContext()) // represents our long-lived, static context...
{
var child = context.Children.Single(x => x.ChildId == 2);
Assert.AreEqual("Sean", child.Name);
var parent = context.Parents.AsNoTracking().Single(x => x.ParentId == 1);
using(var innerContext = new TestDbContext()) // a short-lived context somewhere in a call stack, or an external process modifying data...
{
var sean = innerContext.Children.Single(x => x.ChildId == 2);
sean.Name = "Shawn";
innerContext.SaveChanges();
}
Console.Writeline(child.Name);
var parentsChild = parent.Children.Single(x => x.ChildId == 2); // Lazy load gets triggered here.
Console.Writeline(parentsChild.Name);
Console.Writeline(child.Name);
}
Что вы ожидаете увидеть на выходе? «Шон» или «Шон»?
При отложенной загрузке в первой строке будет напечатано «Шон», а во второй - «Шон», хотя в долгоживущем контексте хранится кэшированная копия в памяти. ,Как насчет 3-й строки? Конечно, теперь, когда EF вызвал ленивую загрузку для дочернего элемента # 2, и у него есть отслеживаемый экземпляр для объекта с таким идентификатором, он будет обновлен? Нет. "Шон" еще. Теперь еще интереснее то, что child
и parentsChild
представляют одну и ту же дочернюю запись в базе данных (ID ребенка № 2) child
говорит, что его зовут "Шон", а parentsChild
говорит, что его зовут "Шон». Обычно EF не допускает множественных ссылок на одну и ту же сущность. Что произойдет, если мы добавим:
child.Name = "Fred";
context.SaveChanges();
Как насчет:
parentsChild.Name = "Ginger";
context.SaveChanges();
Как насчет:
child.Name = "Fred";
parentsChild.Name = "Ginger";
context.SaveChanges();
Что происходит в этих случаях, обновляется ли, EF создает исключение для нескольких отслеживаемых экземпляров? В статических контекстах отслеживаемые экземпляры можно изменять везде и в любом месте области приложения, а SaveChanges можно также вызывать где угодно и везде, вызывая сохранение всех измененных отслеживаемых экземпляров. Это может означать, что недопустимые / неполные изменения, предпринятые где-либо, могут загрязнить отслеживание изменений контекста, вызывая неожиданные сбои других вызовов SaveChanges в других областях без причины сбоя для отслеживания в точке сбоя.
В ASP. Нет, DbContext должен жить не дольше, чем запрос, и даже короче, например, для единицы работы для фиксации связанных изменений. (подумайте о транзакции) Даже в приложениях Windows DbContext должен жить только достаточно долго, чтобы обслуживать запрошенные данные или выполнять действия высокого уровня.
Статический / долгоживущий DbContexts? в двух словах, просто не делай этого. :)