C# - 使用属性名称作为字符串按属性排序的代码
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1689199/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C# - code to order by a property using the property name as a string
提问by Jeremy
What's the simplest way to code against a property in C# when I have the property name as a string? For example, I want to allow the user to order some search results by a property of their choice (using LINQ). They will choose the "order by" property in the UI - as a string value of course. Is there a way to use that string directly as a property of the linq query, without having to use conditional logic (if/else, switch) to map the strings to properties. Reflection?
当我将属性名称作为字符串时,在 C# 中对属性进行编码的最简单方法是什么?例如,我希望允许用户通过他们选择的属性(使用 LINQ)对某些搜索结果进行排序。他们将在 UI 中选择“order by”属性——当然是作为字符串值。有没有办法直接将该字符串用作 linq 查询的属性,而不必使用条件逻辑(if/else、switch)将字符串映射到属性。反射?
Logically, this is what I'd like to do:
从逻辑上讲,这就是我想要做的:
query = query.OrderBy(x => x."ProductId");
Update: I did not originally specify that I'm using Linq to Entities - it appears that reflection (at least the GetProperty, GetValue approach) does not translate to L2E.
更新:我最初没有指定我使用 Linq to Entities - 似乎反射(至少 GetProperty、GetValue 方法)没有转换为 L2E。
采纳答案by Adam Robinson
I would offer this alternative to what everyone else has posted.
我会提供这个替代其他人发布的内容。
System.Reflection.PropertyInfo prop = typeof(YourType).GetProperty("PropertyName");
query = query.OrderBy(x => prop.GetValue(x, null));
This avoids repeated calls to the reflection API for obtaining the property. Now the only repeated call is obtaining the value.
这避免了重复调用反射 API 来获取属性。现在唯一重复的调用是获取值。
However
然而
I would advocate using a PropertyDescriptor
instead, as this will allow for custom TypeDescriptor
s to be assigned to your type, making it possible to have lightweight operations for retrieving properties and values. In the absence of a custom descriptor it will fall back to reflection anyhow.
我提倡使用 aPropertyDescriptor
代替,因为这将允许将自定义TypeDescriptor
s 分配给您的类型,从而可以使用轻量级操作来检索属性和值。在没有自定义描述符的情况下,它无论如何都会回退到反射。
PropertyDescriptor prop = TypeDescriptor.GetProperties(typeof(YourType)).Find("PropertyName");
query = query.OrderBy(x => prop.GetValue(x));
As for speeding it up, check out Marc Gravel's HyperDescriptor
project on CodeProject. I've used this with great success; it's a life saver for high-performance data binding and dynamic property operations on business objects.
至于加快速度,请查看 Marc GravelHyperDescriptor
在 CodeProject 上的项目。我已经成功地使用了它;它是对业务对象进行高性能数据绑定和动态属性操作的救星。
回答by Alon Gubkin
Yes, I don't think there's another way than Reflection.
是的,我认为除了 Reflection 之外没有其他方法。
Example:
例子:
query = query.OrderBy(x => x.GetType().GetProperty("ProductId").GetValue(x, null));
回答by Sebastian Good
Reflection is the answer!
反思就是答案!
typeof(YourType).GetProperty("ProductId").GetValue(theInstance);
There's lots of things you can do to cache the reflected PropertyInfo, check for bad strings, write your query comparison function, etc., but at its heart, this is what you do.
您可以做很多事情来缓存反射的 PropertyInfo、检查错误字符串、编写查询比较函数等,但从本质上讲,这就是您要做的。
回答by dkackman
query = query.OrderBy(x => x.GetType().GetProperty("ProductId").GetValue(x, null));
Trying to recall exact syntax off the top of my head but I think that is correct.
试图回忆起我脑海中的确切语法,但我认为这是正确的。
回答by Partha Choudhury
回答by Mark Powell
I'm a little late to the party, however, I hope this can be of some help.
我参加聚会有点晚了,但是,我希望这可以有所帮助。
The problem with using reflection is that the resulting Expression Tree will almost certainly not be supported by any Linq providers other than the internal .Net provider. This is fine for internal collections, however this will not work where the sorting is to be done at source (be that SQL, MongoDb, etc.) prior to pagination.
使用反射的问题是生成的表达式树几乎肯定不会被除内部 .Net 提供程序之外的任何 Linq 提供程序支持。这对于内部集合来说很好,但是在分页之前要在源(例如 SQL、MongoDb 等)上完成排序的情况下,这将不起作用。
The code sample below provides IQueryable extention methods for OrderBy and OrderByDescending, and can be used like so:
下面的代码示例为 OrderBy 和 OrderByDescending 提供了 IQueryable 扩展方法,可以像这样使用:
query = query.OrderBy("ProductId");
Extension Method:
扩展方法:
public static class IQueryableExtensions
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName)
{
return source.OrderBy(ToLambda<T>(propertyName));
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string propertyName)
{
return source.OrderByDescending(ToLambda<T>(propertyName));
}
private static Expression<Func<T, object>> ToLambda<T>(string propertyName)
{
var parameter = Expression.Parameter(typeof(T));
var property = Expression.Property(parameter, propertyName);
var propAsObject = Expression.Convert(property, typeof(object));
return Expression.Lambda<Func<T, object>>(propAsObject, parameter);
}
}
Regards, Mark.
问候,马克。
回答by David Specht
I liked the answer from @Mark Powell, but as @ShuberFusaid, it gives the error LINQ to Entities only supports casting EDM primitive or enumeration types
.
我喜欢@Mark Powell的回答,但正如@ShuberFu所说,它给出了错误LINQ to Entities only supports casting EDM primitive or enumeration types
。
Removing var propAsObject = Expression.Convert(property, typeof(object));
didn't work with properties that were value types, such as integer, as it wouldn't implicitly box the int to object.
删除var propAsObject = Expression.Convert(property, typeof(object));
不适用于值类型的属性,例如整数,因为它不会将 int 隐式装箱为对象。
Using Ideas from Kristofer Anderssonand Marc GravellI found a way to construct the Queryable function using the property name and have it still work with Entity Framework. I also included an optional IComparer parameter. Caution:The IComparer parameter does not work with Entity Framework and should be left out if using Linq to Sql.
使用Kristofer Andersson和Marc Gravell 的Ideas我找到了一种使用属性名称构造 Queryable 函数的方法,并且它仍然可以与实体框架一起使用。我还包含了一个可选的 IComparer 参数。注意:IComparer 参数不适用于实体框架,如果使用 Linq to Sql,则应将其排除。
The following works with Entity Framework and Linq to Sql:
以下适用于实体框架和 Linq to Sql:
query = query.OrderBy("ProductId");
And @Simon Scheurerthis also works:
而@Simon Scheurer这也适用:
query = query.OrderBy("ProductCategory.CategoryId");
And if you are not using Entity Framework or Linq to Sql, this works:
如果您不使用实体框架或 Linq to Sql,则此方法有效:
query = query.OrderBy("ProductCategory", comparer);
Here is the code:
这是代码:
public static class IQueryableExtensions
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, string propertyName, IComparer<object> comparer = null)
{
return CallOrderedQueryable(query, "OrderBy", propertyName, comparer);
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string propertyName, IComparer<object> comparer = null)
{
return CallOrderedQueryable(query, "OrderByDescending", propertyName, comparer);
}
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> query, string propertyName, IComparer<object> comparer = null)
{
return CallOrderedQueryable(query, "ThenBy", propertyName, comparer);
}
public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> query, string propertyName, IComparer<object> comparer = null)
{
return CallOrderedQueryable(query, "ThenByDescending", propertyName, comparer);
}
/// <summary>
/// Builds the Queryable functions using a TSource property name.
/// </summary>
public static IOrderedQueryable<T> CallOrderedQueryable<T>(this IQueryable<T> query, string methodName, string propertyName,
IComparer<object> comparer = null)
{
var param = Expression.Parameter(typeof(T), "x");
var body = propertyName.Split('.').Aggregate<string, Expression>(param, Expression.PropertyOrField);
return comparer != null
? (IOrderedQueryable<T>)query.Provider.CreateQuery(
Expression.Call(
typeof(Queryable),
methodName,
new[] { typeof(T), body.Type },
query.Expression,
Expression.Lambda(body, param),
Expression.Constant(comparer)
)
)
: (IOrderedQueryable<T>)query.Provider.CreateQuery(
Expression.Call(
typeof(Queryable),
methodName,
new[] { typeof(T), body.Type },
query.Expression,
Expression.Lambda(body, param)
)
);
}
}
回答by devi
More productive than reflection extension to dynamic order items:
比动态订单项目的反射扩展更高效:
public static class DynamicExtentions
{
public static object GetPropertyDynamic<Tobj>(this Tobj self, string propertyName) where Tobj : class
{
var param = Expression.Parameter(typeof(Tobj), "value");
var getter = Expression.Property(param, propertyName);
var boxer = Expression.TypeAs(getter, typeof(object));
var getPropValue = Expression.Lambda<Func<Tobj, object>>(boxer, param).Compile();
return getPropValue(self);
}
}
Example:
例子:
var ordered = items.OrderBy(x => x.GetPropertyDynamic("ProductId"));
Also you may need to cache complied lambas(e.g. in Dictionary<>)
此外,您可能需要缓存已编译的 labas(例如在 Dictionary<> 中)
回答by ali-myousefi
Also Dynamic Expressionscan solve this problem. You can use string-based queries through LINQ expressions that could have been dynamically constructed at run-time.
此外动态表达式可以解决这个问题。您可以通过可以在运行时动态构造的 LINQ 表达式使用基于字符串的查询。
var query = query
.Where("Category.CategoryName == @0 and Orders.Count >= @1", "Book", 10)
.OrderBy("ProductId")
.Select("new(ProductName as Name, Price)");
回答by Abolfazl
I think we can use a powerful tool name Expression an in this case use it as an extension method as follows:
我认为我们可以使用一个强大的工具名称 Expression 并在这种情况下将其用作扩展方法,如下所示:
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, bool descending)
{
var type = typeof(T);
var property = type.GetProperty(ordering);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
MethodCallExpression resultExp =
Expression.Call(typeof(Queryable), (descending ? "OrderByDescending" : "OrderBy"),
new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));
return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(resultExp);
}