D365 CE中OrganizationServiceContext与QueryExpression查询结果差异原因
问题描述
我正在尝试在插件中使用OrganizationServiceContext执行LINQ查询以检索若干报价单,通过.Select()方法仅选取cgk_totalnetprice字段的值,代码如下:
quotes = OrganizationServiceContext.QuoteSet .Where(_ => _.OpportunityId != null && _.OpportunityId.Id == opportunityId && _.QuoteId != currentQuote.Id && (_.StatusCode.Value == (int)Quote_StatusCode.Won || _.StatusCode.Value == (int)Quote_StatusCode.WonOrder) && _.cgk_quotetypecode != null && (_.cgk_quotetypecode.Value == (int)QuoteTypeCode.Regular || _.cgk_quotetypecode.Value == (int)QuoteTypeCode.ServiceUnderWarranty)) .Select(x => new Quote() { Id = x.Id, cgk_totalnetprice = x.cgk_totalnetprice}) .ToList();
然而在检索这些报价单时,除了一份报价单外,上下文返回的其他所有报价单的该字段都没有值(且该份有值的报价单并非触发本次更新的记录,只是一份完全未被更新的随机记录)。
奇怪的是,当我将查询改写为QueryExpression写法时,一切运行完全正常:
QueryExpression qe = new QueryExpression("quote"); //Exclude current quote qe.Criteria.AddCondition("quoteid", ConditionOperator.NotEqual, currentQuote.Id); //Opportunity qe.Criteria.AddCondition("opportunityid", ConditionOperator.NotNull); qe.Criteria.AddCondition("opportunityid", ConditionOperator.Equal, opportunityId); //State-Status FilterExpression statusFilter = new FilterExpression(LogicalOperator.Or); statusFilter.AddCondition("statuscode", ConditionOperator.Equal, (int)Quote_StatusCode.Won); statusFilter.AddCondition("statuscode", ConditionOperator.Equal, (int)Quote_StatusCode.WonOrder); qe.Criteria.AddFilter(statusFilter); //QuoteType qe.Criteria.AddCondition("cgk_quotetypecode", ConditionOperator.NotNull); FilterExpression typeFilter = new FilterExpression(LogicalOperator.Or); typeFilter.AddCondition("cgk_quotetypecode", ConditionOperator.Equal, (int)QuoteTypeCode.Regular); typeFilter.AddCondition("cgk_quotetypecode", ConditionOperator.Equal, (int)QuoteTypeCode.ServiceUnderWarranty); qe.Criteria.AddFilter(typeFilter); qe.ColumnSet = new ColumnSet("quoteid", "cgk_totalnetprice"); quotes = this.OrganizationService.RetrieveMultiple(qe).Entities.Cast<Quote>().ToList();
请问是什么原因导致OrganizationServiceContext查询与OrganizationService+QueryExpression查询出现这种差异?
原因说明
这是Dataverse/Dynamics 365 SDK中OrganizationServiceContext的默认缓存机制导致的,核心有两点:
- 上下文默认启用身份映射缓存:所有通过该上下文加载过的实体都会被缓存在本地,后续再查询到相同ID的实体时,默认只会返回缓存中的旧实体,不会用服务端返回的新字段覆盖已缓存的实体。如果这些报价单之前已经被当前上下文加载过,且当时没有请求
cgk_totalnetprice字段,后续哪怕查询指定了这个字段,返回的依然是缓存里字段为空的旧实体。你观察到只有一份报价单有值,就是因为这份实体之前没有被加载到缓存里,是这次查询新增到缓存的,所以带了字段值。 - 上下文默认的
MergeOption是AppendOnly:该配置下仅会把首次查询到的实体加入缓存,后续查询到相同实体时不会更新已缓存实体的属性。而直接调用OrganizationService.RetrieveMultiple是绕过上下文缓存的,每次直接返回服务端的完整响应,所以不会出现字段丢失的问题。
解决方案
你可以任选一种方式修复:
- 执行LINQ查询前修改上下文的合并配置,跳过缓存:
OrganizationServiceContext.MergeOption = Microsoft.Xrm.Sdk.Client.MergeOption.NoTracking;
- 改写成投影到匿名类后再转换为强类型实体,避开缓存匹配逻辑:
quotes = OrganizationServiceContext.QuoteSet .Where(/* 原有过滤条件不变 */) .Select(x => new { x.Id, x.cgk_totalnetprice }) .ToList() .Select(item => new Quote { Id = item.Id, cgk_totalnetprice = item.cgk_totalnetprice }) .ToList();
- 如果不需要用上下文做后续更新操作,查询完成后直接销毁上下文实例,下次查询新建上下文即可,从根源避免缓存累积问题。
内容的提问来源于stack exchange,提问作者Pieter Dubois
相关产品推荐
相关产品推荐

