Контекст объекта Entity Framework в объекте сеанса ASP.NET?

У нас есть многоуровневое приложение Asp.NET Web Forms. Уровень данных имеет класс с именемDataAccess какие препятствияIDisposable и имеет экземпляр нашего Объектного контекста Entity Framework в качестве частного поля. Класс имеет несколько открытых методов, возвращающих различные коллекции сущностей, и будет располагать свой объектный контекст при его удалении.

Из-за ряда проблем, с которыми мы столкнулись, мы решили, что было бы большим плюсом сохранить контекст объекта (или экземплярDataAccess) по объему дольше на сервере. Было предложено сохранить экземпляр вHttpContext.Current.Items коллекция изэта почта чтобы иметь один экземпляр на запрос Http.

Что мне интересно, так это: какие проблемы / проблемы / проблемы могут возникнуть при хранении экземпляра нашего Объектного контекста вHttpContext.Current.Session объект ????

Я предполагаю, что объект Session будет завершен и настроен на сборку мусора по истечении сеанса пользователя, поэтому экземпляр будет расположен правильно.Я предполагаю, что большинство настроек браузера по умолчанию позволят нашему приложению размещать свой файл cookie SessionId без каких-либо проблем.Объем данных, с которыми будет работать объектный контекст, невелик и не представляет проблемы для нашего достойного серверного оборудования, что касается кэширования во времени и относительно небольшого числа одновременных пользователей.

Это будет относительно быстро реализовать и не повлияет на наши многочисленные существующие модульные тесты.

Мы будем использовать AutoFac и класс ServiceProvider для предоставления экземпляров. Когда требуется экземпляр ObjectContext, он будет возвращен кодом, подобным следующему:

private static Entities GetEntities(IContext context)
{
    if (HttpContext.Current == null)
    {
        return new Entities();
    }

    if (HttpContext.Current.Session[entitiesKeyString] == null)
    {
        HttpContext.Current.Session[entitiesKeyString] = new Entities();
    }

    return (Entities)HttpContext.Current.Session[entitiesKeyString];
}

Приветствия.

Ответы на вопрос(2)

Решение Вопроса

ObjectContext в состоянии сеанса это не то, что я считаю хорошей практикой, поскольку этот класс предназначен для инкапсуляции шаблона единицы работы - вы загружаете некоторые данные (сущности), изменяете их, фиксируете свои изменения (которые отслеживаются UOW), и тогда вы закончите с этим. Объекты UOW не предназначены или не предназначены для длительного проживания.

Тем не менее, этоМожно быть сделано без каких-либо серьезных катастроф, вы просто должны убедиться, что вы понимаете, что происходит за кулисами.Пожалуйста, прочитайте дальше если вы планируете делать это так, чтобы вы знали, во что вы ввязываетесь, и знали о компромиссах.

Я предполагаю, что объект Session будет завершен и настроен на сборку мусора по истечении сеанса пользователя, поэтому экземпляр будет расположен правильно.

Это на самом деле неточно или, по крайней мере, кажется, основано на том, как оно сформулировано. Окончание сеанса / выход из системы не приведет к немедленному удалению каких-либо элементов. Они будутв конце концов быть завершенным / утилизированным, но это зависит от сборщика мусора, и вы не можете контролировать, когда это произойдет. Самая большая потенциальная проблема здесь, если вы случайно открываете соединение наObjectContext, которая не будет закрыта автоматически - если вы не будете осторожны, вы можете получить утечку соединений с базой данных, что не удалось бы обнаружить с помощью регулярных модульных тестов / интеграционных тестов / живых тестов.

Объем данных, с которыми будет работать объектный контекст, невелик и не представляет проблемы для нашего достойного серверного оборудования, что касается кэширования во времени и относительно небольшого числа одновременных пользователей.

Просто имейте в виду, что рост неограничен. Если конкретный пользователь решит использовать ваш сайт в течение 12 часов подряд, выполняя различные запросы в течение всего дня, тогда контекст будет продолжать расти.ObjectContext не имеет собственной внутренней «сборки мусора», он не удаляет кэшированные / отслеживаемые объекты, которые не использовались в течение длительного времени. Если вы уверены, что это не будет проблемой в зависимости от ваших вариантов использования, тогда все в порядке, но главное, что должно вас беспокоить, это тот факт, что вам не хватаетконтроль над ситуацией.

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

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

Если вы знаете обо всех этих проблемах и предприняли шаги для их устранения, хорошо. Но мой вопрос будет, почему именно вы думаете, что на уровне сессииObjectContext такая отличная идея? СозданиеObjectContext действительно очень дешевая операция, потому что метаданные кэшируются для всего AppDomain. Держу пари, что у вас либо ошибочное впечатление, что это дорого, либо вы пытаетесь реализовать сложные процессы с отслеживанием состояния на нескольких разных веб-страницах, и долгосрочные последствия последних намного хуже, чем какие-либо конкретные вред, который вы можете сделать, просто положивObjectContext в сессию.

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

Обновить - для всех, кто рассматривает возможность отказа от голосования, потому что «множественные запросы в одном сеансе могут вызвать проблемы с безопасностью потоков», пожалуйста, прочтите нижнюю частьОбзор состояния сеанса ASP.NET документация. Это не простоиндивидуальный доступ состояния сеанса, которые сериализуются; любой запрос, который получает сеанс, сохраняет исключительную блокировку сеанса, который не освобождается до тех пор, пока весь запрос не будет выполнен. За исключением некоторых оптимизаций, которые я перечислил выше, в конфигурации по умолчанию невозможно, чтобы когда-либо существовало два одновременных запроса, содержащих ссылки на один и тот же локальный экземпляр сеансаObjectContext.

Я все еще не буду хранитьObjectContext в состоянии сеанса по нескольким причинам, перечисленным выше, но это не проблема безопасности потока, если вы не сделаете это.

 Aaronaught05 мар. 2010 г., 18:04
Для пояснения: сериализуется не просто доступ к состоянию сеанса, а весьзапрос это сериализовано. Это принципиальная разница. Второй запрос не может получить доступ к состоянию сеанса, пока первый запрос не имеетполностью завершено выполнение, HTTP-запросы в одном и том же сеансе сериализуются (фактически однопоточны), если они вообще используют сеанс.
 Steven06 мар. 2010 г., 18:21
Я должен извиниться. Ваш длинный ответ на самом деле очень точный, и вы очень четко и правильно изложите все недостатки использования сеанса ObjectContext, и я полностью согласен с вами в этом. В своем ответе вы уже заявили о проблемах безопасности потоков при изменении настроек по умолчанию, поэтому мой первый комментарий следует игнорировать ...
 Aaronaught05 мар. 2010 г., 17:57
@ Steven: Пожалуйста, прочитайте документацию MSDN о состоянии сеанса здесь:msdn.microsoft.com/en-us/library/ms178581.aspx Он говорит, и я цитирую: «... если два одновременных запроса сделаны для одного и того же сеанса (с использованием одного и того же значения SessionID), первый запрос получает эксклюзивный доступ к информации о сеансе. Второй запрос выполняется только после первого запроса закончен." Пожалуйста, удалите понижение, здесь нет никакого риска, если оптимизация программиста (которую я ясно обозначил) не нарушит эту исключительность.
 Aaronaught07 мар. 2010 г., 02:19
Это на самом деле очень хороший момент; Я не хочу, чтобы люди просто читали первые две строки и воспринимали это как одобрение практики. Я только хотел прояснить распространенное заблуждение, что оно полностью взорвет приложение; Это определенно серая область, насколько хорошие практики идут, и я собираюсь сделать это более ясным.
 andrej35116 авг. 2011 г., 07:50
Просто вернулась к этому после всего этого времени, еще раз спасибо за блестящий ответ!
 Steven05 мар. 2010 г., 17:55
Ваше утверждение о безопасности потоков неверно. ObjectContext действительно не является поточно-ориентированным, и, хотя доступ к сеансу сериализуется, вполне возможно, что два разных запроса от одного и того же пользователя одновременно используют один и тот же экземпляр. Вы можете столкнуться с ситуацией, когда первый запрос вызывает SaveChanges сразу после того, как вы проверили состояние ваших сущностей, а второй изменяет некоторую сущность. Это может привести к всевозможным ужасным вещам, таким как недопустимое состояние в базе данных и полностью поврежденный экземпляр ObjectContext.
 Steven06 мар. 2010 г., 18:22
... Тем не менее, я думаю, к сожалению, вы решили начать со слов "все в порядке, чтобы ObjectContext оставался в состоянии сеанса". Мне скорее понравилось видеть, как вы пишете: «Нет, держать ObjectContext в состоянии сеанса опасно, если вы не понимаете следующие компромиссы». Это лучше подытожит ваш ответ. Опасность заключается в том, что разработчики могут просто прочитать первый абзац, если вы заявите «все в порядке». Если честно, вот почему я проголосовал за тебя. Я изначально только прочитал первый абзац. Итак, снова мои скромные извинения. Вы снова проголосовали.

вы не должны хранить это Session. Легко испортить данные в ObjectContext, которые хранятся долгое время:

Что если вы вставите данные, которые не нарушают правила в ObjectContext, но нарушают правила в базе данных? Если вы вставите строку, которая нарушает правила, вы будете удалять ее из контекста? Ситуация с изображением: вы используете один контекст, и неожиданно у вас появляется запрос, который изменяет данные в одной таблице, добавляет строку в другую таблицу, а затем вызывается SaveChanges (). Одно из изменений выдает ошибку нарушения ограничения. Как ты это убираешь? Очистка контекста не легка, проще просто получить новый в следующем запросе.

Что если кто-то удалит данные из базы данных, пока они все еще находятся в контексте? ObjectContext кэширует данные и не ищет время от времени, чтобы проверить, есть ли они еще или они изменились :)

Что если кто-то изменит web.config и сессия будет потеряна? Кажется, вы хотите положиться на Session для хранения информации о вошедшем в систему пользователе. Файл cookie проверки подлинности форм является более надежным местом для хранения этой информации. Сессия может быть потеряна во многих ситуациях.

ObjectContext был разработан, чтобы быть недолгим, лучше всего создавать его в запросе, когда это необходимо, и размещать в конце.

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

Ваш ответ на вопрос