using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace Persistence; public static class EFExtensions { struct TypeAcessor { public LambdaExpression KeySelector { get; set; } public MethodInfo OrderBy { get; set; } public MethodInfo OrderByDescending { get; set; } public MethodInfo ThenBy { get; set; } public MethodInfo ThenByDescending { get; set; } } private static readonly MethodInfo methodOrderBy = GetExtOrderMethod("OrderBy"); private static readonly MethodInfo methodOrderByDescending = GetExtOrderMethod("OrderByDescending"); private static readonly MethodInfo methodThenBy = GetExtOrderMethod("ThenBy"); private static readonly MethodInfo methodThenByDescending = GetExtOrderMethod("ThenByDescending"); private static ConcurrentDictionary> TypePropSelectors { get; set; } = new(); private static MethodInfo GetExtOrderMethod(string methodName) => typeof(System.Linq.Queryable) .GetMethods() .Where(m => m.Name == methodName && m.IsGenericMethodDefinition && m.GetParameters().Length == 2 && m.GetParameters()[1].ParameterType.IsAssignableTo(typeof(LambdaExpression))) .Single(); private static Dictionary MakeTypeAcessors(Type type) { var propContainer = new Dictionary(); var properties = type.GetProperties(); foreach (var propertyInfo in properties) { var name = propertyInfo.Name.ToLower(); ParameterExpression arg = Expression.Parameter(type, "x"); MemberExpression property = Expression.Property(arg, propertyInfo.Name); var selector = Expression.Lambda(property, new ParameterExpression[] { arg }); var typeAccessor = new TypeAcessor { KeySelector = selector, OrderBy = methodOrderBy.MakeGenericMethod(type, propertyInfo.PropertyType), OrderByDescending = methodOrderByDescending.MakeGenericMethod(type, propertyInfo.PropertyType), ThenBy = methodThenBy.MakeGenericMethod(type, propertyInfo.PropertyType), ThenByDescending = methodThenByDescending.MakeGenericMethod(type, propertyInfo.PropertyType), }; propContainer.Add(name, typeAccessor); } return propContainer; } /// /// Добавить в запрос сортировку по возрастанию или убыванию. /// Этот метод сбросит ранее наложенные сортировки. /// /// /// /// /// Свойство сортировки. /// Состоит из названия свойства (в любом регистре) /// и опционально указания направления сортировки "asc" или "desc" /// /// /// var query = query("Date desc"); /// /// Запрос с примененной сортировкой public static IOrderedQueryable SortBy( this IQueryable query, string propertySort) { var parts = propertySort.Split(" ", 2, StringSplitOptions.RemoveEmptyEntries); var isDesc = parts.Length >= 2 && parts[1].ToLower().Trim() == "desc"; var propertyName = parts[0]; var newQuery = query.SortBy(propertyName, isDesc); return newQuery; } /// /// Добавить в запрос сортировку по возрастанию или убыванию /// /// /// /// Название свойства (в любом регистре) /// Сортировать по убыванию /// Запрос с примененной сортировкой public static IOrderedQueryable SortBy( this IQueryable query, string propertyName, bool isDesc) { var typePropSelector = TypePropSelectors.GetOrAdd(typeof(TSource), MakeTypeAcessors); var propertyNamelower = propertyName.ToLower(); var typeAccessor = typePropSelector[propertyNamelower]; var genericMethod = isDesc ? typeAccessor.OrderByDescending : typeAccessor.OrderBy; var newQuery = (IOrderedQueryable)genericMethod .Invoke(genericMethod, new object[] { query, typeAccessor.KeySelector })!; return newQuery; } }