2

我首先使用 C#、.Net 4.5、MVC、实体框架 5.0 和代码。我在使用 devexpress 中的示例之一时遇到了错误。问题在于获取 groupby 值和聚合(计数)查询的列表。

错误是无法将类型“System.Int32”转换为类型“System.Object”。LINQ to Entities 仅支持大小写 EDM 原语或枚举类型

实体/表是

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

获取分组信息的测试代码

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;
}

无论分组是在“Name”(字符串)类型还是“TotalValue”(int)类型上,它都会给我错误。任何人都可以帮忙吗?感谢有人告诉我为什么,什么以及如何,因为我仍在学习整个 .net、mvc 和 linq。

4

1 回答 1

0

我侦察你想要做的是这样的:

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() });

但使用手动创建Expressions的 .

您实际创建的内容以及问题所在是最后一个选择:

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

会给你相当于:

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

事实上,尝试执行这样的查询将导致 LINQ to Entities(以及 Entity Framework,就此而言)抱怨它无法强制转换为object.

它可以处理的是转换为string. 所以:

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

几乎可以了,但现在数组初始化器有问题:“无法在查询结果中初始化数组类型'System.String[]'。考虑使用'System.Collections.Generic.List`1[System.String ]' 反而。” 这很容易:

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

现在可以将其转换为 SQL(至少通过实体框架,但我想 Linq to SQL 也可以处理它)。所以,你应该arrayExpressions用这个替换:

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

现在它可以工作了。

总而言之,这是一种构建 Linq 查询的相当复杂的方法,它很难调试,甚至更难阅读。考虑使用泛型类型 IQueryable 并编写 lambda - 这就是Linq最初的设计目的。如果您有或想要坚持使用手动表达式,我建议您先编写一个 Linq 查询(也许针对更具体的情况),分析它生成的表达式,然后尝试手动创建这个表达式。

此外,将此表达式生成的 SQL 查询与应该执行相同工作的简单 linq 查询进行比较——第一个查询会更复杂。

于 2014-06-23T07:42:37.567 回答