假设我有一个包含四列的DataTable,Company(字符串),Fund(字符串),State(字符串),Value(double):
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构建一个动态查询,该查询在公司,基金或州上分组,然后按条件选择我的组作为第一列,并且sum(value):
string groupbyvalue="Fund";
var q1= table1.AsEnumerable().AsQueryable()
.GroupBy(groupbyvalue,"it")
.Select("new ("+groupbyvalue+" as Group, Sum(Value) as TotalValue)");
在上面的查询中,所选的groupbyvalue(Group)将始终是一个字符串,并且总和将始终为double,因此我希望能够转换为类似List的内容,其中Result是具有属性的对象Group(string)和TotalValue(double)。
我遇到很多麻烦,有人可以解决这个问题吗?
答案 0 :(得分:42)
首先,您将在Select子句中将当前分组值作为Key
访问:
.Select("new (Key as Group, Sum(Value) as TotalValue)");
这应该使您的查询工作。更难的问题是如何将返回的对象(将具有从DynamicClass
继承的动态生成的类型)转换为静态类型。
选项1:使用反射来访问动态对象的Group
和TotalValue
属性。
选项2:使用编译的表达式树生成轻量级代码,以访问Group
和TotalValue
属性。
选项3:修改动态库以支持强类型结果。事实证明这很简单:
在ExpressionParser.Parse()
中,捕获私有字段中的type参数:
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)");
答案 1 :(得分:3)
我将返回的数据转换为List&lt; IExampleInterface&gt;通过以下方式:
1 Extend选择动态linq的方法以支持泛型类型。请参阅第一个答案here
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将列表转换为列表&lt; IExampleInterface&gt;
var result = new List<IExampleInterface>();
foreach (var d in data)
{
dynamic r = Impromptu.ActLike<IExampleInterface>(d);
result.Add(r);
}
答案 2 :(得分:1)
另一种可能性是扩展DLinq的查询语言,并可以在查询字符串的new
子句中指定类型名称。
我已在另一个stackoverflow answer中描述了 Dynamic.cs 中的更改需求。
它允许您提出如下查询:
IQueryable<Result> res
= table1.AsQueryable()
.GroupBy(groupbyvalue, "it")
.Select("new Result(Key as Group, Sum(Value) as TotalValue)")
as IQueryable<Result>;
答案 3 :(得分:0)
另一种更简单的方法是使用 Newtonsoft.Json 来使用Json序列化/反序列化:
有一个包含所需属性的类:
public class MyClass
{
public string Group;
public double Total;
}
在查询之后,您将Json-serialize然后反序列化为您想要的数据类型:
string jsonData = JsonConvert.SerializeObject(q1);
List<MyClass> typedResult = JsonConvert.DeserializeObject<List<MyClass>>(jsonData);