【问题标题】:C# Select Expr off a GroupBy that returns a DictionaryC# 从返回字典的 GroupBy 中选择 Expr
【发布时间】:2014-12-08 14:45:39
【问题描述】:

使用 Net 4.5.1、C#....

使用 IQueryable 我有一个返回字典的 GroupBy 子句。这是通过以下代码完成的:

 public static Expression<Func<ChartJoin, Dictionary<string, object>>> GetGroupByDictionary(NameValueCollection fields)
    {
        var parameter = Expression.Parameter(typeof(ChartJoin));

        var addMethod = typeof(Dictionary<string, object>)
            .GetMethod(
                "Add",
                new[] { typeof(string), typeof(object) }
            );

        var selector = Expression.ListInit(
            Expression.New(typeof(Dictionary<string, object>)),
            fields.AllKeys.Select(
                key => Expression.ElementInit(
                    addMethod,
                    Expression.Constant(key),
                    Expression.Convert(
                        Chart.getNestedPropertyOrField(parameter, fields[key]),  // basically drills down to a nested property (note: static method not shown to save space)
                        typeof(object)
                    )
                )
            )
        );

        var lambda = Expression.Lambda<Func<ChartJoin, Dictionary<string, object>>>(selector, parameter);

        return lambda;
    }

然后调用是:

NameValueCollection fields = new NameValueCollection();
     fields.Add("Year", "Respondent.currentVisitYear");
     fields.Add("Month", "Respondent.currentVisitMonth");
     // .... could be more fields

<some IQueryable<ChartJoin>
     .GroupBy(
           Chart.GetGroupByDictionary(fields).Compile(), 
           new DictionaryComparer<string, object>()
      );

DictionaryComparer 允许唯一性,提供 Equals 和 GetHashCode 实现。我想返回带有 Select 子句的字典。仅尝试选择其中一个 GroupBy 键的简单示例(例如 Select(GetKey("Year").Compile())):

private static Expression<Func<IGrouping<IDictionary<string, object>, ChartJoin>, Dictionary<string, object>>> GetKey(String key)
{
     var block = ? /// Need logic to get a the IGrouping.Key property and pull the value

     var addMethod = typeof(Dictionary<string, object>)
            .GetMethod(
                "Add",
                new[] { typeof(string), typeof(object) }
            );

        var selector = Expression.ListInit(
            Expression.New(typeof(Dictionary<string, object>)),
            Expression.ElementInit(
                addMethod,
                Expression.Constant(key),
                Expression.Convert(
                    block,
                    typeof(object)
                )
            )
        );

        var lambda = Expression.Lambda<Func<IGrouping<IDictionary<string, object>, ChartJoin>, Dictionary<string, object>>>(selector, parameter);

        return lambda;
}

如果有人能让我从上述内容开始(即如何创建块表达式以从 GroupBy.Key 中提取 Dictionary 值),那就太好了。

【问题讨论】:

    标签: c# linq iqueryable


    【解决方案1】:

    回答我自己的问题,因为 cmets 代码空间太浅。

    基本上,经过一些实验后,为 Select 编写表达式并不是火箭科学。例如,这里是将组 Key 拉入选择的代码:

     private static Expression<Func<IGrouping<IDictionary<string, object>, Respondent>, IDictionary<string, object>>> GetKey(String field)
     {
          // x => 
          var ParameterType = typeof(IGrouping<IDictionary<string, object>, Respondent>);
          var parameter = Expression.Parameter(ParameterType, "x");
    
          // x => x.Key
          var Key = Expression.Property(parameter, "Key");
    
          // x => x.Key[field]
          Expression KeyExpression = Expression.Property(Key, "Item", new Expression[] { Expression.Constant(field) });
    
          ParameterExpression KeyResult = Expression.Parameter(typeof(object));
    
          BlockExpression block = Expression.Block(
               new[] { KeyResult },
               Expression.Assign(KeyResult, KeyExpression),
               KeyResult
          );
    
          ....  // <- the block is put in the selector (as shown above)
     }
    

    当我了解静态方法调用时,对 GroupBy 进行计数也很简单:

     // x => x.Count()
     MethodInfo CountMethod = (typeof(Enumerable))
          .GetMethods()
          .First(
               method => method.Name == "Count"
                    && method.IsGenericMethod
          )
          .MakeGenericMethod(typeof(Respondent));
     Expression CountExpression = Expression.Call(null, CountMethod, parameter);
    

    Sum 也很有趣:

     // x => x.Sum(m => m.MWEIGHT) where MWEIGHT is a ?decimal (i.e. nullable)
     var m = Expression.Parameter(typeof(Respondent), "m");
     PropertyInfo SumPropertyInfo = typeof(Respondent).GetProperty("MWEIGHT");
     Expression SumSelector = Expression.Lambda(
          Expression.Convert(Expression.MakeMemberAccess(m, SumPropertyInfo), typeof(decimal)), 
          m
     );
    
     MethodInfo SumMethod = (typeof(Enumerable))
          .GetMethods()
          .First(
               method => method.Name == "Sum"
                    && method.ReturnType == typeof(decimal)
                    && method.IsGenericMethod
           )
          .MakeGenericMethod(typeof(Respondent));
    
     Expression SumExpression = Expression.Call(null, SumMethod, parameter, SumSelector);
    

    【讨论】:

    • 您可以只使用 lambdas 来做到这一点,它会更容易。
    • @Servy,希望得到改进,所以在代码中抛出你的答案。
    • 事实证明,上述方法对 Enumerables 非常有用,但对 Queryables 无效,因为 Linq to EF 无法使用 Dictionary 或特定的 Block 表达式来访问字典。
    猜你喜欢
    • 1970-01-01
    • 2019-02-15
    • 1970-01-01
    • 1970-01-01
    • 2013-11-12
    • 2021-07-19
    • 2020-02-05
    • 1970-01-01
    • 2014-06-02
    相关资源
    最近更新 更多