Как сделать сортировку по типу function(string param for sort,bool asc/desc)?
Подскажите как сделать сортировку , что б на вход я принимал свойство по какому буду сортировать и по какому типу буду сортировать т.е. по убиванию или возрастанию
Ответы (2 шт):
Ну например так.
static IOrderedQueryable<T> AddSort<T, V>(
IQueryable<T> seq, Expression<Func<T, V>> fieldExtractor, bool ascending) =>
ascending ? seq.OrderBy(fieldExtractor) : seq.OrderByDescending(fieldExtractor);
Пользоваться так:
IQueryable<Entity> entities = ...;
var sortedEntities = AddSort(entities, e => e.Weight, ascending: true);
Можно сделать из AddSort метод расширения.
Если вы хотите получить Expression из имени свойства, делайте, например, так:
static Expression<Func<TEntity, TValue>> CreateProjector<TEntity, TValue>(
string propertyName)
{
var entity = Expression.Parameter(typeof(TEntity), "e");
var property = Expression.Property(entity, propertyName);
var extractor = Expression.Lambda<Func<TEntity, TValue>>(property, entity);
return extractor;
}
Соответственно пользоваться нужно так:
IQueryable<Entity> entities = ...;
var projector = CreateProjector<Entity, double>("Weight");
var sortedEntities = AddSort(entities, projector, ascending: true);
Если тип свойства заранее неизвестен, вы можете, например, скастить значение в double, но это приведёт к неоптимальному SQL. Более хитрое решение использует рефлексию, чтобы вызвать OrderBy нужными обобщёнными параметрами:
static IOrderedQueryable<TEntity> AddSortByPropertyName<TEntity>(
IQueryable<TEntity> seq, string propertyName, bool ascending)
{
var entityType = typeof(TEntity);
// часть 1, строим expression как и раньше
var entity = Expression.Parameter(entityType, "e");
var property = Expression.Property(entity, propertyName);
Type propertyType = property.Type;
Type projectorType = typeof(Func<,>).MakeGenericType(entityType, propertyType);
var extractor = Expression.Lambda(projectorType, property, entity);
// в этой точке тип extractor'а нам известен лишь во время выполнения
// часть 2, обобщённый вызов OrderBy
var methodName = ascending ? nameof(Queryable.OrderBy) :
nameof(Queryable.OrderByDescending);
// нам нужен метод с такой вот сигнатурой
// IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector
// но это обобщённый метод, приходится искать его по имени и количеству параметров
var genericMethodInfo =
typeof(Queryable)
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Single(mi => mi.Name == methodName && mi.GetParameters().Length == 2);
// если Single вылетает, придётся строить более жёсткие условия
// имея обобщённый метод, строим его конкретную инстанциацию
var methodInfo = genericMethodInfo.MakeGenericMethod(entityType, propertyType);
// и вызываем
var result = methodInfo.Invoke(null, new object[] { seq, extractor });
return (IOrderedQueryable<TEntity>)result;
}
Пользоваться так:
IQueryable<Entity> entities = ...;
var sortedEntities = AddSortByPropertyName(entities, "Weight", ascending: true);
Используйте OrderBy и OrderByDescending как показано в учебнике от майкрософт:
public ActionResult Index(string sortOrder)
{
ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date";
var students = from s in db.Students
select s;
switch (sortOrder)
{
case "name_desc":
students = students.OrderByDescending(s => s.LastName);
break;
case "Date":
students = students.OrderBy(s => s.EnrollmentDate);
break;
case "date_desc":
students = students.OrderByDescending(s => s.EnrollmentDate);
break;
default:
students = students.OrderBy(s => s.LastName);
break;
}
return View(students.ToList());
}
В принципе, можно генерировать из строки лямбду используя рефлексию, как показано в этом вопросе: https://stackoverflow.com/q/31955025/5752652
Либо можно строить expression по заданным полям:
[UsedImplicitly]
public class Handler : BaseHandler, IRequestHandler<Query, IEnumerable<Contact>>
{
public Handler(ApplicationDbContext applicationDbContext)
: base(applicationDbContext)
{
}
public async Task<IEnumerable<Contact>> Handle(Query request, CancellationToken cancellationToken)
{
var query = this.ApplicationDbContext.Contacts
.Include(x => x.ApplicationUser.City)
.Where(GetPredicate(request));
Expression<Func<Contact, string>> orderBy = x => x.DisplayName;
query = query.AddOrder(orderBy, request.Sort);
var contacts = await query.AsNoTracking()
.ToListAsync(cancellationToken);
return contacts;
}
private static Expression<Func<Contact, bool>> GetPredicate(Query request)
{
Expression<Func<Contact, bool>> predicate = x => x.OwnerId == request.ManagerId;
if (request.Filter.CityId != null)
{
Expression<Func<Contact, bool>> exprCity = x => x.ApplicationUser.CityId == request.Filter.CityId;
predicate = predicate.AndAlso(exprCity);
}
...
return predicate;
}