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 можно определить только во время выполнения.
Это можно обойти?
@JTew Как я могу реализовать второй заказ по предложению .. скажите заказ по идентификатору, а затем по дате





Кажется, что это - это способ сделать это, теперь, чтобы убедиться, что:
// ***** 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 *****
блин, 34 секунды позади! :П
Мы сделали нечто подобное (не на 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);
}
На самом деле мы не использовали универсальный класс, у нас был известный класс, но он должен работать с универсальным (я поместил общий заполнитель там, где он должен быть).
Редактировать: В порядке убывания передайте OrderByDescending вместо OrderBy:
MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderByDescending", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));
Хех нет проблем, я все равно не могу назначить себе ответ :)
для нисходящего порядка передайте «OrderByDescending» вместо «OrderBy» MethodCallExpression resultExp = Expression.Call (typeof (Queryable), «OrderByDescending», ...
Это сработало отлично, но это был просто хороший пример чистого кода: stackoverflow.com/questions/41244/dynamic-linq-orderby
@Aaron Powell Как я могу реализовать второй заказ по предложению ... скажите заказ по идентификатору, а затем по дате
@SRJ, вы бы сделали это, используя метод ThenBy в LINQ, поэтому во второй последней строке будет "ThenBy", а не "OrderBy".
Склонно ли это к внедрению кода любого вида (требуется ли проверка)?
Для чего нужен параметр values?
Привет. я пытаюсь выполнить ту же задачу. Я пытался использовать ваш код, но у меня возникла одна проблема, например (System.Linq.Expressions.IArgumentProvider.ArgumentCount = '((System.Linq.Expressions.IArgumentProvider) resultExp) .ArgumentCount' вызвал исключение типа 'System .InvalidOperationException '), который выдает перед оператором возврата. Это исключение можно найти только в режиме отладки. Любая помощь, пожалуйста?
значения не используются
Я расширил ваши функции, добавив поддержку дочерних свойств.
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);
Ты мой герой !!
должен любить умных людей
Я пробовал этот код, и он работает с одним ребенком, но не более чем с одним, например он работает с сортировкой по x.String и x.Object.String, но не с сортировкой по x.Object.Object.String.
Вы также можете использовать Dynamic 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
Затем просто добавьте using Linq.Dynamic; и вы автоматически получаете 2 дополнительных метода расширения, которые можно использовать следующим образом
return query.OrderBy("StringColumnName");
Спасибо, я видел Linq.Dynamic в образце на сайте Фила Хаака, но не был в этом уверен. Я поиграю с этим на выходных.
В качестве альтернативы Systems.Linq.Dynamic.dll можно загрузить отсюда: github.com/kahanu/System.Linq.Dynamic
Я использовал вашу идею для метода расширения для OrderBy. Но в случае «многие ко многим» я получаю ошибку. Например, у вас есть таблица Site, Customer и Customer_site. Для данного сайта я хочу отсортировать по имени клиента и в расширении OrderBy (когда я передаю «site.customer», где клиент является свойством навигации), я получаю сообщение об ошибке в строке: propertyAccess = Expression.MakeMemberAccess (propertyAccess, property);
Вот что я использую (с некоторыми улучшениями :-)):
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;
}
С уважением
Слободан
Если вы можете добавить пакет "System.Linq.Dynamic", тогда Слишком просто без каких-либо осложнений,
первый пакет insatll "System.Linq.Dynamic" из диспетчера пакетов NuGet, затем попробуйте, как показано ниже, как вам нужно,
Бывший:
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;
}
Вот моя адаптация из ответа @ Дэви Лэндман (мне нужен метод расширения), и я немного упростил.
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);
Не уверен, что это то, что вы ищете, но посмотрите. Ваше здоровье