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

.net c# entity-framework linq-to-entities

Вопрос

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

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

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

ОБНОВЛЕНИЕ: Хорошо, если я использую скомпилированный запрос, первый раз это займет 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; 
} 

Принятый ответ

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

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

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

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 секунд (все еще слишком долго в моем варианте). Насколько я понимаю, параметр MergeOption.NoTracking отключает много накладных расходов на отслеживание обновления и вставки материала обратно в базу данных:

context.groups.MergeOption = MergeOption.NoTracking;

Популярные ответы

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

Я рекомендую вам лениво загружать только те данные, которые вам нужны.



Related

Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow