Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом разделе описываются характеристики производительности ADO.NET Entity Framework и приведены некоторые рекомендации по повышению производительности приложений Entity Framework.
Этапы выполнения запроса
Чтобы лучше понять производительность запросов в Entity Framework, полезно понять операции, возникающие при выполнении запроса в концептуальной модели и возвращающих данные в виде объектов. В следующей таблице описывается эта серия операций.
Операция | Относительные затраты | Частота | Комментарии |
---|---|---|---|
Загрузка метаданных | Умеренный | Один раз в каждом домене приложения. | Метаданные модели и сопоставления, используемые в Entity Framework, загружаются в объект MetadataWorkspace. Эти метаданные кэшируются глобально и доступны другим экземплярам ObjectContext в том же домене приложения. |
Открытие подключения к базе данных | Умеренный1 | По мере необходимости. | Так как открытое подключение к базе данных использует ценный ресурс, Entity Framework открывает и закрывает подключение к базе данных только по мере необходимости. Вы также можете явно открыть подключение. Дополнительные сведения см. в разделе "Управление подключениями и транзакциями". |
Создание представлений | Высоко | Один раз в каждом домене приложения. (Может быть предварительно создано.) | Прежде чем Entity Framework может выполнить запрос к концептуальной модели или сохранить изменения в источнике данных, он должен создать набор представлений локальных запросов для доступа к базе данных. Из-за высокой стоимости создания этих представлений можно предварительно создать представления и добавить их в проект во время разработки. Дополнительные сведения см. в статье "Практическое руководство. Создание представлений для повышения производительности запросов". |
Подготовка запроса | Умеренный2 | Один раз для каждого уникального запроса. | Включает затраты на создание команды запроса, создание дерева команд на основе метаданных модели и сопоставления и определение формы возвращаемых данных. Так как теперь кэшируются обе команды запросов Entity SQL и запросы LINQ, последующие выполнение одного и того же запроса занимает меньше времени. Вы по-прежнему можете использовать скомпилированные запросы LINQ для снижения затрат в последующих выполнениях, и они могут быть более эффективными, чем запросы LINQ, которые автоматически кэшируются. Дополнительные сведения см. в разделе "Скомпилированные запросы" (LINQ to Entity). Общие сведения о выполнении запроса LINQ см. в разделе LINQ to Entities.
Заметка: Запросы LINQ to Entity, которые применяют Enumerable.Contains оператор к коллекциям в памяти, не кэшируются автоматически. Кроме того, параметризация коллекций в памяти в скомпилированных запросах LINQ запрещена. |
Выполнение запроса | Низкий2 | Один раз для каждого запроса. | Стоимость выполнения команды в источнике данных с помощью поставщика данных ADO.NET. Поскольку большинство планов запросов к источникам данных кэшируются, более поздние выполнение одного запроса может занять еще меньше времени. |
Загрузка и проверка типов | Низкий 3 | Один раз для каждого ObjectContext объекта. | Типы загружаются и проверяются в отношении типов, определяющих концептуальную модель. |
Отслеживание | Низкий 3 | Один раз для каждого объекта, возвращаемого запросом. 4 | Если запрос использует NoTracking параметр слияния, этот этап не влияет на производительность. Если в запросе используется параметр слияния AppendOnly, PreserveChanges или OverwriteChanges, результаты запроса отслеживаются в ObjectStateManager. Создается EntityKey для каждого отслеживаемого объекта, возвращаемого запросом, и используется для создания ObjectStateEntry в ObjectStateManager. Если для ObjectStateEntry можно найти существующий EntityKey, возвращается этот объект. Если используется опция PreserveChanges или OverwriteChanges, объект обновляется перед возвратом. Дополнительные сведения см. статью «Разрешение удостоверений», «Управление состоянием» и «Отслеживание изменений». |
Материализация объектов | Умеренный3 | Один раз для каждого объекта, возвращаемого запросом. 4 | Процесс чтения возвращаемого DbDataReader объекта и создания объектов и задания значений свойств, основанных на значениях в каждом экземпляре DbDataRecord класса. Если объект уже существует в ObjectContext, и запрос использует параметры слияния AppendOnly или PreserveChanges, этот этап не влияет на производительность. Дополнительные сведения см. статью «Разрешение удостоверений», «Управление состоянием» и «Отслеживание изменений». |
1 Если поставщик источников данных реализует пул подключений, стоимость открытия подключения распределяется по пулу. Поставщик .NET для SQL Server поддерживает пул подключений.
2 Затраты увеличиваются с повышенной сложностью запроса.
3 Общая стоимость увеличивается пропорционально количеству объектов, возвращаемых запросом.
4 Это не требуется для запросов EntityClient, так как запросы EntityClient возвращают EntityDataReader вместо объектов. Дополнительные сведения см. в разделе EntityClient Provider для Entity Framework.
Дополнительные рекомендации
Ниже приведены другие рекомендации, которые могут повлиять на производительность приложений Entity Framework.
Выполнение запроса
Так как запросы могут быть ресурсоемкими, рассмотрим, в какой момент в коде и на каком компьютере выполняется запрос.
Отложенное и немедленное выполнение
При создании ObjectQuery<T> или запросе LINQ запрос может не выполняться немедленно. Выполнение запроса откладывается до тех пор, пока результаты не потребуются, например, при перечислении foreach
(C#) или For Each
(Visual Basic), либо при назначении для заполнения коллекции List<T>. Выполнение запроса начинается немедленно при вызове метода Execute на ObjectQuery<T> или при вызове метода LINQ, возвращающего единственный запрос, например First или Any. Дополнительные сведения см. в разделе "Запросы объектов " и "Выполнение запросов" (LINQ to Entity).
Выполнение клиентских запросов LINQ
Хотя выполнение запроса LINQ происходит на компьютере, на котором размещен источник данных, некоторые части запроса LINQ могут оцениваться на клиентском компьютере. Дополнительные сведения см. в разделе "Выполнение в магазине" в разделе "Выполнение запросов" (LINQ to Entities).
Сложность запросов и сопоставления
Сложность отдельных запросов и сопоставления в модели сущностей существенно влияет на производительность запросов.
Сложность картирования
Модели, которые являются более сложными, чем простое сопоставление между сущностями в концептуальной модели и таблицами в модели хранения, создают более сложные команды, чем модели с сопоставлением "один к одному".
Сложность запросов
Запросы, требующие большого количества соединений в командах, выполняемых в источнике данных или возвращающие большой объем данных, могут повлиять на производительность следующим образом:
Запросы к концептуальной модели, которая кажется простой, могут привести к выполнению более сложных запросов к источнику данных. Это может произойти, так как Entity Framework преобразует запрос к концептуальной модели в эквивалентный запрос к источнику данных. Если один набор сущностей в концептуальной модели сопоставляется с несколькими таблицами в источнике данных или когда связь между сущностями сопоставляется с таблицей соединения, команда запроса, выполняемая в запросе источника данных, может потребовать одного или нескольких соединений.
Замечание
Используйте метод класса ToTraceString или ObjectQuery<T> для просмотра команд, выполняемых в источнике данных для данного запроса. Дополнительные сведения см. в разделе "Как просмотреть команды Магазина".
Вложенные запросы Entity SQL могут создавать соединения на сервере и могут возвращать большое количество строк.
Ниже приведен пример вложенного запроса в предложении проекции:
SELECT c, (SELECT c, (SELECT c FROM AdventureWorksModel.Vendor AS c ) As Inner2 FROM AdventureWorksModel.JobCandidate AS c ) As Inner1 FROM AdventureWorksModel.EmployeeDepartmentHistory AS c
Кроме того, такие запросы заставляют конвейер запросов генерировать единственный запрос с дублированием объектов в рамках вложенных запросов. Из-за этого один столбец может повторяться несколько раз. В некоторых базах данных, включая SQL Server, это может привести к значительному увеличению таблицы TempDB, что может снизить производительность сервера. Будьте внимательны при выполнении вложенных запросов.
Любые запросы, возвращающие большой объем данных, могут привести к снижению производительности, если клиент выполняет операции, ресурсы для которых потребляются пропорционально размеру результирующего набора. В таких случаях следует ограничить объем данных, возвращаемых запросом. Дополнительные сведения см. в разделе "Практическое руководство. Страница с помощью результатов запроса".
Все команды, автоматически созданные Entity Framework, могут быть более сложными, чем аналогичные команды, написанные явным образом разработчиком базы данных. Если вам нужен явный контроль над командами, выполняемыми в источнике данных, рассмотрите возможность определения сопоставления с табличной функцией или хранимой процедурой.
Отношения
Для оптимальной производительности запросов необходимо определить связи между сущностями как ассоциации в модели сущности, так и как логические связи в источнике данных.
Пути запроса
По умолчанию при выполнении ObjectQuery<T> связанные объекты не возвращаются (хотя объекты, представляющие сами связи, возвращаются). Связанные объекты можно загрузить одним из трех способов:
Задайте путь запроса перед выполнением ObjectQuery<T>.
Вызовите метод
Load
навигационного свойства, раскрываемого объектом.Задайте параметру LazyLoadingEnabled значение ObjectContext. Обратите внимание, что это выполняется автоматически при создании кода уровня объектов с помощью конструктора моделей данных сущности. Дополнительные сведения см. в разделе "Обзор созданного кода".
При рассмотрении варианта использования следует учитывать, что существует компромисс между количеством запросов к базе данных и объемом данных, возвращаемым в одном запросе. Дополнительные сведения см. в разделе "Загрузка связанных объектов".
Использование путей запроса
Пути запроса определяют граф объектов, возвращаемых запросом. При определении пути запроса требуется только один запрос к базе данных, чтобы вернуть все объекты, которые определяет путь. Использование путей запроса может привести к выполнению сложных команд в источнике данных из, казалось бы, простых запросов объектов. Это происходит, так как для возврата связанных объектов в одном запросе требуется одно или несколько соединений. Эта сложность больше в запросах к сложной модели сущности, например сущности с наследованием или путь, включающий связи "многие ко многим".
Замечание
Используйте метод ToTraceString, чтобы увидеть команду, которая будет сгенерирована ObjectQuery<T>. Дополнительные сведения см. в разделе "Как просмотреть команды Магазина".
Если путь запроса содержит слишком много связанных объектов или объектов содержит слишком много данных строк, источник данных может не завершить запрос. Это происходит, если запрос требует промежуточного временного хранилища, превышающего возможности источника данных. При этом можно уменьшить сложность запроса источника данных, явно загрузив связанные объекты.
Явная загрузка связанных объектов
Вы можете явно загрузить связанные объекты, вызвав Load
метод для свойства навигации, возвращающего EntityCollection<TEntity> или EntityReference<TEntity>. Для явной загрузки объектов каждый раз, когда вызывается Load
, требуется обращение к базе данных.
Замечание
При вызове Load
во время цикла по коллекции возвращаемых объектов, например, при использовании оператора foreach
(For Each
в Visual Basic), провайдер, специфичный для источника данных, должен поддерживать несколько активных наборов результатов на одном соединении. Для базы данных SQL Server необходимо указать значение MultipleActiveResultSets = true
в строке подключения поставщика.
Использовать метод LoadProperty можно также, если в сущностях нет свойств EntityCollection<TEntity> или EntityReference<TEntity>. Это полезно при использовании сущностей POCO.
Хотя явно загрузка связанных объектов уменьшит количество соединений и уменьшит объем избыточных данных, Load
требует повторяющихся подключений к базе данных, что может стать дорогостоящим при явной загрузке большого количества объектов.
Сохранение изменений
При вызове метода SaveChanges для ObjectContext создается отдельная команда создания, обновления или удаления для каждого добавленного, обновленного или удаленного объекта в контексте. Эти команды выполняются в источнике данных в одной транзакции. Как и в случае с запросами, производительность операций создания, обновления и удаления зависит от сложности сопоставления в концептуальной модели.
Распределенные транзакции
Операции в явной транзакции, требующей ресурсов, управляемых координатором распределенных транзакций (DTC), будут гораздо дороже, чем аналогичная операция, которая не требует DTC. Повышение уровня DTC будет происходить в следующих ситуациях:
Явная транзакция с операцией с базой данных SQL Server 2000 или другим источником данных, которая всегда способствует явным транзакциям в DTC.
Явная транзакция с операцией против SQL Server 2005, когда подключением управляет Entity Framework. Это происходит из-за того, что SQL Server 2005 переносит транзакцию на распределённого координатора транзакций (DTC) при закрытии и повторном открытии подключения в рамках одной транзакции, что является поведением по умолчанию Entity Framework. Эта DTC-акция не осуществляется при использовании SQL Server 2008. Чтобы избежать этого апгрейда при использовании SQL Server 2005, необходимо явно открыть и закрыть подключение в рамках транзакции. Дополнительные сведения см. в разделе "Управление подключениями и транзакциями".
Явная транзакция используется при выполнении одной или нескольких операций System.Transactions внутри транзакции. Дополнительные сведения см. в разделе "Управление подключениями и транзакциями".
Стратегии повышения производительности
Вы можете повысить общую производительность запросов в Entity Framework с помощью следующих стратегий.
Предварительная генерация представлений
Создание представлений на основе модели сущностей является значительной стоимостью при первом выполнении запроса приложением. Используйте программу EdmGen.exe для предварительного создания представлений в виде файла кода Visual Basic или C#, который можно добавить в проект во время разработки. Вы также можете использовать набор средств преобразования текстовых шаблонов для создания предварительно скомпилированных представлений. Предварительно созданные представления проверяются во время выполнения, чтобы убедиться, что они соответствуют текущей версии указанной модели сущности. Дополнительные сведения см. в статье "Практическое руководство. Создание представлений для повышения производительности запросов".
При работе с очень большими моделями следует учитывать следующее:
Формат метаданных .NET ограничивает количество символов строки пользователя в заданном двоичном файле до 16 777 215 (0xFFFFFF). Если вы создаете представления для очень большой модели и файл представления достигает этого ограничения размера, вы получите ошибку компиляции "Нет логического пространства, чтобы создать дополнительные строки пользователей". Это ограничение размера применяется ко всем управляемым двоичным файлам. Дополнительные сведения см. в блоге , где показано, как избежать ошибки при работе с крупными и сложными моделями.
Рассмотрите возможность использования параметра слияния NoTracking для запросов
Для отслеживания возвращаемых объектов в рамках объекта требуются расходы. Обнаружение изменений объектов и обеспечение того, чтобы несколько запросов для одной логической сущности возвращали один и тот же экземпляр объекта, требуется, чтобы объекты были присоединены к экземпляру ObjectContext . Если вы не планируете обновлять или удалять объекты и не требуете управления удостоверениями, рассмотрите возможность использования параметров слияния NoTracking при выполнении запросов.
Возврат правильного объема данных
В некоторых сценариях указание пути запроса с помощью Include метода гораздо быстрее, так как для этого требуется меньше круговых путей к базе данных. Однако в других сценариях дополнительные круговые пути в базу данных для загрузки связанных объектов могут быть быстрее, так как более простые запросы с меньшим количеством соединений приводят к снижению избыточности данных. Из-за этого рекомендуется протестировать производительность различных способов извлечения связанных объектов. Дополнительные сведения см. в разделе "Загрузка связанных объектов".
Чтобы избежать возврата слишком большого объема данных в одном запросе, рассмотрите возможность разбиения результатов запроса в более управляемые группы. Дополнительные сведения см. в разделе "Практическое руководство. Страница с помощью результатов запроса".
Ограничение области объекта ObjectContext
В большинстве случаев следует создавать экземпляр ObjectContext внутри выражения using
(Using…End Using
в Visual Basic). Это может повысить производительность, обеспечивая автоматическое удаление ресурсов, связанных с контекстом объекта, при выходе кода из блока инструкций. Однако если элементы управления привязаны к объектам, управляемым контекстом объекта, экземпляр ObjectContext должен сохраняться до тех пор, пока привязка необходима, и удаляться вручную. Дополнительные сведения см. в разделе "Управление подключениями и транзакциями".
Рассмотрите возможность открытия подключения к базе данных вручную
Когда ваше приложение выполняет серию запросов к объектам или часто вызывает SaveChanges для сохранения операций создания, обновления и удаления в источнике данных, Entity Framework необходимо постоянно открывать и закрывать подключение к этому источнику данных. В этих ситуациях рекомендуется вручную открыть подключение в начале этих операций и закрыть или удалить подключение после завершения операций. Дополнительные сведения см. в разделе "Управление подключениями и транзакциями".
Данные о производительности
Некоторые данные о производительности для Entity Framework публикуются в следующих записях в блоге команды ADO.NET:
Изучение производительности платформы сущностей ADO.NET — часть 1
Исследование производительности платформы ADO.NET Entity Framework — Часть 2