using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Threading.Tasks; using Generic.Contracts.Generics; namespace Generic.Services; public static class SortExt { public static IQueryable ApplySimpleSort(this IQueryable query, SortModel sortModel) { if (string.IsNullOrWhiteSpace(sortModel?.SortBy)) return query; return ApplySortInternal(query, new[] { sortModel }); } public static IQueryable ApplyComplexSort(this IQueryable query, IEnumerable sortModels) { var models = sortModels?.Where(x => !string.IsNullOrWhiteSpace(x.SortBy)).ToList(); if (models == null || !models.Any()) return query; return ApplySortInternal(query, models); } private static IQueryable ApplySortInternal(IQueryable query, IEnumerable sortModels) { var first = true; IOrderedQueryable orderedQuery = null; foreach (var model in sortModels) { var propertyInfo = typeof(T).GetProperty( model.SortBy, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance ); if (propertyInfo == null) { // Decide whether to throw or ignore. For now, letting it throw or we could ignore. // Original code threw exception, so we will throw to be safe/consistent. throw new ArgumentException( $"No property '{model.SortBy}' on type '{typeof(T).Name}'" ); } var parameter = Expression.Parameter(typeof(T), "x"); var property = Expression.Property(parameter, propertyInfo); var lambda = Expression.Lambda(property, parameter); string methodName = ""; if (first) { methodName = model.SortDirection == SortDir.Descending ? "OrderByDescending" : "OrderBy"; var method = typeof(Queryable).GetMethods() .First(m => m.Name == methodName && m.GetParameters().Length == 2) .MakeGenericMethod(typeof(T), propertyInfo.PropertyType); orderedQuery = (IOrderedQueryable)method.Invoke(null, new object[] { query, lambda }); first = false; } else { methodName = model.SortDirection == SortDir.Descending ? "ThenByDescending" : "ThenBy"; var method = typeof(Queryable).GetMethods() .First(m => m.Name == methodName && m.GetParameters().Length == 2) .MakeGenericMethod(typeof(T), propertyInfo.PropertyType); orderedQuery = (IOrderedQueryable)method.Invoke(null, new object[] { orderedQuery, lambda }); } } return orderedQuery ?? query; } }