Ola*_*ers 6 c# linq-to-entities sql-server-2008-r2
我正在编写一段代码,由同事编写,与我们公司使用的CRM应用程序连接.在这段代码中有两个LINQ to Entities查询在我们的应用程序中执行了很多次,并且我被要求优化它们,因为其中一个非常慢.
这些是查询:
第一个查询,这个编译几乎立即.它从CRM数据库获取关系信息,按应用程序给出的关系ID列表进行过滤:
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 };
Run Code Online (Sandbox Code Playgroud)
第二个查询,编译大约需要4-5秒,并从数据库中获取有关人员的信息(再次按ID列表过滤):
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 };
Run Code Online (Sandbox Code Playgroud)
我已经设置了SQL事件探查器并从两个查询中获取了SQL,然后在SQL Server Management Studio中运行它.即使ID很大(~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
});
Run Code Online (Sandbox Code Playgroud)
似乎更具可读性,而且速度也更快.
感谢您的建议,我一定会考虑为不同数量的参数制作多个编译查询!
加布里埃尔的答案是正确的:使用编译的查询。
看起来您正在为每个 WCF 请求再次编译它,这当然违背了一次性初始化的目的。相反,请将编译的查询放入静态字段中。
编辑:
执行以下操作:向您的服务发送最大负载并暂停调试器 10 次。查看调用堆栈。它在 L2S 代码中还是在 ADO.NET 代码中停止得更频繁?这将告诉您问题是否仍出在 L2S 或 SQL Server 上。
接下来,让我们修复过滤器。我们需要将其推回已编译的查询中。这只有通过转换才能实现:
rolIds.Contains(role.FIDROLE)
Run Code Online (Sandbox Code Playgroud)
对此:
role.FIDROLE == rolIds_0 || role.FIDROLE == rolIds_1 || ...
Run Code Online (Sandbox Code Playgroud)
您需要对 rolId 的每个基数进行新的编译查询。这很糟糕,但有必要让它编译。在我的项目中,我已自动执行此任务,但您可以在此处执行一次性解决方案。
我想大多数查询都只有很少的角色 ID,因此您可以针对基数 1-10 实现 10 个编译查询,如果基数超过 10,您将回退到客户端过滤。