Контекст объекта Entity Framework в объекте сеанса ASP.NET? - PullRequest
4 голосов
/ 05 марта 2010

У нас есть многоуровневое приложение 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 ]

18 голосов
/ 05 марта 2010

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

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


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

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

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

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


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

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


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

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


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

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

3 голосов
/ 05 марта 2010

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...