21

Say I have a DataTable with four columns, Company (string), Fund (string), State (string), 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));

I want to use System.LINQ.Dynamic to build a dynamic query which groups on either Company, Fund, or State, and then selects my group by criteria as the first column, and sum(value):

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

In the above query, the selected groupbyvalue (Group) will always be a string, and the sum will always be a double, so I want to be able to cast into something like a List, where Result is an object with properties Group (string) and TotalValue (double).

I'm having a lot of trouble with this, can anyone shed some light?

4

4 回答 4

43

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

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

这应该使您的查询工作。更难的问题是如何将返回的对象(将具有继承自 的动态生成的类型DynamicClass)转换为静态类型。

选项 1:使用反射来访问动态对象的GroupTotalValue属性。

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

选项 3:修改动态库以支持强类型结果。事实证明这很简单:

  1. ExpressionParser.Parse()中,捕获私有字段中的类型参数:

    private Type newResultType;
    public Expression Parse(Type resultType)
    {
        newResultType = resultType;
        int exprPos = token.pos;
        // ...
    
  2. 接近尾声时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);
    }
    
  3. 最后,我们需要一个强类型版本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)");
于 2009-09-23T20:33:32.723 回答
3

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

1 扩展动态 linq 的 Select 方法以支持泛型类型。在这里查看第一个答案

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);
}
于 2012-10-03T09:11:17.060 回答
1

另一种可能性是扩展 DLinq 的查询语言,可以new在查询字符串的子句中指定类型名称。

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

它将允许您提出如下查询:

IQueryable<Result> res 
    = table1.AsQueryable()
    .GroupBy(groupbyvalue, "it")
    .Select("new Result(Key as Group, Sum(Value) as TotalValue)")
    as IQueryable<Result>;
于 2012-01-09T08:47:00.947 回答
0

另一种更简单的方法是使用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);
于 2018-03-20T15:49:10.003 回答