Entity Framework и пул соединений

268

Недавно я начал использовать Entity Framework 4.0 в своем приложении .NET 4.0, и мне любопытно несколько вещей, касающихся пула.

  1. Как мне известно, пул соединений управляется поставщиком данных ADO.NET, в моем случае это сервер MS SQL. Применимо ли это, когда вы создаете новый объект context ( ObjectContext), то есть без параметров new MyDatabaseModelEntities()?

  2. Каковы преимущества и недостатки а) создания глобального контекста сущностей для приложения (т.е. одного статического экземпляра) или б) создания и раскрытия контекста сущностей для каждой данной операции / метода с помощью usingблока.

  3. Любые другие рекомендации, лучшие практики или общие подходы для определенных сценариев, о которых я должен знать?

нолдорин
источник

Ответы:

369
  1. Пул соединений обрабатывается как в любом другом приложении ADO.NET. При подключении объекта все еще используется традиционное подключение к базе данных с традиционной строкой подключения. Я считаю, что вы можете отключить пул подключений в строке подключения, если вы не хотите его использовать. (подробнее о пуле соединений SQL Server (ADO.NET) )
  2. Никогда не используйте глобальный контекст. ObjectContext внутренне реализует несколько шаблонов, включая Identity Map и Unit of Work. Влияние использования глобального контекста различно для разных типов приложений.
  3. Для веб-приложений используйте один контекст для каждого запроса. Для веб-сервисов используйте один контекст на вызов. В приложении WinForms или WPF используйте один контекст для формы или для докладчика. Могут быть особые требования, которые не позволят использовать этот подход, но в большинстве случаев этого достаточно.

Если вы хотите узнать, какое влияние оказывает один объектный контекст на приложение WPF / WinForm, проверьте эту статью . Речь идет о NHibernate Session, но идея та же.

Редактировать:

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

Любые изменения, внесенные в сущность, не сохраняются до тех пор, пока вы не вызовете SaveChangesконтекст. Вы можете вносить изменения в несколько объектов и сохранять их одновременно. Это называется шаблоном Unit of Work . Вы не можете выборочно сказать, какую модифицированную присоединенную сущность вы хотите сохранить.

Объедините эти две модели, и вы увидите некоторые интересные эффекты. У вас есть только один экземпляр объекта для всего приложения. Любые изменения в сущности влияют на все приложение, даже если изменения еще не сохранены (зафиксированы). В большинстве случаев это не то, что вы хотите. Предположим, что у вас есть форма редактирования в приложении WPF. Вы работаете с сущностью и решаете отменить сложное редактирование (изменение значений, добавление связанных сущностей, удаление других связанных сущностей и т. Д.). Но сущность уже изменена в общем контексте. Что ты будешь делать? Подсказка: я не знаю ни о каких CancelChanges или UndoChanges на ObjectContext.

Я думаю, что нам не нужно обсуждать сценарий сервера. Простое совместное использование одной сущности между несколькими HTTP-запросами или вызовами веб-службы делает ваше приложение бесполезным. Любой запрос может просто вызвать SaveChangesи сохранить частичные данные из другого запроса, потому что вы разделяете одну единицу работы между всеми ними. Это также будет иметь другую проблему - контекст и любые манипуляции с сущностями в контексте или подключением базы данных, используемым контекстом, не являются поточно-ориентированными.

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

Ладислав Мрнка
источник
Спасибо за ваш ответ. Возможно, вы могли бы уточнить, почему плохо использовать один глобальный контекст? Конечно, это затрудняет параллельный доступ, но что еще ...?
Нолдорин
Хорошо, теперь все прояснилось, спасибо. Просто чтобы подтвердить, хотя глобальный контекст никогда не бывает уместным, единый контекст для «диалога редактирования» или такой может быть правильным? В других ситуациях, таких как веб-службы и ASP.NET, контексты внутри методов имеют больше смысла. О правильном?
Нолдорин
Я принял ваш совет и удалил сингелтон. Теперь я получаю еще одну ошибку: stackoverflow.com/questions/14795899/…
Elad Benda
Я понимаю, что реализация модуля рабочего шаблона и инкапсуляция DbContext должны разделять бизнес-логику и операции с базой данных. Я не могу понять, как реализовать шаблон единицы работы и использовать TransactionScope только для некоторых операций.
Рудольф Дворачек
4
@RudolfDvoracek: легко. TransactionScopeне принадлежит единице работы, она принадлежит вашей бизнес-логике, потому что сама логика определяет транзакцию. Единица работы определяет только то, что должно сохраняться вместе, тогда как объем транзакции позволяет многократно использовать постоянство единицы работы в одной и той же транзакции.
Ладислав Мрнка
70

По словам Даниэля Симмонса:

Создайте новый экземпляр ObjectContext в операторе Using для каждого метода службы, чтобы он был удален до возврата метода. Этот шаг очень важен для масштабируемости вашего сервиса. Это гарантирует, что соединения с базой данных не будут оставаться открытыми при вызовах служб, и что временное состояние, используемое конкретной операцией, будет собираться мусором по окончании этой операции. Entity Framework автоматически кэширует метаданные и другую информацию, необходимую для домена приложения, и ADO.NET объединяет соединения с базой данных, поэтому повторное создание контекста каждый раз является быстрой операцией.

Это из его всеобъемлющей статьи здесь:

http://msdn.microsoft.com/en-us/magazine/ee335715.aspx

Я считаю, что этот совет распространяется на HTTP-запросы, поэтому будет действительным для ASP.NET. Приложение с полным состоянием клиента, такое как приложение WPF, может быть единственным случаем для «общего» контекста.

Дейв Сверски
источник
Спасибо, это очень информативная цитата. Однако мне все еще интересно, будет ли общий (глобальный) контекст подходящим даже для клиентского приложения WPF или чего-то подобного. Есть ли какое - либо преимущество даже в этом случае?
Нолдорин
Не будет никакого преимущества для глобального контекста в приложении WPF, но, вероятно, также не будет никакого существенного вреда. Если вы реализуете глобальный контекст, вам может потребоваться некоторое ручное управление соединениями с базой данных (явное закрытие соединения) в случаях высокой частоты запросов.
Дэйв Сверски
1
Правильно; так что, по сути, я никогда не смогу ошибиться, используя несколько временных контекстов (учитывая, что я знаю, что происходит пул соединений)? ... Если бы вы использовали один глобальный контекст, разве теоретически связь не может разорваться в случайный момент времени?
Нолдорин
1
@Nolodrin: Я не думаю, что соединение будет разорвано «случайно» ... риск состоит в том, что соединения могут оставаться открытыми слишком долго и насыщать пул соединений.
Дэйв Сверски
1
На мой взгляд, реализация ObjectContext / DbContext IDisposableдолжна быть открыта в кратчайшие разумные сроки.
nicodemus13
12

В соответствии с документацией EF6 (4,5 также): https://msdn.microsoft.com/en-us/data/hh949853#9

9.3 Контекст на запрос

Контексты Entity Framework предназначены для использования в качестве кратковременных экземпляров, чтобы обеспечить наиболее оптимальную производительность . Ожидается, что контексты будут недолговечными и отбрасываются, и поэтому были реализованы так, чтобы быть очень легкими и повторно использовать метаданные, когда это возможно. В веб-сценариях важно помнить об этом и не иметь контекста для более чем продолжительности одного запроса. Точно так же в не-веб-сценариях контекст должен быть отброшен на основе вашего понимания различных уровней кэширования в Entity Framework. Вообще говоря, следует избегать наличия экземпляра контекста на протяжении всей жизни приложения, а также контекстов для потока и статических контекстов.

Радж Рао
источник
2
Я знаю, что этот ответ был здесь некоторое время, но я должен сказать, что это спасло меня от головной боли. При использовании EF с Oracle получал ошибку «Pooled Connection» и не мог понять, почему. Я установил dbContext как переменную класса, создавая его экземпляр при создании. Изменение его для создания контекста по мере необходимости исправило все беды моего мира. Спасибо!
Флетчиус
1

Приведенный ниже код помог обновить мой объект свежими значениями базы данных. Команда Entry (object) .Reload () заставляет объект вызывать значения базы данных.

GM_MEMBERS member = DatabaseObjectContext.GM_MEMBERS.FirstOrDefault(p => p.Username == username && p.ApplicationName == this.ApplicationName);
DatabaseObjectContext.Entry(member).Reload();
HGMamaci
источник
а также это для коллекций (код VB):CType(myContext, IObjectContextAdapter).ObjectContext.Refresh(RefreshMode.StoreWins,myCustomers)
Иван Феррер Вилла