Dynamics CRM SDK - 带有 OrganizationServiceContext 的 linq 的 IN 运算符
Posted
技术标签:
【中文标题】Dynamics CRM SDK - 带有 OrganizationServiceContext 的 linq 的 IN 运算符【英文标题】:Dynamics CRM SDK - IN operator for linq with OrganizationServiceContext 【发布时间】:2014-03-11 12:57:46 【问题描述】:我正在使用 svcutil 生成的 OrganizationServiceContext 实现从 CRM 中检索实体:
context.new_productSet.First(p => p.new_name == "Product 1");
是否可以一次检索具有不同属性值的多个实体-(类似于 SQL 中的 IN 运算符)?
示例:我想通过一次调用检索多个产品(“产品 1”、“产品 2”……)。产品名称列表是动态的,存储在名为 productNames 的数组中。
【问题讨论】:
【参考方案1】:不,你不能。 CRM LINQ 提供程序只允许变量出现在表达式的左侧,而右侧必须包含常量。
即
Product.Where(e => e.Name == desiredName)
不支持且无法工作(它会抱怨在比较右侧使用变量)。
如果您无法避免这种查询,则必须先.ToList()
数据(这可能会导致巨大结果集,并且可能会变得难以想象的缓慢):
Product.ToList().Where(e => e.Name == desiredName)
这会起作用,因为现在 .Where()
正被应用到 List<>
上。
另一种方法(不过我没有关于性能的数据)是创建许多查询,基本上一次获取一个记录:
// ... this is going to be a nightmare ... don't do it ...
var entities = new List<Product>();
entities.Add(Product.Where(e => e.Name == "Product 1"));
entities.Add(Product.Where(e => e.Name == "Product 2"));
或者像这样使用QueryExpression
(我个人最喜欢的,因为我总是迟到)
var desiredNames = new string[]"Product 1", "Product 2";
var filter = new FilterExpression(LogicalOperator.And)
Conditions =
new ConditionExpression("name", ConditionOperator.In, desiredNames)
;
var query = new QueryExpression(Product.EntityLogicalName)
ColumnSet = new ColumnSet(true),
Criteria = filter
;
var records = service.RetrieveMultiple(query).Entities;
【讨论】:
in 子句的最大限制是多少? 可以使用.AsEnumerable()
,而不是.ToList()
,然后使用Linq-to-objects对数据做进一步的转换。【参考方案2】:
如果结合Linq和Lambda表达式没问题,就可以了。首先你需要创建一个扩展方法:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace Kipon.Dynamics.Extensions.IQueryable
public static class Methods
public static IQueryable<TSource> WhereIn<TSource, TValue>(this IQueryable<TSource> source, Expression<Func<TSource, TValue>> valueSelector, IEnumerable<TValue> values)
if (null == source) throw new ArgumentNullException("source");
if (null == valueSelector) throw new ArgumentNullException("valueSelector");
if (null == values) throw new ArgumentNullException("values");
var equalExpressions = new List<BinaryExpression>();
foreach (var value in values)
var equalsExpression = Expression.Equal(valueSelector.Body, Expression.Constant(value));
equalExpressions.Add(equalsExpression);
ParameterExpression p = valueSelector.Parameters.Single();
var combined = equalExpressions.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));
var combinedLambda = Expression.Lambda<Func<TSource, bool>>(combined, p);
return source.Where(combinedLambda);
有了这个方法,您现在可以根据您的上下文使用它。首先记得导入扩展的命名空间,使该方法在 IQueryable 上可用:
using System.Linq;
using Kipon.Dynamics.Extensions.IQueryable;
public class MyClass
void myQueryMethod(CrmContext ctx, Guid[] contacts)
var accounts = (from a in ctx.accountSet.WhereIn(ac => ac.primarycontactid.id,contacts)
where a.name != null
select a).toArray();
据我所知,您无法连接到 Dynamics 365 Linq 表达式编译器,但上述代码将在针对 CRM 的一个请求中执行,并利用 使用 Linq 时不需要考虑分页等等。
如您所见,whereIn 子句添加了 lambda 样式表达式,而其余查询使用的是 Linq 样式。
【讨论】:
有没有办法将扩展 WhereIn() 与连接结合起来?试图这样做没有成功:(不过,很好的答案! 尝试在连接查询上执行 WhereIn,而不是在 WhereIn 查询上连接附加实体。执行顺序对 CRM SDK 查询映射器很重要。 (从 a in first join b in second on .... select new a, b ).WhereIn()【参考方案3】:在使用QueryExpression 时,我们可以在where 子句中添加条件表达式。 ConditionExpression 接受一个 ConditionOperator 枚举器,我们可以使用 ConditionOperator.In。下面是如何使用“In”运算符启动 conidtionExpression,第三个参数可以是数组或集合。
ConditionExpression ce = new ConditionExpression("EntityName",
ConditionOperator.In, collectionObject);
请参阅下面的详细说明。
http://msdn.microsoft.com/en-us/library/microsoft.xrm.sdk.query.conditionexpression.conditionexpression.aspx
【讨论】:
【参考方案4】:我不知道如何用 Linq 做到这一点,据我所知这是不可能的。
可以通过查询表达式来完成:
String[] productNames = new[] "test1", "test2" ;
QueryExpression products = new QueryExpression(Product.EntityLogicalName);
products.ColumnSet = new ColumnSet("name", "new_att1", "new_att2"); // fields to get
products.Criteria.AddCondition("name", ConditionOperator.In,
productNames.Cast<Object>().ToArray()); // filter by array
EntityCollection res = service.RetrieveMultiple(products);
IEnumerable<Product> opportunities = res.Entities
.Select(product => product.ToEntity<Product>()); // you can use Linq again from here
【讨论】:
以上是关于Dynamics CRM SDK - 带有 OrganizationServiceContext 的 linq 的 IN 运算符的主要内容,如果未能解决你的问题,请参考以下文章
是否可以在没有 SDK 的情况下调用 Dynamics CRM 2011 后期绑定 WCF 组织服务 - 直接自定义绑定?
Force SDK to create Appointment programmatically in crm 2013 onPremise
Dynamics CRM 2015/2016新特性之二十三:使用Web API执行函数
Dynamics CRM 2015/2016新特性之二十四:使用Web API执行操作