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执行操作

Dynamics CRM Form表单中通过javascript抓取触发change事件字段的属性名

Dynamics CRM2016 Web API之获取查找字段的text及选项集的text