Как применить OrderBy к IQueryable, используя имя строкового столбца в универсальном методе расширения?
public static IQueryable<TResult> ApplySortFilter<T, TResult>(this IQueryable<T> query, string columnName)
where T : EntityObject
{
var param = Expression.Parameter(typeof(T), "o");
var body = Expression.PropertyOrField(param,columnName);
var sortExpression = Expression.Lambda(body, param);
return query.OrderBy(sortExpression);
}
потому что тип для OrderBy не выводится из sortExpression мне нужно указать его что-то вроде этого во время выполнения:
var sortExpression = Expression.Lambda<T, TSortColumn>(body, param);
или
return query.OrderBy<T, TSortColumn>(sortExpression);
Я не думаю, что это возможно, однако, как TSortColumn может быть определен только во время выполнения.
есть ли способ обойти это?
8 ответов:
мы сделали что-то подобное (не 100% то же самое, но похожее) в проекте LINQ to SQL. Вот код:
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values) { 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), "OrderBy", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp)); return source.Provider.CreateQuery<T>(resultExp); }
мы на самом деле не использовали общий, у нас был известный класс, но он должен работать на общем (я поставил общий заполнитель, где он должен быть).
Edit: для нисходящего порядка, пройдите в
OrderByDescending
вместо "OrderBy":MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderByDescending", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));
вы также можете использовать динамический Linq
информация здесь http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
C# скачать здесь http://msdn.microsoft.com/en-us/vcsharp/bb894665.aspx
затем просто добавьте с помощью Linq.Динамический; и вы автоматически получаете 2 дополнительных метода расширения, которые можно использовать следующим образом
return query.OrderBy("StringColumnName");
я расширил ваши функции, чтобы добавить поддержку дочерних свойств.
private static LambdaExpression GenerateSelector<TEntity>(String propertyName, out Type resultType) where TEntity : class { // Create a parameter to pass into the Lambda expression (Entity => Entity.OrderByField). var parameter = Expression.Parameter(typeof(TEntity), "Entity"); // create the selector part, but support child properties PropertyInfo property; Expression propertyAccess; if (propertyName.Contains('.')) { // support to be sorted on child fields. String[] childProperties = propertyName.Split('.'); property = typeof(TEntity).GetProperty(childProperties[0]); propertyAccess = Expression.MakeMemberAccess(parameter, property); for (int i = 1; i < childProperties.Length; i++) { property = property.PropertyType.GetProperty(childProperties[i]); propertyAccess = Expression.MakeMemberAccess(propertyAccess, property); } } else { property = typeof(TEntity).GetProperty(propertyName); propertyAccess = Expression.MakeMemberAccess(parameter, property); } resultType = property.PropertyType; // Create the order by expression. return Expression.Lambda(propertyAccess, parameter); } private static MethodCallExpression GenerateMethodCall<TEntity>(IQueryable<TEntity> source, string methodName, String fieldName) where TEntity : class { Type type = typeof(TEntity); Type selectorResultType; LambdaExpression selector = GenerateSelector<TEntity>(fieldName, out selectorResultType); MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName, new Type[] { type, selectorResultType }, source.Expression, Expression.Quote(selector)); return resultExp; }
вы можете использовать такие функции, как:
GenerateMethodCall<TEntity>(source, "OrderByDescending", fieldName);
я использовал вашу идею для метода расширения для OrderBy. Но в случае "многие ко многим", я получаю ошибку. Например, у вас есть таблица Site, Customer и Customer_site. Для данного Сайта Я хочу Сортировать по имени клиента и в расширении OrderBy (когда я передаю "сайт.customer " где customer-свойство навигации) я получаю ошибку в строке: propertyAccess = выражение.MakeMemberAccess(propertyAccess, свойство);
Это то, что я использую (с некоторыми расширениями :-) ):
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByValues) where TEntity : class { IQueryable<TEntity> returnValue = null; string orderPair = orderByValues.Trim().Split(',')[0]; string command = orderPair.ToUpper().Contains("DESC") ? "OrderByDescending" : "OrderBy"; var type = typeof(TEntity); var parameter = Expression.Parameter(type, "p"); string propertyName = (orderPair.Split(' ')[0]).Trim(); System.Reflection.PropertyInfo property; MemberExpression propertyAccess; if (propertyName.Contains('.')) { // support to be sorted on child fields. String[] childProperties = propertyName.Split('.'); property = typeof(TEntity).GetProperty(childProperties[0]); propertyAccess = Expression.MakeMemberAccess(parameter, property); for (int i = 1; i < childProperties.Length; i++) { Type t = property.PropertyType; if (!t.IsGenericType) { property = t.GetProperty(childProperties[i]); } else { property = t.GetGenericArguments().First().GetProperty(childProperties[i]); } propertyAccess = Expression.MakeMemberAccess(propertyAccess, property); } } else { property = type.GetProperty(propertyName); propertyAccess = Expression.MakeMemberAccess(parameter, property); } var orderByExpression = Expression.Lambda(propertyAccess, parameter); var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExpression)); returnValue = source.Provider.CreateQuery<TEntity>(resultExpression); if (orderByValues.Trim().Split(',').Count() > 1) { // remove first item string newSearchForWords = orderByValues.ToString().Remove(0, orderByValues.ToString().IndexOf(',') + 1); return source.OrderBy(newSearchForWords); } return returnValue; }
в отношении
С.
Кажется,этой - это способ сделать это, теперь убедитесь, что:
// ***** OrderBy(company => company) ***** // Create an expression tree that represents the expression // 'whereCallExpression.OrderBy(company => company)' MethodCallExpression orderByCallExpression = Expression.Call( typeof(Queryable), "OrderBy", new Type[] { queryableData.ElementType, queryableData.ElementType }, whereCallExpression, Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe })); // ***** End OrderBy *****
Если вы можете добавить " система.В LINQ.Динамический пакет" тогда, Слишком легко без каких-либо осложнений,
первый нет пакета "Системы.В LINQ.Динамический " из диспетчера пакетов NuGet затем попробуйте, как ниже, как вам нужно,
Ex:
public IQueryable<TEntity> GetWithInclude(Expression<Func<TEntity, bool>> predicate, List<string> sortBy, int pageNo, int pageSize = 12, params string[] include) { try { var numberOfRecordsToSkip = pageNo * pageSize; var dynamic = DbSet.AsQueryable(); foreach (var s in include) { dynamic.Include(s); } return dynamic.OrderBy("CreatedDate").Skip(numberOfRecordsToSkip).Take(pageSize); } catch (Exception e) { throw new Exception(e.Message); } }
надеюсь, это поможет
Я немного исправил этот код:https://stackoverflow.com/a/1670085/5852630
этот код работает с последовательной сортировкой: сначала выполнить "OrderBy", затем"ThenBy "(не"OrderBy"!)
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByValues) where TEntity : class { IQueryable<TEntity> returnValue = null; string[] orderPairs = orderByValues.Trim().Split(','); Expression resultExpression = source.Expression; string strAsc = "OrderBy"; string strDesc = "OrderByDescending"; foreach (string orderPair in orderPairs) { if (string.IsNullOrWhiteSpace(orderPair)) continue; string[] orderPairArr = orderPair.Trim().Split(' '); string propertyName = orderPairArr[0].Trim(); string orderNarrow = orderPairArr.Length > 1 ? orderPairArr[1].Trim() : string.Empty; string command = orderNarrow.ToUpper().Contains("DESC") ? strDesc : strAsc; Type type = typeof(TEntity); ParameterExpression parameter = Expression.Parameter(type, "p"); System.Reflection.PropertyInfo property; Expression propertyAccess; if (propertyName.Contains('.')) { // support to be sorted on child fields. String[] childProperties = propertyName.Split('.'); property = typeof(TEntity).GetProperty(childProperties[0]); propertyAccess = Expression.MakeMemberAccess(parameter, property); for (int i = 1; i < childProperties.Length; i++) { Type t = property.PropertyType; if (!t.IsGenericType) { property = t.GetProperty(childProperties[i]); } else { property = t.GetGenericArguments().First().GetProperty(childProperties[i]); } propertyAccess = Expression.MakeMemberAccess(propertyAccess, property); } } else { property = type.GetProperty(propertyName); propertyAccess = Expression.MakeMemberAccess(parameter, property); } if (property.PropertyType == typeof(object)) { propertyAccess = Expression.Call(propertyAccess, "ToString", null); } LambdaExpression orderByExpression = Expression.Lambda(propertyAccess, parameter); resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType == typeof(object) ? typeof(string) : property.PropertyType }, resultExpression, Expression.Quote(orderByExpression)); strAsc = "ThenBy"; strDesc = "ThenByDescending"; } returnValue = source.Provider.CreateQuery<TEntity>(resultExpression); return returnValue; }
вот моя адаптация от @Davy Landmanответ (я хотел метод расширения), и я немного упростил.
public static IQueryable<T> SortBy<T>(this IQueryable<T> source, String propertyName, WebControls.SortDirection direction) { if (source == null) throw new ArgumentNullException("source"); if (String.IsNullOrEmpty(propertyName)) return source; // Create a parameter to pass into the Lambda expression //(Entity => Entity.OrderByField). var parameter = Expression.Parameter(typeof(T), "Entity"); // create the selector part, but support child properties (it works without . too) String[] childProperties = propertyName.Split('.'); MemberExpression property = Expression.Property(parameter, childProperties[0]); for (int i = 1; i < childProperties.Length; i++) { property = Expression.Property(property, childProperties[i]); } LambdaExpression selector = Expression.Lambda(property, parameter); string methodName = (direction > 0) ? "OrderByDescending" : "OrderBy"; MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName, new Type[] { source.ElementType, property.Type }, source.Expression, Expression.Quote(selector)); return source.Provider.CreateQuery<T>(resultExp); }
Он может быть использован следующим образом:
gridview1.DataSource = DbContext.TB_CARS.SortBy("model", SortDirection.Descending); //OR gridview1.DataSource = DbContext.TB_CARS.SortBy("owner.first_name", 0);