Вопросы производительности (платформа Entity Framework)

В этом подразделе описаны характеристики производительности платформы ADO.NET Entity Framework, а также приведены соображения по повышению производительности приложений платформы Entity Framework.

Этапы выполнения запросов

Чтобы лучше понять специфику производительности запросов платформы Entity Framework, необходимо разобраться с операциями, которые совершаются при выполнении запроса к концептуальной модели и при возврате данных в виде объектов. В следующей таблице описан этот ряд операций.

Операция Относительные затраты Частота Комментарии

Загрузка метаданных

Средняя

По одному разу в каждом домене приложения.

Метаданные модели и сопоставления, используемые платформой Entity Framework, загружаются в MetadataWorkspace. Эти метаданные собираются глобально и доступны другим экземплярам ObjectContext в том же домене приложения.

Открытие подключения базы данных

Средняя1

При необходимости.

Поскольку открытое соединение с базой данных является весьма ресурсоемким, Entity Framework открывает и закрывает его только при необходимости. Кроме того, соединение можно открыть явно. Дополнительные сведения см. в разделе Управление соединениями и транзакциями (платформа Entity Framework).

Создание представлений...

Высокий

По одному разу в каждом домене приложения. (могут создаваться предварительно.)

Прежде чем платформа Entity Framework сможет выполнять запросы к концептуальной модели или сохранять изменения в источнике данных, ей необходимо создать набор локальных представлений запросов для доступа к базе данных. В связи с высокими затратами на создание этих представлений их можно создать заранее и добавить в проект во время разработки. Дополнительные сведения см. в разделе Как предварительно создать представления для повышения производительности запросов.

Подготовка запроса

Средняя2

Один раз для каждого уникального запроса.

Включает затраты на создание команды запроса, создание дерева команд на базе метаданных модели и сопоставления, а также определение вида возвращаемых данных. Поскольку команды запросов Entity SQL кэшируются, при последующем выполнении один и тот же запрос занимает меньше времени. Кроме того, можно использовать скомпилированные запросы LINQ для снижения затрат при последующем выполнении. Дополнительные сведения см. в разделе Скомпилированные запросы (LINQ to Entities). Общие сведения о выполнении запросов LINQ см. в разделе LINQ to Entities.

Выполнение запроса

Низкий2

Один раз для каждого запроса.

Затраты на выполнение команды к источнику данных с помощью поставщика данных ADO.NET. Поскольку большинство источников данных кэширует планы запросов, при последующем выполнении один и тот же запрос может занимать меньше времени.

Загрузка и проверка типов

Низкий3

Один раз для каждого элемента ObjectContext.

Типы загружаются и проверяются относительно типов, определенных в концептуальной модели.

Отслеживание

Низкий3

Один раз для каждого объекта, возвращаемого запросом. 4

Если запрос использует параметр слияния NoTracking, то этот шаг не влияет на производительность.

Если запрос использует параметры слияния AppendOnly, PreserveChanges или OverwriteChanges, то результаты запроса отслеживаются в ObjectStateManager. Для каждого отслеживаемого объекта, возвращаемого запросом, создается ключ EntityKey, который используется для создания ObjectStateEntry в ObjectStateManager. Если для EntityKey можно найти существующий объект ObjectStateEntry, то возвращается существующий объект. Если используется параметр PreserveChanges или OverwriteChanges, то объект обновляется до возвращения.

Дополнительные сведения см. в разделе Разрешение идентификаторов, управление состоянием и отслеживание изменений (платформа Entity Framework).

Материализация объектов

Средняя3

Один раз для каждого объекта, возвращаемого запросом. 4

Процесс считывания возвращенного объекта DbDataReader, создания объектов и установки значений свойств, основанных на значениях в каждом экземпляре класса DbDataRecord. Если объект уже существует в классе ObjectContext и в запросе используются параметры слияния AppendOnly или PreserveChanges, то этот шаг не влияет на производительность. Дополнительные сведения см. в разделе Разрешение идентификаторов, управление состоянием и отслеживание изменений (платформа Entity Framework).

1 Если в поставщике источника данных реализована организация пулов соединений, то затраты на открытие одного соединения распределяются по всему пулу. Поставщик .NET для SQL Server поддерживает организацию пулов соединений.

2 Затраты возрастают с повышением сложности запросов.

3 Общие затраты возрастают пропорционально числу объектов, возвращенных запросом.

4 Эти накладные расходы не являются обязательными для запросов EntityClient, поскольку они вместо объектов возвращают EntityDataReader. Дополнительные сведения см. в разделе Поставщик EntityClient для платформы Entity Framework.

Дополнительные сведения

Ниже приведены дополнительные соображения по факторам, способным влиять на производительность приложений Entity Framework.

Выполнение запроса

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

Отложенное или немедленное выполнение

При создании запроса ObjectQuery или LINQ запрос может выполняться не сразу. Выполнение запроса откладывается до тех пор, пока не понадобятся его результаты, например результаты перечисления foreach (C#) или For Each (Visual Basic), либо если запрос должен заполнить коллекцию List. Выполнение запроса начинается немедленно при вызове метода Execute в ObjectQuery либо при вызове метода LINQ, возвращающего одноэлементный запрос, например First или Any. Дополнительные сведения см. в разделе Запросы объектов (платформа Entity Framework) и Выполнение запросов (язык LINQ to Entities).

Выполнение запросов LINQ на стороне клиента

Хотя запрос LINQ выполняется на том компьютере, где размещен источник данных, некоторые части запроса LINQ могут обрабатываться на клиентском компьютере. Дополнительные сведения см. в подразделе «Выполнение в хранилище» раздела Выполнение запросов (язык LINQ to Entities).

Сложность запросов и сопоставления

Сложность отдельных запросов и сопоставления в модели сущности значительно влияет на производительность запросов.

Сложность сопоставления

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

Сложность запросов

Запросы, требующие большого числа соединений в командах, выполняемых с источником данных или возвращающих большой объем данных, могут влиять на производительность следующим образом:

  • Запросы к концептуальной модели, которые кажутся простыми, могут приводить к выполнению более сложных запросов к источнику данных. Это может происходить как следствие преобразования платформой Entity Framework запроса к концептуальной модели в эквивалентный запрос к источнику данных. Если один набор сущностей, заданный в концептуальной модели, сопоставляется с несколькими таблицами источника данных либо если связь между сущностями сопоставлена с соединяемой таблицей, то команда запроса к источнику данных может потребовать одного или нескольких соединений.

    Cc853327.note(ru-ru,VS.100).gifПримечание
    Чтобы просмотреть команды, выполняемые по источнику данных для данного запроса, воспользуйтесь методом ToTraceString классов ObjectQuery или EntityCommand.Дополнительные сведения см. в разделе Как просмотреть команды хранилища (платформа Entity Framework).

  • Вложенные запросы 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).

Любые команды, автоматически созданные платформой Entity Framework, могут оказываться сложнее аналогичных команд, написанных вручную разработчиком базы данных. Если требуется явный контроль над командами, выполняемыми с источником данных, подумайте об определении сопоставления с возвращающей табличное значение функцией или хранимой процедурой.

Связи

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

Пути запроса

По умолчанию при выполнении запроса ObjectQuery связанные объекты не возвращаются (хотя возвращаются объекты, представляющие сами связи). Связанные объекты можно загрузить одним из трех способов.

  1. Задайте путь запроса до выполнения ObjectQuery.

  2. Вызовите метод Load для свойства навигации, доступного для объекта.

  3. Установите параметр LazyLoadingEnabled объекта ObjectContext в значение true. Обратите внимание, что это будет сделано автоматически при формировании кода уровня объектов с помощью Entity Data Model Designer. Дополнительные сведения см. в разделе Generated Code Overview.

При выборе параметра помните, что придется соблюдать баланс между числом запросов к базе данных и объемом данных, возвращаемых в одном запросе. Дополнительные сведения см. в разделе Загрузка связанных объектов (платформа Entity Framework).

Использование путей запроса

Пути запроса определяют граф объектов, возвращаемых запросом. При указании пути запроса для возврата всех определенных в нем объектов требуется только один запрос к базе данных. Использование путей запроса может привести к тому, что над источником данных будут выполняться сложные команды из, на первый взгляд, простых запросов к объектам. Происходит это потому, что для возвращения связанных объектов в рамках одного запроса требуется одно или несколько соединений. Сложность будет выше в запросах к сложным моделям сущностей, таким как, например, сущность с наследованием или путь, содержащий связи «многие ко многим».

Cc853327.note(ru-ru,VS.100).gifПримечание
Используйте метод ToTraceString, чтобы увидеть команду, которая будет сформирована методом ObjectQuery.Дополнительные сведения см. в разделе Как просмотреть команды хранилища (платформа Entity Framework).

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

Явная загрузка связанных объектов

Явно загрузить связанные объекты можно, вызвав метод Load для свойства навигации, возвращающего EntityCollection или EntityReference. При явной загрузке объектов требуется выполнять цикл обмена данными с базой данных при каждом вызове метода Load.

Cc853327.note(ru-ru,VS.100).gifПримечание
Если при циклической обработке коллекции возвращенных объектов, например при использовании инструкции foreach (For Each в Visual Basic), вызывается метод Load, поставщик для каждого конкретного источника данных должен поддерживать несколько активных результирующих наборов на одном соединении.Для базы данных SQL Server в строке соединения поставщика необходимо указать значение MultipleActiveResultSets = true.

Можно также использовать метод LoadProperty если сущности не имеют свойств EntityCollection и EntityReference. Это может оказаться полезным в том случае, если используются сущности POCO.

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

Сохранение изменений

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

Распределенные транзакции

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

  • Явная транзакция с операцией для базы данных SQL Server 2000 или другого источника данных, который всегда повышает уровень явных транзакций до DTC.

  • Явная транзакция с операцией для SQL Server 2005, если соединение управляется Entity Framework . Причина этого заключается в том, что SQL Server 2005 передает транзакцию в DTC при каждом закрытии и повторном открытии соединения в пределах одной транзакции, что является поведением Entity Framework по умолчанию. Такое повышение уровня до DTC не происходит при использовании SQL Server 2008. Чтобы избежать такого повышения уровня при работе с SQL Server 2005, необходимо явно открывать и закрывать соединение в пределах одной транзакции. Дополнительные сведения см. в разделе Управление соединениями и транзакциями (платформа Entity Framework).

Явная транзакция используется, если в пределах транзакции System.Transactions выполняется одна или несколько операций. Дополнительные сведения см. в разделе Управление соединениями и транзакциями (платформа Entity Framework).

Стратегии повышения производительности

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

Предварительное создание представлений

Создание представлений на базе модели сущностей — это значительная статья расходов при первом выполнении запроса приложением. С помощью программы EdmGen.exe можно заранее создавать представления в виде файлов с кодом Visual Basic или C#, которые будут добавляться в проект во время проектирования. Предварительно созданные представления во время выполнения будут проверяться на согласованность с текущей версией указанной модели сущностей. Дополнительные сведения см. в разделе Как предварительно создать представления для повышения производительности запросов.

Рассмотрите возможность использования параметра слияния NoTracking для запросов

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

Использование скомпилированных запросов LINQ

Если приложение многократно выполняет похожие по структуре запросы на платформе Entity Framework, во многих случаях можно повысить производительность, скомпилировав запрос один раз, а затем выполняя его несколько раз с разными параметрами. Например, приложению может понадобиться получить всех клиентов из определенного города; имя города указывается пользователем во время выполнения с помощью формы. Для этих целей технология LINQ to Entities поддерживает использование скомпилированных запросов. Компиляция запроса выполняется только один раз во время первого выполнения. Параметры слияния, которые заданы для запроса во время компиляции, далее не могут быть изменены.

Дополнительные сведения см. в разделе Скомпилированные запросы (LINQ to Entities).

Возвращение правильного объема данных

В некоторых случаях указание пути запроса с помощью метода Include выполняется гораздо быстрее, поскольку требуется меньше циклов обмена данными с базой данных. Однако в других сценариях дополнительные циклы обмена данными с базой данных при загрузке связанных объектов могут выполняться быстрее, поскольку более простые запросы с меньшим количеством соединений ведут к меньшей избыточности данных. В связи с этим рекомендуется проверять производительность разных способов получения связанных объектов. Дополнительные сведения см. в разделе Загрузка связанных объектов (платформа Entity Framework).

Чтобы избежать возвращения слишком большого объема данных в одном запросе, можно прибегнуть к подкачке страниц результатов запроса, что даст более простые в управлении группы. Дополнительные сведения см. в разделе Как постранично просматривать результаты запроса (платформа Entity Framework).

Ограничение области объекта ObjectContext

В большинстве случаев следует создавать экземпляр ObjectContext внутри инструкции using (Using…End Using в Visual Basic). Это может повысить производительность, поскольку гарантирует автоматическое удаление ресурсов, связанных с контекстом объекта, при выходе из блока инструкции в коде. Однако, если элементы управления привязаны к объектам, управляемым контекстом объекта, экземпляр ObjectContext следует сохранять до тех пор, пока требуется привязка, а удалять его — вручную. Дополнительные сведения см. в разделе Управление соединениями и транзакциями (платформа Entity Framework).

Рассмотрите возможность подключения базы данных вручную

Если приложение выполняет серию запросов объектов или часто вызывает метод SaveChanges для сохранения операций создания, обновления или удаления в источнике данных, то Entity Framework приходится постоянно открывать и закрывать соединение с источником данных. В таких случаях следует попробовать вручную открывать соединение в начале этих операций и либо закрывать, либо удалять соединение по их завершении. Дополнительные сведения см. в разделе Управление соединениями и транзакциями (платформа Entity Framework).

Данные производительности

Некоторые данные производительности платформы Entity Framework опубликованы в приведенных ниже сообщениях блога группы разработчиков ADO.NET:

См. также

Основные понятия

Вопросы разработки и развертывания (платформа Entity Framework)