按字符串生成EF orderby Expression

我想通过字符串参数生成表达式,一些代码如:

private Expression<Func> Generate(string orderby) { switch (orderby) { case "Time": return t => t.Time; case "Money": return t => t.RewardMoney; default: return t => t.Id; } } 

然后叫它:

 _context.Items.OrderBy(Generate("Money")); 

但它无法编译! 我将T改为对象。

 private Expression<Func> Generate(string orderby) 

然后它可以编译,但它不起作用。

System.NotSupportedException:无法将类型“System.Int32”强制转换为“System.Object”类型。 LINQ to Entities仅支持转换EDM原语或枚举类型。

使用reflection和表达式树,您可以提供参数,然后调用OrderBy函数,而不是返回Expression> ,然后调用OrderBy

请注意, OrderBy是一个扩展方法,并在System.Linq.EnumarableSystem.Linq.Queryable类中实现。 第一个用于linq到对象 ,后者用于linq到实体 。 entity-framework需要查询的表达式树,以便将其转换为SQL命令。 所以我们使用Queryable实现。

它可以通过扩展方法完成(解释添加为注释):

 public static IOrderedQueryable OrderBy( this IEnumerable query, string propertyName) { var entityType = typeof(TSource); //Create x=>x.PropName var propertyInfo = entityType.GetProperty(propertyName); ParameterExpression arg = Expression.Parameter(entityType, "x"); MemberExpression property = Expression.Property(arg, propertyName); var selector = Expression.Lambda(property, new ParameterExpression[] { arg }); //Get System.Linq.Queryable.OrderBy() method. var enumarableType = typeof(System.Linq.Queryable); var method = enumarableType.GetMethods() .Where(m => m.Name == "OrderBy" && m.IsGenericMethodDefinition) .Where(m => { var parameters = m.GetParameters().ToList(); //Put more restriction here to ensure selecting the right overload return parameters.Count == 2;//overload that has 2 parameters }).Single(); //The linq's OrderBy has two generic types, which provided here MethodInfo genericMethod = method .MakeGenericMethod(entityType, propertyInfo.PropertyType); /*Call query.OrderBy(selector), with query and selector: x=> x.PropName Note that we pass the selector as Expression to the method and we don't compile it. By doing so EF can extract "order by" columns and generate SQL for it.*/ var newQuery = (IOrderedQueryable)genericMethod .Invoke(genericMethod, new object[] { query, selector }); return newQuery; } 

现在你可以调用OrderBy这个重载,就像它的任何其他重载一样。
例如:

 var cheapestItems = _context.Items.OrderBy("Money").Take(10).ToList(); 

这意味着:

 SELECT TOP (10) {coulmn names} FROM [dbo].[Items] AS [Extent1] ORDER BY [Extent1].[Money] ASC 

此方法可用于定义OrderByOrderByDescending方法的所有重载以具有string属性选择器。

您可以尝试在generics方法中转换Generate方法:

 private Expression> Generate(string orderby) { switch (orderby) { case "Time": return t => t.Time; case "Money": return t => t.RewardMoney; default: return t => t.Id; } } 

因此,如果您调用此方法,则需要指定要按以下顺序排列的属性的类型:

 _context.Items.OrderBy(Generate("Money")); 

现在请记住, TResult只能是原始类型或枚举类型。