diff --git a/API/TaskManager.EntityFramework/IRepository/IRepository.cs b/API/TaskManager.EntityFramework/IRepository/IRepository.cs index e6cfd74..2244f0e 100644 --- a/API/TaskManager.EntityFramework/IRepository/IRepository.cs +++ b/API/TaskManager.EntityFramework/IRepository/IRepository.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading.Tasks; using TaskManager.Entity; using TaskManager.EntityFramework.Repository; +using Wood.Util.Filters; namespace TaskManager.EntityFramework { @@ -25,5 +26,9 @@ namespace TaskManager.EntityFramework Expression> filter = null, PagingParams pagingParams = null); + Task> GetDataPagedAsync( + Expression> filter = null, + PagingParams pagingParams = null,Condition condition = null); + } } \ No newline at end of file diff --git a/API/TaskManager.EntityFramework/Repository/Repository.cs b/API/TaskManager.EntityFramework/Repository/Repository.cs index 406c071..6a34dfa 100644 --- a/API/TaskManager.EntityFramework/Repository/Repository.cs +++ b/API/TaskManager.EntityFramework/Repository/Repository.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using TaskManager.Entity; using TaskManager.EntityFramework; using Wood.Util; +using Wood.Util.Filters; namespace TaskManager.EntityFramework.Repository { @@ -82,7 +83,7 @@ namespace TaskManager.EntityFramework.Repository // 应用动态过滤 if (pagingParams?.Filters != null && pagingParams.Filters.Any()) { - query = query.ApplyFilters(pagingParams.Filters); + query = query.ApplyFilters(pagingParams.Filters); } // 应用分页和排序 @@ -94,6 +95,36 @@ namespace TaskManager.EntityFramework.Repository return page; } + public async Task> GetDataPagedAsync( + Expression> filter = null, + PagingParams pagingParams = null,Condition condition = null) + { + IQueryable query = _dbSet.AsNoTracking(); + + // 应用过滤条件 + if (filter != null) + { + query = query.Where(filter); + } + + // 应用动态过滤 + if (condition?.Filters != null && condition.Filters.Any()) + { + query = query.ApplyConditionFilters(condition); + } + + // 应用分页和排序 + pagingParams ??= new PagingParams(); + + var page = await query.ToPagedListAsync(pagingParams); + + + return page; + } + + + + } public class PagedResult { @@ -181,9 +212,18 @@ namespace TaskManager.EntityFramework.Repository return query; } + public static IQueryable ApplyConditionFilters(this IQueryable query, Condition condition) + { + if (condition.Filters == null || !condition.Filters.Any()) return query; + + query = query.Where(condition.Filters.ToLambda()); + + return query; + } + - public static IQueryable ApplyStringFilter(this IQueryable query, + public static IQueryable ApplyStringFilter(this IQueryable query, string propertyName, string value) { var property = typeof(T).GetProperty(propertyName, diff --git a/API/Wood.Service/Controllers/CheryRecurringJobOutPageController.cs b/API/Wood.Service/Controllers/CheryRecurringJobOutPageController.cs index f727f32..6c17be5 100644 --- a/API/Wood.Service/Controllers/CheryRecurringJobOutPageController.cs +++ b/API/Wood.Service/Controllers/CheryRecurringJobOutPageController.cs @@ -21,6 +21,7 @@ using TaskManager.Controllers; using TaskManager.Entity; using TaskManager.EntityFramework; using TaskManager.EntityFramework.Repository; +using Wood.Util.Filters; namespace TaskManager.Controllers { @@ -449,6 +450,30 @@ namespace TaskManager.Controllers return Ok(pagedResult); } + /// + /// 分页New + /// + /// + /// + [HttpPost] + public async Task>> GetDataPaged(RequestInputBase input) + { + var pagingParams = new PagingParams + { + PageNumber = input.pageNumber, + PageSize = input.pageSize, + SortBy = input.sortBy, + IsAscending = input.isAscending, + }; + + // 可以在这里构建表达式树过滤条件 + Expression> filter = null; + + var pagedResult = await _repository.GetDataPagedAsync(filter, pagingParams, input.Condition); + return Ok(pagedResult); + } + + /// /// 导出 /// diff --git a/API/Wood.Util/Filters/Condition.cs b/API/Wood.Util/Filters/Condition.cs new file mode 100644 index 0000000..a3a1a59 --- /dev/null +++ b/API/Wood.Util/Filters/Condition.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wood.Util.Filters +{ + public class Condition + { + public ICollection Filters { get; set; } = new List(); + } +} diff --git a/API/Wood.Util/Filters/EnumFilterAction.cs b/API/Wood.Util/Filters/EnumFilterAction.cs new file mode 100644 index 0000000..385480f --- /dev/null +++ b/API/Wood.Util/Filters/EnumFilterAction.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wood.Util.Filters +{ + /// + /// 过滤条件 + /// + public enum EnumFilterAction + { + /// + /// equal + /// + [Description("等于")] Equal = 0, + + /// + /// Not equal + /// + [Description("不等于")] NotEqual = 1, + + /// + /// Bigger + /// + [Description("大于")] BiggerThan = 2, + + /// + /// Smaller + /// + [Description("小于")] SmallThan = 3, + + /// + /// Bigger or equal + /// + [Description("大于等于")] BiggerThanOrEqual = 4, + + /// + /// Small or equal + /// + [Description("小于等于")] SmallThanOrEqual = 5, + + /// + /// Like + /// + [Description("类似于")] Like = 6, + + /// + /// Not like + /// + [Description("不类似于")] NotLike = 7, + + /// + /// Contained in + /// List items = new List(); + /// string value = JsonSerializer.Serialize(items);//转成Json字符串 + ///FilterCondition filterCondition = new FilterCondition() { Column = "Name", Value = value, Action = EnumFilterAction.In, Logic = EnumFilterLogic.And }; + /// + [Description("包含于")] In = 8, + + /// + /// Not contained in + /// + [Description("不包含于")] NotIn = 9, + } + +} diff --git a/API/Wood.Util/Filters/EnumFilterLogic.cs b/API/Wood.Util/Filters/EnumFilterLogic.cs new file mode 100644 index 0000000..8075b69 --- /dev/null +++ b/API/Wood.Util/Filters/EnumFilterLogic.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wood.Util.Filters +{ + public enum EnumFilterLogic + { + /// + /// 与 + /// + And = 0, + + /// + /// 或 + /// + Or = 1 + } +} diff --git a/API/Wood.Util/Filters/Filter.cs b/API/Wood.Util/Filters/Filter.cs new file mode 100644 index 0000000..ce792fc --- /dev/null +++ b/API/Wood.Util/Filters/Filter.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wood.Util.Filters +{ + public class Filter + { + public Filter() + { + Logic = "And"; + } + + public Filter(string column, string value, + string action = "==", + string logic = "And") + { + Column = column; + Action = action; + Value = value; + Logic = logic; + } + + /// + /// 过滤条件之间的逻辑关系:AND和OR + /// + public string Logic { get; set; } = "And"; + + /// + /// 过滤条件中使用的数据列 + /// + public string Column { get; set; } + + /// + /// 过滤条件中的操作:==,!=,>,<,>=,<=,In,NotIn,Like,NotLike + /// Equal、NotEqual、BiggerThan、SmallThan、BiggerThanOrEqual、SmallThanOrEqual、In、NotIn + /// + public string Action { get; set; } = "=="; + + /// + /// 过滤条件中的操作的值 + /// + public string Value { get; set; } + } + +} diff --git a/API/Wood.Util/Filters/FilterExtensions.cs b/API/Wood.Util/Filters/FilterExtensions.cs new file mode 100644 index 0000000..b2ccfb6 --- /dev/null +++ b/API/Wood.Util/Filters/FilterExtensions.cs @@ -0,0 +1,337 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Wood.Util.Filters +{ + public static class FilterExtensions + { + public static Expression> ToLambda(this string jsonFilter) + { + if (string.IsNullOrWhiteSpace(jsonFilter)) + { + return p => true; + } + + var filterConditions = JsonSerializer.Deserialize>(jsonFilter); + return filterConditions.ToLambda(); + } + + public static Expression> ToLambda(this Filter filter) + { + var filterConditions = new List { filter }; + return filterConditions.ToLambda(); + } + + public static Expression> ToLambda(this ICollection filterConditionList) + { + Expression> condition = null; + try + { + if (!filterConditionList.Any()) + { + //创建默认表达式 + return p => true; + } + + foreach (var filterCondition in filterConditionList) + { + var tempCondition = CreateLambda(filterCondition); + if (condition == null) + { + condition = tempCondition; + } + else + { + condition = filterCondition.Logic switch + { + "And" => condition.And(tempCondition), + "Or" => condition.Or(tempCondition), + _ => condition + }; + } + } + } + catch (Exception ex) + { + throw new Exception($"获取筛选条件异常:{ex.Message}"); + } + + return condition; + } + + private static Expression> CreateLambda(Filter filter) + { + Expression> expression = p => false; + try + { + var parameter = Expression.Parameter(typeof(T), "p"); //创建参数p + var member = Expression.PropertyOrField(parameter, filter.Column); //创建表达式中的属性或字段 + // var propertyType = member.Type; //取属性类型,常量constant按此类型进行转换 + //var constant = Expression.Constant(filterCondition.Value);//创建常数 + + ConstantExpression constant = null; + if (filter.Action != "In" && filter.Action != "NotIn") + { + constant = CreateConstantExpression(member.Type, filter.Value); + } + + switch (filter.Action.ToLower()) + { + case "==": + expression = Expression.Lambda>(Expression.Equal(member, constant), parameter); + break; + + case "!=": + expression = Expression.Lambda>(Expression.NotEqual(member, constant), parameter); + break; + + case ">": + expression = Expression.Lambda>(Expression.GreaterThan(member, constant), parameter); + break; + + case "<": + expression = Expression.Lambda>(Expression.LessThan(member, constant), parameter); + break; + + case ">=": + expression = Expression.Lambda>(Expression.GreaterThanOrEqual(member, constant), parameter); + break; + + case "<=": + expression = Expression.Lambda>(Expression.LessThanOrEqual(member, constant), parameter); + break; + + case "like": + expression = GetExpressionLikeMethod("Contains", filter); + break; + + case "notlike": + expression = GetExpressionNotLikeMethod("Contains", filter); + break; + + case "in": + expression = GetExpressionInMethod("Contains", member.Type, filter); + break; + + case "notin": + expression = GetExpressionNotInMethod("Contains", member.Type, filter); + break; + + default: + break; + } + } + catch (Exception ex) + { + throw new Exception(ex.Message); + } + + return expression; + } + + /// + /// + /// + /// + /// + /// + private static ConstantExpression CreateConstantExpression(Type propertyType, string value) + { + ConstantExpression constant; + try + { + if (propertyType.IsGenericType && + propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + var objValue = Convert.ChangeType(value, propertyType.GetGenericArguments()[0], CultureInfo.InvariantCulture); + constant = Expression.Constant(objValue); + } + else if (propertyType.IsEnum) + { + var enumValue = (Enum)Enum.Parse(propertyType, value, true); + constant = Expression.Constant(enumValue); + } + else + { + constant = propertyType.Name switch + { + "Guid" => Expression.Constant(Guid.Parse(value)), + _ => Expression.Constant(Convert.ChangeType(value, propertyType, CultureInfo.InvariantCulture)) + }; + } + } + catch (Exception ex) + { + throw new Exception($"获取ConstantExpression异常:{ex.Message}"); + } + + return constant; + } + + private static Expression> GetExpressionLikeMethod(string methodName, Filter filter) + { + var parameterExpression = Expression.Parameter(typeof(T), "p"); + // MethodCallExpression methodExpression = GetMethodExpression(methodName, filterCondition.Column, filterCondition.Value, parameterExpression); + var methodExpression = GetMethodExpression(methodName, filter.Column, filter.Value, + parameterExpression); + return Expression.Lambda>(methodExpression, parameterExpression); + } + + private static Expression> GetExpressionNotLikeMethod(string methodName, Filter filter) + { + var parameterExpression = Expression.Parameter(typeof(T), "p"); + var methodExpression = GetMethodExpression(methodName, filter.Column, filter.Value, + parameterExpression); + var notMethodExpression = Expression.Not(methodExpression); + return Expression.Lambda>(notMethodExpression, parameterExpression); + } + + /// + /// 生成guidList.Contains(p=>p.GUId); + /// 除String类型,其他类型涉及到类型转换.如GUID + /// + /// + /// Contains + /// PropertyType/typeof(GUId) + /// PropertyName/PropertyValue + /// + private static Expression> GetExpressionInMethod(string methodName, Type propertyType, Filter filter) + { + var parameterExpression = Expression.Parameter(typeof(T), "p"); + Type lstType = typeof(List<>).MakeGenericType(propertyType); + + //转换枚举 + //if (propertyType.IsEnum) + //{ + // var valueArrayStrings = JsonSerializer.Deserialize>(filter.Value); + // List newValues = new List(); + + // var enumValues = propertyType.GetEnumValues(); + + // foreach (var valueArray in valueArrayStrings) + // { + // foreach (var enumValue in enumValues) + // { + // if (enumValue.ToString() == valueArray) + // { + // newValues.Add(enumValue); + // break; + // } + // } + // } + // var newValue = JsonSerializer.Serialize(newValues); + // filter.Value = newValue; + //} + + object propertyValue = JsonSerializer.Deserialize(filter.Value, lstType); + if (propertyValue != null) + { + var methodExpression = GetListMethodExpression(methodName, propertyType, filter.Column, propertyValue, parameterExpression); + var expression = Expression.Lambda>(methodExpression, parameterExpression); + return expression; + } + else + { + return p => false; + } + } + + private static Expression> GetExpressionNotInMethod(string methodName, Type propertyType, Filter filter) + { + var parameterExpression = Expression.Parameter(typeof(T), "p"); + Type lstType = typeof(List<>).MakeGenericType(propertyType); + object propertyValue = JsonSerializer.Deserialize(filter.Value, lstType); + if (propertyValue != null) + { + var methodExpression = GetListMethodExpression(methodName, propertyType, filter.Column, propertyValue, parameterExpression); + var notMethodExpression = Expression.Not(methodExpression); + return Expression.Lambda>(notMethodExpression, parameterExpression); + } + else + { + return p => false; + } + } + + private static MethodCallExpression GetListMethodExpression(string methodName, Type propertyType, string propertyName, object propertyValue, ParameterExpression parameterExpression) + { + var propertyExpression = Expression.Property(parameterExpression, propertyName); //p.GUID + Type type = typeof(List<>).MakeGenericType(propertyType); + var method = type.GetMethod(methodName);//获取 List.Contains() + var someValue = Expression.Constant(propertyValue);//Value + return Expression.Call(someValue, method, propertyExpression); + } + + /// + /// 生成类似于p=>p.Code.Contains("xxx");的lambda表达式 + /// parameterExpression标识p,propertyName表示values,propertyValue表示"Code",methodName表示Contains + /// 仅处理p的属性类型为string这种情况 + /// + /// + /// + /// + /// + /// + private static MethodCallExpression GetMethodExpression(string methodName, string propertyName, + string propertyValue, ParameterExpression parameterExpression) + { + var propertyExpression = Expression.Property(parameterExpression, propertyName); + var method = typeof(string).GetMethod(methodName, new[] { typeof(string) }); + var someValue = Expression.Constant(propertyValue, typeof(string)); + return Expression.Call(propertyExpression, method, someValue); + } + + /// + /// 默认True条件 + /// + /// + /// + public static Expression> True() + { + return f => true; + } + + /// + /// 默认False条件 + /// + /// + /// + public static Expression> False() + { + return f => false; + } + + /// + /// 拼接 OR 条件 + /// + /// + /// + /// + /// + private static Expression> Or(this Expression> exp, Expression> condition) + { + var inv = Expression.Invoke(condition, exp.Parameters); + return Expression.Lambda>(Expression.Or(exp.Body, inv), exp.Parameters); + } + + /// + /// 拼接And条件 + /// + /// + /// + /// + /// + private static Expression> And(this Expression> exp, Expression> condition) + { + var inv = Expression.Invoke(condition, exp.Parameters); + return Expression.Lambda>(Expression.And(exp.Body, inv), exp.Parameters); + } + } + +} diff --git a/API/Wood.Util/Filters/RequestInputBase.cs b/API/Wood.Util/Filters/RequestInputBase.cs new file mode 100644 index 0000000..c9e4d5e --- /dev/null +++ b/API/Wood.Util/Filters/RequestInputBase.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wood.Util.Filters +{ + public class RequestInputBase + { + public int pageNumber { get; set; } + + public int pageSize { get; set; } + + public string sortBy { get; set; } + + public bool isAscending { get; set; } + /// + /// 条件 + /// + public Condition Condition { get; set; } = new(); + } +}