C# – 使用属性名称作为string的属性代码
当我将属性名称作为string时,在C#中对属性进行编码的最简单方法是什么? 例如,我想允许用户通过他们select的属性(使用LINQ)来订购一些search结果。 他们将在UI中select“order by”属性 – 当然是一个string值。 有没有办法直接使用该string作为linq查询的属性,而不必使用条件逻辑(if / else,switch)将string映射到属性。 reflection?
从逻辑上讲,这是我想要做的事情:
query = query.OrderBy(x => x."ProductId");
更新:我最初没有指定我正在使用Linq实体 – 看来reflection(至less是GetProperty,GetValue方法)不会转换为L2E。
我会提供这个替代别人已经发布。
System.Reflection.PropertyInfo prop = typeof(YourType).GetProperty("PropertyName"); query = query.OrderBy(x => prop.GetValue(x, null));
这可以避免重复调用reflectionAPI来获取属性。 现在唯一重复的呼叫是获取值。
然而
我会主张使用PropertyDescriptor
,因为这样可以将自定义的TypeDescriptor
分配给您的types,从而可以使用轻量级操作来检索属性和值。 在没有自定义描述符的情况下,无论如何都会回到reflection。
PropertyDescriptor prop = TypeDescriptor.GetProperties(typeof(YourType)).Find("PropertyName"); query = query.OrderBy(x => prop.GetValue(x));
至于加快速度,请查看CodeProject上的Marc Gravel的HyperDescriptor
项目。 我用这个很成功, 它是对业务对象进行高性能数据绑定和dynamic属性操作的救命稻草。
我晚了一点,但是,我希望这可以有所帮助。
使用reflection的问题在于生成的expression式树几乎肯定不会被内部.Net提供者以外的任何Linq提供者支持。 这对于内部集合来说很好,但是在分页之前在源文件(比如SQL,MongoDb等)上进行sorting的地方是不行的。
下面的代码示例提供了OrderBy和OrderByDescending的IQueryable扩展方法,可以这样使用:
query = query.OrderBy("ProductId");
扩展方法:
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); } }
问候,马克。
是的,我不认为除了思考还有别的方法。
例:
query = query.OrderBy(x => x.GetType().GetProperty("ProductId").GetValue(x, null));
query = query.OrderBy(x => x.GetType().GetProperty("ProductId").GetValue(x, null));
试图从我头顶回忆确切的语法,但我认为这是正确的。
我喜欢@Mark Powell的答案,但是正如@ShuberFu所说,它给出的错误LINQ to Entities only supports casting EDM primitive or enumeration types
。
删除var propAsObject = Expression.Convert(property, typeof(object));
没有使用值types的属性,如整数,因为它不会隐式地将int对象框。
使用来自Kristofer Andersson和Marc Gravell的想法我find了一种使用属性名称构造Queryable函数的方法,并且它仍然可以与Entity Framework一起工作。 我还包括一个可选的IComparer参数。 警告: IComparer参数不适用于entity framework,如果使用Linq to Sql应该省略。
以下工作与entity framework和Linq到Sql:
query = query.OrderBy("ProductId");
而@西蒙Scheurer这也适用:
query = query.OrderBy("ProductCategory.CategoryId");
如果你没有使用entity framework或Linq到Sql,这个工程:
query = query.OrderBy("ProductCategory", comparer);
这里是代码:
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) ) ); } }
你可以使用dynamicLinq – 检查这个博客。
也检查出这个 StackOverFlow后…
反思就是答案!
typeof(YourType).GetProperty("ProductId").GetValue(theInstance);
有很多事情可以cachingreflection的PropertyInfo,检查错误的string,编写查询比较函数等等,但是就其本质而言,这是您的工作。