说我有一个四列,公司(串),基金(串),国家(串),值(双)数据表:
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建立一个动态查询在任公司,基金,或国家,这组,然后通过标准的第一列,和(数值)选择我的组:
string groupbyvalue="Fund";
var q1= table1.AsEnumerable().AsQueryable()
.GroupBy(groupbyvalue,"it")
.Select("new ("+groupbyvalue+" as Group, Sum(Value) as TotalValue)");
在上面的查询,选定groupbyvalue(集团)将永远是一个字符串,总和将永远是一个双重的,所以我希望能够投进去的东西就像一个列表,其中结果是地产集团的对象(字符串)及总价值(双)。
我有很多的麻烦这一点,任何人都可以提供一些线索?
首先,你访问当前分组值作为Key
在SELECT子句:
.Select("new (Key as Group, Sum(Value) as TotalValue)");
这应该使您的查询工作。 更难的问题是如何将返回的对象,这将具有从继承的动态生成的类型DynamicClass
,成静态类型。
方法1:使用反射来访问动态对象的Group
和TotalValue
属性。
选项2:轻量级代码生成编译使用表达式树访问Group
和TotalValue
属性。
方案3:修改动态库,支持强类型的结果。 这原来是很简单的:
在ExpressionParser.Parse()
捕获一个私有字段的类型参数:
private Type newResultType; public Expression Parse(Type resultType) { newResultType = resultType; int exprPos = token.pos; // ...
临近年底ExpressionParser.ParseNew()
我们将尝试使用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); }
最后,我们需要的强类型版本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
。
现在,我们只需要一个命名的类型返回:
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)");
我铸造通过以下方式返回的数据列表<IExampleInterface>:
1扩展动态LINQ的选择方法来支持泛型类型。 见第一个答案在这里
2使用选择方法(非常类似于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使用即兴到列表转换为列表<IExampleInterface>
var result = new List<IExampleInterface>();
foreach (var d in data)
{
dynamic r = Impromptu.ActLike<IExampleInterface>(d);
result.Add(r);
}
另一种可能性是延长的DLinq的查询语言与可能性,在指定类型名new
查询字符串中的条款。
我所描述的变化的Dynamic.cs需要在另一计算器的答案 。
它可以让你提出类似下面的查询:
IQueryable<Result> res
= table1.AsQueryable()
.GroupBy(groupbyvalue, "it")
.Select("new Result(Key as Group, Sum(Value) as TotalValue)")
as IQueryable<Result>;
另外,更容易的方式做到这一点是使用Newtonsoft.Json像这样使用JSON序列化/反序列化:
具有与希望属性的类:
public class MyClass
{
public string Group;
public double Total;
}
查询后,你的JSON序列化,然后反序列化到你想要的数据类型:
string jsonData = JsonConvert.SerializeObject(q1);
List<MyClass> typedResult = JsonConvert.DeserializeObject<List<MyClass>>(jsonData);