System.LINQ.Dynamic:将Select(“new(…)”)select到List <T>(或任何其他的<T>的可枚举集合)

说我有一个DataTable与四列公司(string),基金(string),国家(string),价值(双):

table1.Rows.Add("Company 1","Fund 1","NY",100)); table1.Rows.Add("Company 2","Fund 1","CA",200)); table1.Rows.Add("Company 3","Fund 1","FL",300)); table1.Rows.Add("Company 4","Fund 2","CA",400)); table1.Rows.Add("Company 5","Fund 1","NY",500)); table1.Rows.Add("Company 6","Fund 2","CA",600)); table1.Rows.Add("Company 7","Fund 3","FL",700)); 

我想要使​​用System.LINQ.Dynamic构build一个dynamic查询,这个查询在公司,基金或者状态上进行分组,然后按照标准select我的组作为第一列,然后求和(value):

 string groupbyvalue="Fund"; var q1= table1.AsEnumerable().AsQueryable() .GroupBy(groupbyvalue,"it") .Select("new ("+groupbyvalue+" as Group, Sum(Value) as TotalValue)"); 

在上面的查询中,所选的groupbyvalue(Group)将始终是一个string,总和将总是一个double,所以我想能够转换成类似List的东西,其中Result是具有属性的对象Group(string )和TotalValue(双)。

我遇到了很多麻烦,谁能解释一下?

首先,您将在您的Select子句中以Key访问当前的分组值:

 .Select("new (Key as Group, Sum(Value) as TotalValue)"); 

这应该使您的查询工作。 更难的问题是如何将返回的对象(将具有从DynamicClassinheritance的dynamic生成的types)转换为静态types。

选项1:使用reflection来访问dynamic对象的GroupTotalValue属性。

选项2:使用编译的expression式树生成轻量级代码以访问GroupTotalValue属性。

选项3:修改dynamic库以支持强types的结果。 原来这很简单:

  1. ExpressionParser.Parse() ,在私有字段中捕获types参数:

     private Type newResultType; public Expression Parse(Type resultType) { newResultType = resultType; int exprPos = token.pos; // ... 
  2. ExpressionParser.ParseNew()的结尾处,我们将尝试在默认为dynamictypes之前使用newResultType

     Expression ParseNew() { // ... NextToken(); Type type = newResultType ?? DynamicExpression.CreateClass(properties); MemberBinding[] bindings = new MemberBinding[properties.Count]; for (int i = 0; i < bindings.Length; i++) bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]); return Expression.MemberInit(Expression.New(type), bindings); } 
  3. 最后,我们需要一个强types的Select()

     public static IQueryable<TResult> Select<TResult>(this IQueryable source, string selector, params object[] values) { if (source == null) throw new ArgumentNullException("source"); if (selector == null) throw new ArgumentNullException("selector"); LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(TResult), selector, values); return source.Provider.CreateQuery<TResult>( Expression.Call( typeof(Queryable), "Select", new Type[] { source.ElementType, typeof(TResult) }, source.Expression, Expression.Quote(lambda))); } 

    来自原始Select()的唯一更改是我们引用TResult地方。

现在我们只需要一个指定types来返回:

  public class Result { public string Group { get; set; } public double TotalValue { get; set; } } 

而你更新的查询将如下所示:

  IQueryable<Result> res = table1.AsQueryable() .GroupBy(groupbyvalue, "it") .Select<Result>("new (Key as Group, Sum(Value) as TotalValue)"); 

我通过以下方式将返回的数据转换为List <IExampleInterface>:

1扩展selectdynamicLINQ的方法来支持genericstypes。 在这里看第一个答案

2使用Select方法(非常类似于dahlbyk答案的第一行)

 Select<dynamic>("new (Key as Group, Sum(Value) as TotalValue)") 

如果你需要多列,你可以使用以下内容:

 GroupBy(@"new (Fund, Column1, Column2)", "it"). Select<dynamic>("new (Key.Fund, Key.Column1, Key.Column2, Sum(Value) as TotalValue)") 

3将数据转换为列表。

 var data = ... GroupBy("...").Select<dynamic>("..."). Cast<dynamic>().AsEnumerable().ToList(); 

4使用Impromptu将List转换为List <IExampleInterface>

 var result = new List<IExampleInterface>(); foreach (var d in data) { dynamic r = Impromptu.ActLike<IExampleInterface>(d); result.Add(r); } 

另一种可能性是扩展DLinq的查询语言,以便在查询string的new子句中指定types名称。

我已经在另一个stackoverflow答案中描述了dynamic.cs中需要的更改。

它将允许你提出像下面这样的查询:

 IQueryable<Result> res = table1.AsQueryable() .GroupBy(groupbyvalue, "it") .Select("new Result(Key as Group, Sum(Value) as TotalValue)") as IQueryable<Result>; 
Interesting Posts