115 lines
4.9 KiB
C#
115 lines
4.9 KiB
C#
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<Type, Dictionary<string, TypeAcessor>> 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<string, TypeAcessor> MakeTypeAcessors(Type type)
|
||
{
|
||
var propContainer = new Dictionary<string, TypeAcessor>();
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Добавить в запрос сортировку по возрастанию или убыванию.
|
||
/// Этот метод сбросит ранее наложенные сортировки.
|
||
/// </summary>
|
||
/// <typeparam name="TSource"></typeparam>
|
||
/// <param name="query"></param>
|
||
/// <param name="propertySort">
|
||
/// Свойство сортировки.
|
||
/// Состоит из названия свойства (в любом регистре)
|
||
/// и опционально указания направления сортировки "asc" или "desc"
|
||
/// </param>
|
||
/// <example>
|
||
/// var query = query("Date desc");
|
||
/// </example>
|
||
/// <returns>Запрос с примененной сортировкой</returns>
|
||
public static IOrderedQueryable<TSource> SortBy<TSource>(
|
||
this IQueryable<TSource> 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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Добавить в запрос сортировку по возрастанию или убыванию
|
||
/// </summary>
|
||
/// <typeparam name="TSource"></typeparam>
|
||
/// <param name="query"></param>
|
||
/// <param name="propertyName">Название свойства (в любом регистре)</param>
|
||
/// <param name="isDesc">Сортировать по убыванию</param>
|
||
/// <returns>Запрос с примененной сортировкой</returns>
|
||
public static IOrderedQueryable<TSource> SortBy<TSource>(
|
||
this IQueryable<TSource> 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<TSource>)genericMethod
|
||
.Invoke(genericMethod, new object[] { query, typeAccessor.KeySelector })!;
|
||
return newQuery;
|
||
}
|
||
}
|