Entity Framework и пул соединений
Недавно я начал использовать Entity Framework 4.0 в своем приложении.NET 4.0, и мне любопытно несколько вещей, связанных с пулами.
Как мне известно, пул соединений управляется поставщиком данных ADO.NET, в моем случае это сервер MS SQL. Применимо ли это при создании экземпляра нового контекста сущностей (
ObjectContext
), т.е. без параметровnew MyDatabaseModelEntities()
?Каковы преимущества и недостатки а) создания глобального контекста сущностей для приложения (то есть одного статического экземпляра) или б) создания и раскрытия контекста сущностей для каждой данной операции / метода с
using
блок.Любые другие рекомендации, лучшие практики или общие подходы для определенных сценариев, о которых я должен знать?
4 ответа
- Пул соединений обрабатывается как в любом другом приложении ADO.NET. При подключении объекта все еще используется традиционное подключение к базе данных с традиционной строкой подключения. Я считаю, что вы можете отключить пул подключений в строке подключения, если вы не хотите его использовать. (подробнее о пуле соединений SQL Server (ADO.NET))
- Никогда не используйте глобальный контекст. ObjectContext внутренне реализует несколько шаблонов, включая Identity Map и Unit of Work. Влияние использования глобального контекста зависит от типа приложения.
- Для веб-приложений используйте один контекст для каждого запроса. Для веб-сервисов используйте один контекст на вызов. В WinForms или приложении WPF используйте один контекст для формы или для докладчика. Могут быть особые требования, которые не позволят использовать этот подход, но в большинстве случаев этого достаточно.
Если вы хотите узнать, какое влияние оказывает один объектный контекст на приложение WPF / WinForm, проверьте эту статью. Это о NHibernate Session, но идея та же.
Редактировать:
Когда вы используете EF, он по умолчанию загружает каждую сущность только один раз для контекста. Первый запрос создает объект сущности и сохраняет его внутри. Любой последующий запрос, для которого требуется объект с тем же ключом, возвращает этот сохраненный экземпляр. Если значения в хранилище данных изменились, вы все равно получите объект со значениями из исходного запроса. Это называется шаблоном карты идентичности. Вы можете заставить объектный контекст перезагрузить объект, но он перезагрузит один общий экземпляр.
Любые изменения, внесенные в сущность, не сохраняются, пока вы не позвоните SaveChanges
в контексте. Вы можете вносить изменения в несколько объектов и сохранять их одновременно. Это называется шаблоном Unit of Work. Вы не можете выборочно сказать, какую модифицированную присоединенную сущность вы хотите сохранить.
Объедините эти две модели, и вы увидите несколько интересных эффектов. У вас есть только один экземпляр объекта для всего приложения. Любые изменения в сущности влияют на все приложение, даже если изменения еще не сохранены (зафиксированы). В большинстве случаев это не то, что вы хотите. Предположим, что у вас есть форма редактирования в приложении WPF. Вы работаете с сущностью и решаете отменить сложное редактирование (изменение значений, добавление связанных сущностей, удаление других связанных сущностей и т. Д.). Но сущность уже изменена в общем контексте. Что вы будете делать? Подсказка: я не знаю ни о каких CancelChanges или UndoChanges на ObjectContext
,
Я думаю, что нам не нужно обсуждать сценарий сервера. Простое совместное использование одной сущности между несколькими HTTP-запросами или вызовами веб-службы делает ваше приложение бесполезным. Любой запрос может просто вызвать SaveChanges
и сохраните частичные данные из другого запроса, потому что вы разделяете одну единицу работы между всеми ними. Это также будет иметь другую проблему - контекст и любые манипуляции с сущностями в контексте или соединением базы данных, используемым контекстом, не являются поточно-ориентированными.
Даже для приложения, доступного только для чтения, глобальный контекст не является хорошим выбором, потому что вам, вероятно, нужны свежие данные каждый раз, когда вы запрашиваете приложение.
По словам Даниэля Симмонса:
Создайте новый экземпляр ObjectContext в операторе Using для каждого метода службы, чтобы он был удален до возврата метода. Этот шаг очень важен для масштабируемости вашего сервиса. Это гарантирует, что соединения с базой данных не будут оставаться открытыми при вызовах сервисов, и что временное состояние, используемое конкретной операцией, будет собираться мусором после ее завершения. Entity Framework автоматически кэширует метаданные и другую информацию, необходимую для домена приложения, и ADO.NET объединяет пулы соединений с базами данных, поэтому повторное создание контекста каждый раз является быстрой операцией.
Это из его всеобъемлющей статьи здесь:
http://msdn.microsoft.com/en-us/magazine/ee335715.aspx
Я считаю, что этот совет распространяется на HTTP-запросы, поэтому будет действительным для ASP.NET. Приложение с полным состоянием клиента, такое как приложение WPF, может быть единственным случаем для "общего" контекста.
В соответствии с документацией EF6 (4,5 также): https://msdn.microsoft.com/en-us/data/hh949853
9.3 Контекст на запрос
Контексты Entity Framework предназначены для использования в качестве кратковременных экземпляров, чтобы обеспечить наиболее оптимальную производительность. Ожидается, что контексты будут недолговечными и отброшенными, и поэтому были реализованы, чтобы быть очень легкими и повторно использовать метаданные, когда это возможно. В веб-сценариях важно помнить об этом и не иметь контекста для более чем продолжительности одного запроса. Аналогично, в не-веб-сценариях контекст должен быть отброшен на основе вашего понимания различных уровней кэширования в Entity Framework. Вообще говоря, следует избегать наличия экземпляра контекста на протяжении всей жизни приложения, а также контекстов для потока и статических контекстов.
Приведенный ниже код помог обновить мой объект новыми значениями базы данных. Команда Entry(object).Reload() заставляет объект вызывать значения базы данных.
GM_MEMBERS member = DatabaseObjectContext.GM_MEMBERS.FirstOrDefault(p => p.Username == username && p.ApplicationName == this.ApplicationName);
DatabaseObjectContext.Entry(member).Reload();