【问题标题】:IQueryable GroupBy Lambda expression fails for primitive types with object key对于具有对象键的原始类型,IQueryable GroupBy Lambda 表达式失败
【发布时间】:2018-03-11 09:06:45
【问题描述】:

我正在为 IQueryable<TSource> 创建 Lambda 表达式,以下是我的扩展方法代码,我需要调用它:

queryableData.GroupBy<int,Product>("ID")

queryableData.GroupBy<string,Product>("Name")

public static IQueryable<IGrouping<TKey,TSource>> GroupBy<TKey,TSource>(this IQueryable<TSource> queryable, string propertyName)
    {
        // Access the propertyInfo, using Queryable Element Type (Make it Case insensitive)
        var propInfo = queryable.ElementType.GetProperty(propertyName,BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);

        // Access the Collection / Queryable Type
        var collectionType = queryable.ElementType;

        // Creating Group Parameter Expression
        var groupParameterExpression = Expression.Parameter(collectionType, "g");

        // Create MemberEXpression with the Property (access the property of a Type)
        var propertyAccess = Expression.MakeMemberAccess(groupParameterExpression, propInfo);

        // Create Lambda Expression
        var lambdaExpression = Expression.Lambda<Func<TSource,TKey>>(propertyAccess, groupParameterExpression);

        // Return GroupBy result
        return queryable.GroupBy(lambdaExpression);
    }

我的目标是生成Expression&lt;Func&lt;TSource,object&gt;&gt;而不是Expression&lt;Func&lt;TSource,TKey&gt;&gt;,这样就可以在不提供Key类型的情况下调用它,代码如下:

public static IQueryable<IGrouping<object, TSource>> GroupByObjectKey<TSource>(this IQueryable<TSource> queryable, string propertyName)
    {
        // Access the propertyInfo, using Queryable Element Type (Make it Case insensitive)
        var propInfo = queryable.ElementType.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);

        // Access the Collection / Queryable Type
        var collectionType = queryable.ElementType;

        // Creating Group Parameter Expression
        var groupParameterExpression = Expression.Parameter(collectionType, "g");

        // Create MemberEXpression with the Property (access the property of a Type)
        var propertyAccess = Expression.MakeMemberAccess(groupParameterExpression, propInfo);

        // Create Lambda Expression
        var lambdaExpression = Expression.Lambda<Func<TSource, object>>(propertyAccess, groupParameterExpression);

        // Return GroupBy result
        return queryable.GroupBy(lambdaExpression);
    }

现在我可以使它适用于字符串类型,如下所示:

queryableData.GroupBy&lt;Product&gt;("Name")

但它在以下调用整数类型时失败,如下所述:

queryableData.GroupBy&lt;Product&gt;("Id")

Expression of type 'System.Int32' cannot be used for return type 'System.Object'

这是一个类型转换的明显案例,但我很惊讶为什么一个类型会拒绝转换为 Object 基类,可能是什么原因,任何指针/建议

【问题讨论】:

  • this 会在这里申请吗?当然还有this
  • 第二个可以,但我不知道为什么所有东西都需要转换为 Object 类型。它适用于引用类型,但不适用于原始类型。
  • 我会按照“此处需要显式转换”的方式来回答,但不能说比Eric Lippert here 更好; derives fromconvertible to 不同:“纠正这个迷思的方法是简单地将“derives from”替换为“is convertible to”,并忽略指针类型:C# 中的每个非指针类型都可转换为对象"
  • 这是表达式的陷阱之一:值类型装箱需要明确的Convert 指令。它类似于编译器在您编写诸如return (object)0; 之类的代码时为您执行的操作。只需接受值类型不同的事实,并顺其自然。
  • 其他评论者已经提到您需要将值类型返回表达式 (propertyAccess) 与Expression.Convert 包装起来。这将允许您成功构建您的查询。但请注意,如果您打算在 EF6 查询中使用它,您将得到简单的运行时异常 无法将类型“System.Int32”转换为类型“System.Object”。 LINQ to Entities 仅支持转换 EDM 基元或枚举类型。。一般来说,方差不适用于值类型。 GroupByTKey 参数是有原因的。

标签: c# linq expression-trees iqueryable


【解决方案1】:

正如 cmets 已经指出的,您需要添加转换为object

var convertToObject = Expression.Convert(propertyAccess, typeof(object));

var lambdaExpression = Expression.Lambda<Func<TSource, object>>(convertToObject, groupParameterExpression);

【讨论】:

  • 谢谢,虽然我找到了解决方案,但一旦我了解了与类型转换相关的要求
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多