使用带有N-N连接的PredicateBuilder优化Linq查询

时间:2012-09-19 19:49:52

标签: linq-to-sql dynamics-crm-2011 predicatebuilder

我正在使用Linq查询MS CRM 2011 Web服务。我有一个查询导致SQL非常差,它获取了太多的中间数据,其性能非常糟糕!我是新手,所以它很可能是我使用它的方式......

我有两个通过N-N关系链接的实体:ProductSalesLink。我想从Product中恢复一堆SerialNumber以及与之关联的所有SalesLink

这是我使用PredicateBuilder的查询:

// Build inner OR predicate on Serial Number list
var innerPredicate = PredicateBuilder.False<Xrm.c_product>();
foreach (string sn in serialNumbers) {
   string temp = sn; // This temp assignement is important!
   innerPredicate = innerPredicate.Or(p => p.c_SerialNumber == temp);
}

// Combine predicate with outer AND predicate
var predicate = PredicateBuilder.True<Xrm.c_product>();
predicate = predicate.And(innerPredicate);
predicate = predicate.And(p => p.statecode == (int)CrmStateValueType.Active);

// Inner Join Query
var prodAndLinks = from p in orgContext.CreateQuery<Xrm.c_product>().AsExpandable()
                                                                    .Where(predicate)
                                                                    .AsEnumerable()
                   join link in orgContext.CreateQuery<Xrm.c_saleslink>()
                        on p.Id equals link.c_ProductSalesLinkId.Id
                   where link.statecode == (int)CrmStateValueType.Active
                   select new {
                         productId = p.Id
                       , productSerialNumber = p.c_SerialNumber
                       , accountId = link.c_Account.Id
                       , accountName = link.c_Account.Name
                   };
...

使用SQL分析器,我看到它导致没有WHERE子句的中间SQL查询,如下所示:

select 
top 5001 "c_saleslink0".statecode as "statecode"
  ...
, "c_saleslink0".ModifiedOnBehalfByName as "modifiedonbehalfbyname"
, "c_saleslink0".ModifiedOnBehalfByYomiName as "modifiedonbehalfbyyominame" 
from
 c_saleslink as "c_saleslink0" order by
 "c_saleslink0".c_saleslinkId asc

这会返回大量(无用的)数据。我认为连接是在客户端而不是数据库端完成的......

我该如何改进此查询?我跑了大约3分钟,这是完全不可接受的。

感谢。


“解决方案”

根据Daryl使用QueryExpression代替Linq到CRM的答案,我得到的结果完全相同。

var qe = new QueryExpression("c_product");
qe.ColumnSet = new ColumnSet("c_serialnumber");
var filter = qe.Criteria.AddFilter(LogicalOperator.Or);
filter.AddCondition("c_serialnumber", ConditionOperator.In, serialNumbers.ToArray());
var link = qe.AddLink("c_saleslink", "c_productid", "c_productsaleslinkid");
link.LinkCriteria.AddCondition("statecode", ConditionOperator.Equal, (int)CrmStateValueType.Active);
link.Columns.AddColumns("c_account");
var entities = serviceProxy.RetrieveMultiple(qe).Entities.ToList();;

var prodAndLinks = entities.Select(x => x.ToEntity<Xrm.c_product>()).Select(x => 
                   new {
                      productId = x.c_productId
                    , productSerialNumber = x.c_SerialNumber
                    , accountId = ((Microsoft.Xrm.Sdk.EntityReference)((Microsoft.Xrm.Sdk.AliasedValue)x["c_saleslink1.c_account"]).Value).Id
                    , accountName = ((Microsoft.Xrm.Sdk.EntityReference)((Microsoft.Xrm.Sdk.AliasedValue)x["c_saleslink1.c_account"]).Value).Name
                   }).ToList();

我真的很想找到一个使用Linq的解决方案,但似乎Linq到CRM还没有...

2 个答案:

答案 0 :(得分:1)

95%的情况下,当您在CRM中遇到复杂查询的性能问题时,提高性能的最简单方法是对数据库运行直接的SQL查询(假设当然不是CRM在线)。这可能是5%的时间之一。

在您的情况下,您遇到的主要性能问题是由于谓词构建器强制CRM服务器(而不是SQL数据库)端数据连接。如果您使用了查询表达式(这是您的链接语句得到的翻译),您可以使用IN运算符指定一个条件表达式,该运算符允许您传入serialNumbers集合。您也可以使用FetchXml。这两种方法都允许CRM执行SQL端连接。

编辑:

使用查询表达式可以获得80%的优势:

IOrganizationService service = GetService();
var qe = new QueryExpression("c_product");
var filter = qe.Criteria.AddFilter(LogicalOperator.Or);
filter.AddCondition("c_serialnumber", ConditionOperator.In, serialNumbers.ToArray());
var link = qe.AddLink("c_saleslink", "c_productid", "c_productsaleslinkid");
link.LinkCriteria.AddCondition("statecode", ConditionOperator.Equal, (int)CrmStateValueType.Active);
link.Columns.AddColumns("c_Account");
var entities = service.RetrieveMultiple(qe).Entities.ToList();

答案 1 :(得分:0)

你可能会发现你可以通过不使用Linq to Crm来获得更好的控制。你可以尝试:

  1. FetchXml,这是一种xml语法,类似于tsql MSDN

  2. QueryExpressionMSDN

  3. 您可以发出RetrieveRequestblog