如何在运行时创建表达式以在具有entity framework的GroupBy()中使用?

我正在构建一个新的扩展方法,它将动态地能够对来自Entity Framework的查询结果进行分组。

我已经能够使用LinqKit构建动态“where”表达式,但这似乎是一个不同的动物。

新扩展方法的预期用法:

var results = entities.GroupBy("someFieldName").ToList(); 

扩展方法定义:

  public static IQueryable<IGrouping> GroupBy( this IQueryable source, string fieldName) where TEntity : class, IDataEntity { if (string.IsNullOrEmpty(fieldName)) { return new List<IGrouping>().AsQueryable(); } var parameter = Expression.Parameter(typeof(TEntity), "x"); var fieldXExpression = Expression.Property(parameter, fieldName); var lambda = Expression.Lambda<Func>( Expression.Convert(fieldXExpression, typeof(object)), // throws error when using EF parameter); return source.AsExpandable().GroupBy(lambda); } 

我需要使用Expression.Convert(...)因为当我使用linqTOobjects进行测试时,代码在列为int32时失败。 所以我需要手动将列转换为对象,它工作得很好。

现在我正在使用EF实体测试它,我想EF正在尝试将转换转换为等效的SQL,当然我知道它不存在。

错误:

 System.NotSupportedException: Unable to cast the type 'System.String' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types. 

有没有办法在运行时生成适合GroupBy的表达式,它也与EF兼容?

感谢您的指导。

不幸的是,您不能按entity framework中的object类型进行分组。 我认为有两种选择:你可以从方法中返回一个IQueryable (非generics)对象,如果你正在对结果进行数据绑定,那么它将起作用,但对其他方法并不好。 这允许您忽略要分组的属性的类型。

 public static IQueryable GroupBy( IQueryable source, string fieldName) where TEntity : class, IDataEntity { if (string.IsNullOrEmpty(fieldName)) { return new List>().AsQueryable(); } var parameter = Expression.Parameter(typeof(TEntity), "x"); var propertyAccess = Expression.Property(parameter, fieldName); var lambda = Expression.Lambda(propertyAccess, parameter); var groupExpression = Expression.Call( typeof(Queryable).GetMethods() .First (x => x.Name == "GroupBy") .MakeGenericMethod(new[]{ typeof(TEntity), propertyAccess.Type }), source.Expression, lambda); var result = source.Provider.CreateQuery(groupExpression); return result; } 

从此方法返回的IQueryable将是正确的类型(如果fieldName引用int属性,您可以将结果转换为IQueryable> ),但是编写可能需要的代码将会很困难这个事实的优点,如果fieldNameint ,你将无法将它IQueryable>IQueryable> ,因为协方差不适用于值类型。

另一个选项仅在您知道调用位置的属性类型时才可用,这样您就可以在方法参数中提供分组键的类型(这是您能够正确获取的唯一方法类型,通用IQueryable):

 public static IQueryable> GroupBy( IQueryable source, string fieldName) where TEntity : class, IDataEntity { if (string.IsNullOrEmpty(fieldName)) { return new List>().AsQueryable(); } var parameter = Expression.Parameter(typeof(TEntity), "x"); var propertyAccess = Expression.Property(parameter, fieldName); var lambda = Expression.Lambda>(propertyAccess, parameter); var result = source.GroupBy (lambda); return result; } 

通过使用Dynamic Linq,我能够完成我所需要的。 我创建了一个名为ListGroupKeys的扩展方法:

  public static List ListGroupKeys(this IQueryable source, string fieldName) where TEntity : class, IDataEntity { var results = source.GroupBy(fieldName, "it").Select("new (KEY as Group)"); var list = new List(); foreach (var result in results) { var type = result.GetType(); var prop = type.GetProperty("Group"); list.Add(prop.GetValue(result)); } return list; } 

这允许我获取组密钥值以供在后续查询中使用。