简体   繁体   English

使用动态表达式的组的IQueryable LINQ转换错误

[英]IQueryable LINQ cast error for group by using dynamic expression

I'm using C#, .Net 4.5, MVC, entity framework 5.0 with code first. 我先使用C#、. Net 4.5,MVC,实体框架5.0和代码。 I've encountered error on using one of the examples from the devexpress. 我在使用devexpress的示例之一时遇到错误。 The problem lies on getting a list of groupby value and aggregate (count) queries. 问题在于获取groupby值和聚合(计数)查询的列表。

The error is Unable to cast the type 'System.Int32' to type 'System.Object'. 该错误是无法将类型'System.Int32'强制转换为类型'System.Object'。 LINQ to Entities only supports casing EDM primitive or enumeration types . LINQ to Entities仅支持大小写EDM基本或枚举类型

The entity/table is 实体/表是

public class Test
{
    [Key]
    public int ID { get; set; }
    public string Name { get; set; }
    public DateTime SubmitDate { get; set; }
    public int TotalValue { get; set; }
}

Test code on getting the grouping information 有关获取分组信息的测试代码

public void GetGroupInfo() 
{
    GetGroupInfo(Context.Tests, "TotalValue");
    GetGroupInfo(Context.Tests, "Name");
}

public static void GetGroupInfo(this IQueryable query, string fieldName)
{
    CriteriaToExpressionConverter converter = new CriteriaToExpressionConverter();

    var rowType = query.ElementType;
    query = query.MakeGroupBy(converter, new OperandProperty(fieldName));
    query = query.MakeOrderBy(converter, new ServerModeOrderDescriptor(new OperandProperty("Key"), false));

    /*
      i think the problem is from here
    */
    query = ApplyExpression(query, rowType, "Key", "Count");

    // ignore the GridViewGroupInfo, just a class to store the value
    var list = new List<GridViewGroupInfo>();
    foreach (var item in query)
    {
        var obj = (object[])item;
        list.Add(new GridViewGroupInfo() {KeyValue=obj[0], DataRowCount =(int)obj[1]});
    }
}

static IQueryable ApplyExpression(IQueryable query, Type rowType, params string[] names)   
{
    var parameter = Expression.Parameter(query.ElementType, string.Empty);
    var expressions = names.Select(n => query.GetExpression(n, rowType, parameter));
    var arrayExpressions = Expression.NewArrayInit(
        typeof(object),
        expressions.Select(expr=>Expression.Convert(expr,typeof(object))).ToArray()
    );
    var lambda = Expression.Lambda(arrayExpressions, parameter);

    var expression = Expression.Call(
        typeof(Queryable),
        "Select",
        new Type[] { query.ElementType, lambda.Body.Type },
        query.Expression,
        Expression.Quote(lambda)
    );
    return query.Provider.CreateQuery(expression);
}

static Expression GetExpression(this IQueryable query, string commandName, Type rowType,    ParameterExpression parameter)
{
    switch (commandName)
    {
        case "Key":
            return Expression.Property(parameter, "Key");
        case "Count":
            return Expression.Call(typeof(Enumerable), "Count", new Type[] { rowType }, parameter);
    }
    return null;
}

It gives me error regardless the grouping is on the "Name" (string) type or the "TotalValue" (int) type. 无论分组是在“名称”(字符串)类型还是“ TotalValue”(整数)类型上,它都给我错误。 Anyone can help? 有人可以帮忙吗? Appreciate if anyone tells me why, what and how since I'm still learning about this entire .net, mvc & linq. 感谢是否有人告诉我原因,原因和方式,因为我仍在学习整个.net,mvc和linq。

I recon what you're trying to do is something like this: 我确认您要执行的操作是这样的:

Context.Tests.GroupBy(t => t.TotalValue).Select(g => new { Key = g.Key, Count = g.Count() });
Context.Tests.GroupBy(t => t.Name).Select(g => new { Key = g.Key, Count = g.Count() });

but using manually created Expressions . 但是使用手动创建的Expressions

What you're actually creating and where the problem lies is the last select: 您实际创建的以及问题所在是最后的选择:

 var arrayExpressions = Expression.NewArrayInit(
    typeof(object),
    expressions.Select(expr=>Expression.Convert(expr,typeof(object))).ToArray()
);

Will give you the equivalent of: 将为您提供相当于:

Select(g => new object[] { (object)g.Key, (object)g.Count() });

And indeed, trying to execute a query like this will result in LINQ to Entities (and Entity Framework too, for that matter) complaining it cannot do the cast to object . 确实,尝试执行这样的查询将导致LINQ to Entities(就此而言,也是Entity Framework)抱怨它无法进行object

What it can handle is casting to string . 它可以处理的是转换为string So: 所以:

Select(g => new string[] { g.Key.ToString(), g.Count().ToString() });

Is almost OK, but now there is a problem with the array initializer: "The array type 'System.String[]' cannot be initialized in a query result. Consider using 'System.Collections.Generic.List`1[System.String]' instead." 几乎可以,但是现在数组初始化程序存在问题: “无法在查询结果中初始化数组类型'System.String []'。请考虑使用'System.Collections.Generic.List`1 [System.String ]”。 That's easy: 这很容易:

Select(g => new List<string> { g.Key.ToString(), g.Count().ToString() });

And now that can be translated into SQL (at least by Entity Framework, but i suppose Linq to SQL can handle it too). 现在可以将其转换为SQL(至少是通过Entity Framework,但我想Linq to SQL也可以处理)。 So, you should replace arrayExpressions with this: 因此,您应该使用以下arrayExpressions替换arrayExpressions

var arrayExpressions = Expression.ListInit(Expression.New(typeof(List<string>)),
                expressions.Select(expr => Expression.Call(expr, "ToString", null)).ToArray()
            );

And now it works. 现在就可以了。

All in all, this is a rather complicated way to build Linq queries, it's hard to debug and even harder to read. 总而言之,这是一种构建Linq查询的相当复杂的方法,很难调试,甚至更难阅读。 Consider using generic type IQueryable and writing lambdas - that's what Linq was designed for in the first place. 考虑使用通用类型IQueryable并编写lambda-这是Linq首先设计的目的。 If you have or want to stick with manual Expressions, i would recommend writing a Linq query first (maybe for a more specific case), analyzing the Expression it generates and then trying to create this Expression manually. 如果您有或想要坚持使用手动表达式,我建议您先编写一个Linq查询(可能针对更具体的情况),分析它生成的表达式,然后尝试手动创建此表达式。

Also, compare the SQL query that this expression generates with a simple linq query that should do the same job - the first one will be way more complicated. 另外,将该表达式生成的SQL查询与应该执行相同工作的简单linq查询进行比较-第一个查询将更加复杂。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM