C# - код для заказа по свойству с использованием имени свойства в виде строки
каков самый простой способ кодирования для свойства В C#, когда у меня есть имя свойства в виде строки? Например, я хочу разрешить пользователю заказывать некоторые результаты поиска по свойству по своему выбору (используя LINQ). Они выберут свойство "order by" в пользовательском интерфейсе - как строковое значение, конечно. Есть ли способ использовать эту строку непосредственно в качестве свойства запроса linq, не используя условную логику (if/else, switch) для сопоставления строк со свойствами. Отражение?
логически, это то, что я хочу сделать:
query = query.OrderBy(x => x."ProductId");
обновление:
Я изначально не указывал, что я использую Linq для сущностей-похоже, что отражение (по крайней мере, подход GetProperty, GetValue) не переводится в L2E.
9 ответов:
Я бы предложил эту альтернативу тому, что все остальные опубликовали.
System.Reflection.PropertyInfo prop = typeof(YourType).GetProperty("PropertyName"); query = query.OrderBy(x => prop.GetValue(x, null));это позволяет избежать повторных вызовов API отражения для получения свойства. Теперь единственный повторный вызов-это получение значения.
Я бы выступил за использование
PropertyDescriptorвместо этого, как это позволит таможниTypeDescriptors для присвоения вашего типа, что позволяет иметь легкие операции для извлечения свойств и значений. В отсутствие пользовательского дескриптора он все равно вернется к отражению.PropertyDescriptor prop = TypeDescriptor.GetProperties(typeof(YourType)).Find("PropertyName"); query = query.OrderBy(x => prop.GetValue(x));Что касается ускорения, проверьте Marc Gravel's
HyperDescriptorпроект на CodeProject. Я использовал это с большим успехом; это экономит жизнь для высокопроизводительной привязки данных и динамических операций с свойствами на бизнес-объектах.
Я немного опоздал на вечеринку, однако, я надеюсь, что это может помочь.
проблема с использованием отражения заключается в том, что результирующее дерево выражений почти наверняка не будет поддерживаться никакими поставщиками Linq, кроме внутреннего поставщика .Net. Это отлично подходит для внутренних коллекций, однако это не будет работать там, где сортировка должна выполняться в источнике (будь то SQL, MongoDb и т. д.) перед разбиением на страницы.
приведенный ниже пример кода предоставляет IQueryable методы расширения для OrderBy и OrderByDescending, и могут быть использованы следующим образом:
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); } }С Уважением, Марк.
мне понравился ответ от @Mark Powell, а как @ShuberFu сказал, это дает ошибку
LINQ to Entities only supports casting EDM primitive or enumeration types.удаление
var propAsObject = Expression.Convert(property, typeof(object));не работал со свойствами, которые были типами значений, такими как integer, поскольку он не будет неявно вставлять int в объект.используя идеи из Кристофер Андерссон и Марк Gravell я нашел способ построить запрашиваемую функцию, используя имя свойства, и она все еще работает платформа Entity Framework. Я также включил дополнительный параметр IComparer. внимание: параметр IComparer не работает с Entity Framework и должен быть исключен при использовании Linq to Sql.
следующие работы с Entity Framework и Linq to Sql:
query = query.OrderBy("ProductId");и @Simon Scheurer это также работает:
query = query.OrderBy("ProductCategory.CategoryId");и если вы не используете Entity Framework или Linq to 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) ) ); } }
Да, я не думаю, что есть другой путь, чем отражение.
пример:
query = query.OrderBy(x => x.GetType().GetProperty("ProductId").GetValue(x, null));
query = query.OrderBy(x => x.GetType().GetProperty("ProductId").GetValue(x, null));пытаясь вспомнить точный синтаксис с верхней части моей головы, но я думаю, что это правильно.
отражение-это ответ!
typeof(YourType).GetProperty("ProductId").GetValue(theInstance);есть много вещей, которые вы можете сделать, чтобы кэшировать отраженный PropertyInfo, проверить наличие плохих строк, написать функцию сравнения запросов и т. д. но в глубине души, это то, что вы делаете.
и Динамические Выражения может решить эту проблему. Вы можете использовать строковые запросы через выражения LINQ, которые могли быть динамически построены во время выполнения.
var query = query .Where("Category.CategoryName == @0 and Orders.Count >= @1", "Book", 10) .OrderBy("ProductId") .Select("new(ProductName as Name, Price)");
более продуктивно, чем расширение отражения для динамических элементов заказа:
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); } }пример:
var ordered = items.OrderBy(x => x.GetPropertyDynamic("ProductId"));
Comments