Запрос LINQ to Entities компилируется долго, SQL работает быстро

Я работаю над фрагментом кода, написанным коллегой, который взаимодействует с приложением CRM, которое использует наша компания. В этом фрагменте кода есть два запроса LINQ to Entities, которые многократно выполняются в нашем приложении, и меня попросили оптимизировать их, поскольку один из них очень медленный.

Вот запросы:

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

from relation in context.ADRELATION
where ((relationIds.Contains(relation.FIDADRELATION)) && (relation.FLDELETED != -1))
join addressTable in context.ADDRESS on relation.FIDADDRESS equals addressTable.FIDADDRESS
   into temporaryAddressTable
from address in temporaryAddressTable.DefaultIfEmpty()
join mailAddressTable in context.ADDRESS on relation.FIDMAILADDRESS equals
   mailAddressTable.FIDADDRESS into temporaryMailAddressTable
from mailAddress in temporaryMailAddressTable.DefaultIfEmpty()
select new { Relation = relation, Address = address, MailAddress = mailAddress };

Второй запрос, который занимает около 4-5 секунд для компиляциии берет информацию о людях из база данных (снова отфильтрованная по списку идентификаторов):

from role in context.ROLE
join relationTable in context.ADRELATION on role.FIDADRELATION equals relationTable.FIDADRELATION into temporaryRelationTable
from relation in temporaryRelationTable.DefaultIfEmpty()
join personTable in context.PERSON on role.FIDPERS equals personTable.FIDPERS into temporaryPersonTable
from person in temporaryPersonTable.DefaultIfEmpty()
join nationalityTable in context.TBNATION on person.FIDTBNATION equals nationalityTable.FIDTBNATION into temporaryNationalities
from nationality in temporaryNationalities.DefaultIfEmpty()
join titelTable in context.TBTITLE on person.FIDTBTITLE equals titelTable.FIDTBTITLE into temporaryTitles
from title in temporaryTitles.DefaultIfEmpty()
join suffixTable in context.TBSUFFIX on person.FIDTBSUFFIX equals suffixTable.FIDTBSUFFIX into temporarySuffixes
from suffix in temporarySuffixes.DefaultIfEmpty()
where ((rolIds.Contains(role.FIDROLE)) && (relation.FLDELETED != -1))
select new { Role = role, Person = person, relation = relation, Nationality = nationality, Title = title.FTXTBTITLE, Suffix = suffix.FTXTBSUFFIX };

Я настроил SQL Profiler и взял SQL из обоих запросов, а затем запустил его в SQL Server Management Studio. Оба запроса выполнялись очень быстро, даже с большим (~1000) количеством идентификаторов. Таким образом, проблема, похоже, заключается в компиляции запроса LINQ.

Я пытался использовать скомпилированный запрос, но поскольку он может содержать только примитивные параметры, мне пришлось вырезать часть с фильтром и применить его после вызова Invoke(), так что я не уверен, поможет ли это много. Кроме того, поскольку этот код выполняется в операции службы WCF, я не уверен, что скомпилированный запрос будет существовать даже при последующих вызовах.

Наконец, я попытался выбрать только один столбец во втором запросе. Хотя это, очевидно, не даст мне нужной мне информации, я подумал, что это будет быстрее, чем ~ 200 столбцов, которые мы выбираем сейчас. Нет такого случая, все равно прошло 4-5 секунд.

Я вовсе не гуру LINQ, поэтому едва могу понять этот код (у меня такое ощущение, что он написан неоптимально, но я не могу понять его). Может ли кто-нибудь дать мне подсказку, почему эта проблема может возникнуть?

Единственное решение, которое у меня осталось, — вручную выбрать всю информацию вместо объединения всех этих таблиц. Тогда я получил бы около 5-6 запросов. Я думаю, не так уж плохо, но поскольку я не имею здесь дело с ужасно неэффективным SQL (или, по крайней мере, с приемлемым уровнем неэффективности), я надеялся предотвратить это.

Заранее спасибо, надеюсь, я объяснил. Если нет, не стесняйтесь спрашивать, и я предоставлю дополнительную информацию.


Изменить: В итоге я добавил ассоциации в свою структуру сущностей (в целевой базе данных не были указаны внешние ключи) и переписал запрос таким образом:

context.ROLE.Where(role => rolIds.Contains(role.FIDROLE) && role.Relation.FLDELETED != -1)
            .Select(role => new 
                            { 
                                ContactId = role.FIDROLE, 
                                Person = role.Person, 
                                Nationality = role.Person.Nationality.FTXTBNATION,
                                Title = role.Person.Title.FTXTBTITLE,
                                Suffix = role.Person.Suffix.FTXTBSUFFIX
                            });

Выглядит намного читабельнее и быстрее.

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

6
задан Olaf Keijsers 9 April 2012 в 21:44
поделиться