Почему Entity Framework занимает 30 секунд для загрузки записей, если сгенерированный запрос занимает всего 1/2 секунды? - PullRequest
16 голосов
/ 26 марта 2009

Время выполнения ниже - 30 секунд в первый раз и 25 секунд в следующий раз, когда я выполняю тот же набор кода. При просмотре в SQL Profiler я сразу вижу логин, потом он просто сидит около 30 секунд. Затем, как только выполняется оператор выбора, приложение завершает команду ToList. Когда я запускаю сгенерированный запрос из Management Studio, запрос к базе данных занимает около 400 мс. Возвращает 14 строк и 350 столбцов. Похоже, что время, необходимое для преобразования результатов базы данных в сущности, настолько мало, что это не заметно.

Так что же происходит за 30 секунд до вызова базы данных?

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

UPDATE: Хорошо, если я использую скомпилированный запрос, первый раз это займет 30 секунд, а второй раз это займет 1/4 секунды. Что я могу сделать, чтобы ускорить первый звонок?

using (EntitiesContext context = new EntitiesContext()) 
{ 
    Stopwatch sw = new Stopwatch(); 
    sw.Start(); 
    var groupQuery = (from g in context.Groups.Include("DealContract") 
                    .Include("DealContract.Contracts") 
                    .Include("DealContract.Contracts.AdvertiserAccountType1") 
                    .Include("DealContract.Contracts.ContractItemDetails") 
                    .Include("DealContract.Contracts.Brands") 
                    .Include("DealContract.Contracts.Agencies") 
                    .Include("DealContract.Contracts.AdvertiserAccountType2") 
                    .Include("DealContract.Contracts.ContractProductLinks.Products") 
                    .Include("DealContract.Contracts.ContractPersonnelLinks") 
                    .Include("DealContract.Contracts.ContractSpotOrderTypes") 
                    .Include("DealContract.Contracts.Advertisers") 
                where g.GroupKey == 6 
                select g).OfType<Deal>(); 
    sw.Stop(); 
    var queryTime = sw.Elapsed; 
    sw.Reset(); 
    sw.Start(); 
    var groups = groupQuery.ToList(); 
    sw.Stop(); 
    var executeTime = sw.Elapsed; 
} 

Ответы [ 4 ]

12 голосов
/ 29 марта 2009

У меня была точно такая же проблема, мой запрос занимал 40 секунд.

Я обнаружил, что проблема была с функциями .Include("table_name"). Чем больше таких у меня было, тем хуже было. Вместо этого я изменил свой код на «Ленивая загрузка» всех необходимых мне данных сразу после запроса, что снизило общее время с 40 до 1,5 с. Насколько я знаю, это точно так же.

Так что для вашего кода это будет примерно так:

var groupQuery = (from g in context.Groups
            where g.GroupKey == 6 
            select g).OfType<Deal>(); 

var groups = groupQuery.ToList();

foreach (var g in groups)
{
    // Assuming Dealcontract is an Object, not a Collection of Objects
    g.DealContractReference.Load();
    if (g.DealContract != null)
    {
        foreach (var d in g.DealContract)
        {
            // If the Reference is to a collection, you can just to a Straight ".Load"
            //  if it is an object, you call ".Load" on the refence instead like with "g.DealContractReference" above
            d.Contracts.Load();
            foreach (var c in d.Contracts)
            {
                c.AdvertiserAccountType1Reference.Load();
                // etc....
            }
        }
    }
}

Кстати, если бы вы добавили эту строку кода над запросом в вашем текущем коде, это сократило бы время примерно до 4-5 секунд (все еще слишком долго в моем варианте) * опция отключает много накладных расходов на отслеживание обновления и вставки материала обратно в базу данных:

context.groups.MergeOption = MergeOption.NoTracking;
4 голосов
/ 26 марта 2009

Это из-за включения. Я предполагаю, что вы стремитесь загрузить много объектов в память. Создание объектов c #, соответствующих вашим объектам БД, занимает много времени.

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

2 голосов
/ 26 марта 2009

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

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

Однако я должен спросить, действительно ли вам нужно каждое свойство каждой сущности, которую вы здесь включаете. Мне кажется, что в этом запросе много разных типов сущностей, поэтому их реализация может быть довольно дорогой. Если вы просто пытаетесь получить табличные результаты, которые не собираетесь обновлять, проецирование (относительно) меньшего количества полей, которые вам действительно нужны, в плоский анонимный тип должно быть значительно быстрее по различным причинам. Кроме того, это избавляет вас от необходимости беспокоиться об активной загрузке, вызове Load / IsLoaded и т. Д.

Конечно, вы можете ускорить первоначальное генерирование представлений, предварительно скомпилировав представления сущностей. Для этого имеется документация на MSDN. Но поскольку вы оплачиваете эту стоимость во время выполнения первого запроса, ваш тест с простым запросом показывает, что он выполняется за 2 секунды. Приятно сказать, что 2 секунды, но больше ничего не спасет.

0 голосов
/ 26 марта 2009

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

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