gpt4 book ai didi

c# - 如何在运行时创建一个表达式以在 GroupBy() 中使用 Entity Framework?

转载 作者:行者123 更新时间:2023-11-30 18:32:00 24 4
gpt4 key购买 nike

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

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

新扩展方法的预期用途:

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

扩展方法定义:

    public static IQueryable<IGrouping<object, TEntity>> GroupBy<TEntity>(
this IQueryable<TEntity> source,
string fieldName)
where TEntity : class, IDataEntity
{
if (string.IsNullOrEmpty(fieldName))
{
return new List<IGrouping<object, TEntity>>().AsQueryable();
}

var parameter = Expression.Parameter(typeof(TEntity), "x");
var fieldXExpression = Expression.Property(parameter, fieldName);
var lambda = Expression.Lambda<Func<TEntity, object>>(
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 兼容的表达式?

感谢您的指导。

最佳答案

您不能按 object 类型的值进行分组不幸的是,在 Entity Framework 中。我认为有两种选择:您可以返回 IQueryable。来自该方法的(非通用)对象,如果您正在对结果进行数据绑定(bind)之类的操作,它将起作用,但对其他很多事情都没有好处。这使您可以忽略分组所依据的属性类型。

public static IQueryable GroupBy<TEntity>(
IQueryable<TEntity> source,
string fieldName)
where TEntity : class, IDataEntity
{
if (string.IsNullOrEmpty(fieldName))
{
return new List<IGrouping<object, TEntity>>().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<IGrouping<int,TEntity>> ),但是很难编写可以利用这一事实的代码, 你将无法将其转换为 IQueryable<IGrouping<object,TEntity>>如果fieldName是一个 int ,因为协方差不适用于值类型。

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

public static IQueryable<IGrouping<TProperty,TEntity>> GroupBy<TEntity, TProperty>(
IQueryable<TEntity> source,
string fieldName)
where TEntity : class, IDataEntity
{
if (string.IsNullOrEmpty(fieldName))
{
return new List<IGrouping<TProperty, TEntity>>().AsQueryable();
}

var parameter = Expression.Parameter(typeof(TEntity), "x");
var propertyAccess = Expression.Property(parameter, fieldName);
var lambda = Expression.Lambda<Func<TEntity, TProperty>>(propertyAccess, parameter);

var result = source.GroupBy (lambda);
return result;
}

关于c# - 如何在运行时创建一个表达式以在 GroupBy() 中使用 Entity Framework?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19164308/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com