【问题标题】:Dynamics CRM SDK - IN operator for linq with OrganizationServiceContextDynamics CRM SDK - 带有 OrganizationServiceContext 的 linq 的 IN 运算符
【发布时间】:2014-03-11 12:57:46
【问题描述】:

我正在使用 svcutil 生成的 OrganizationServiceContext 实现从 CRM 中检索实体:

context.new_productSet.First(p => p.new_name == "Product 1");

是否可以一次检索具有不同属性值的多个实体-(类似于 SQL 中的 IN 运算符)?

示例:我想通过一次调用检索多个产品(“产品 1”、“产品 2”……)。产品名称列表是动态的,存储在名为 productNames 的数组中。

【问题讨论】:

    标签: dynamics-crm-2011 dynamics-crm


    【解决方案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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-22
        • 1970-01-01
        • 2021-09-17
        • 1970-01-01
        相关资源
        最近更新 更多