C# 从返回字典的 GroupBy 中选择 Expr

本文关键字:选择 Expr GroupBy 返回 字典 | 更新日期: 2023-09-27 18:33:43

Using 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")。编译())):

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 中提取字典值),那就太好了。

C# 从返回字典的 GroupBy 中选择 Expr

回答我自己的问题,因为注释对于代码来说太浅了。

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

 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);