Browse Source

增加Filter过滤条件

master
周红军 1 week ago
parent
commit
e7fde7945a
  1. 5
      API/TaskManager.EntityFramework/IRepository/IRepository.cs
  2. 44
      API/TaskManager.EntityFramework/Repository/Repository.cs
  3. 25
      API/Wood.Service/Controllers/CheryRecurringJobOutPageController.cs
  4. 13
      API/Wood.Util/Filters/Condition.cs
  5. 69
      API/Wood.Util/Filters/EnumFilterAction.cs
  6. 21
      API/Wood.Util/Filters/EnumFilterLogic.cs
  7. 48
      API/Wood.Util/Filters/Filter.cs
  8. 337
      API/Wood.Util/Filters/FilterExtensions.cs
  9. 23
      API/Wood.Util/Filters/RequestInputBase.cs

5
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<Func<TEntity, bool>> filter = null,
PagingParams pagingParams = null);
Task<PagedResult<TEntity>> GetDataPagedAsync(
Expression<Func<TEntity, bool>> filter = null,
PagingParams pagingParams = null,Condition condition = null);
}
}

44
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<PagedResult<TEntity>> GetDataPagedAsync(
Expression<Func<TEntity, bool>> filter = null,
PagingParams pagingParams = null,Condition condition = null)
{
IQueryable<TEntity> 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<T>
{
@ -181,9 +212,18 @@ namespace TaskManager.EntityFramework.Repository
return query;
}
public static IQueryable<T> ApplyConditionFilters<T>(this IQueryable<T> query, Condition condition)
{
if (condition.Filters == null || !condition.Filters.Any()) return query;
query = query.Where(condition.Filters.ToLambda<T>());
return query;
}
public static IQueryable<T> ApplyStringFilter<T>(this IQueryable<T> query,
public static IQueryable<T> ApplyStringFilter<T>(this IQueryable<T> query,
string propertyName, string value)
{
var property = typeof(T).GetProperty(propertyName,

25
API/Wood.Service/Controllers/CheryRecurringJobOutPageController.cs

@ -18,6 +18,7 @@ using TaskManager.Controllers;
using TaskManager.Entity;
using TaskManager.EntityFramework;
using TaskManager.EntityFramework.Repository;
using Wood.Util.Filters;
namespace TaskManager.Controllers
{
@ -411,6 +412,30 @@ namespace TaskManager.Controllers
return Ok(pagedResult);
}
/// <summary>
/// 分页New
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost]
public async Task<ActionResult<PagedResult<T>>> GetDataPaged(RequestInputBase input)
{
var pagingParams = new PagingParams
{
PageNumber = input.pageNumber,
PageSize = input.pageSize,
SortBy = input.sortBy,
IsAscending = input.isAscending,
};
// 可以在这里构建表达式树过滤条件
Expression<Func<T, bool>> filter = null;
var pagedResult = await _repository.GetDataPagedAsync(filter, pagingParams, input.Condition);
return Ok(pagedResult);
}
/// <summary>
/// 导出
/// </summary>

13
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<Filter> Filters { get; set; } = new List<Filter>();
}
}

69
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
{
/// <summary>
/// 过滤条件
/// </summary>
public enum EnumFilterAction
{
/// <summary>
/// equal
/// </summary>
[Description("等于")] Equal = 0,
/// <summary>
/// Not equal
/// </summary>
[Description("不等于")] NotEqual = 1,
/// <summary>
/// Bigger
/// </summary>
[Description("大于")] BiggerThan = 2,
/// <summary>
/// Smaller
/// </summary>
[Description("小于")] SmallThan = 3,
/// <summary>
/// Bigger or equal
/// </summary>
[Description("大于等于")] BiggerThanOrEqual = 4,
/// <summary>
/// Small or equal
/// </summary>
[Description("小于等于")] SmallThanOrEqual = 5,
/// <summary>
/// Like
/// </summary>
[Description("类似于")] Like = 6,
/// <summary>
/// Not like
/// </summary>
[Description("不类似于")] NotLike = 7,
/// <summary>
/// Contained in
/// List<string > items = new List<string>();
/// string value = JsonSerializer.Serialize(items);//转成Json字符串
///FilterCondition filterCondition = new FilterCondition() { Column = "Name", Value = value, Action = EnumFilterAction.In, Logic = EnumFilterLogic.And };
/// </summary>
[Description("包含于")] In = 8,
/// <summary>
/// Not contained in
/// </summary>
[Description("不包含于")] NotIn = 9,
}
}

21
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
{
/// <summary>
/// 与
/// </summary>
And = 0,
/// <summary>
/// 或
/// </summary>
Or = 1
}
}

48
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;
}
/// <summary>
/// 过滤条件之间的逻辑关系:AND和OR
/// </summary>
public string Logic { get; set; } = "And";
/// <summary>
/// 过滤条件中使用的数据列
/// </summary>
public string Column { get; set; }
/// <summary>
/// 过滤条件中的操作:==,!=,>,<,>=,<=,In,NotIn,Like,NotLike
/// Equal、NotEqual、BiggerThan、SmallThan、BiggerThanOrEqual、SmallThanOrEqual、In、NotIn
/// </summary>
public string Action { get; set; } = "==";
/// <summary>
/// 过滤条件中的操作的值
/// </summary>
public string Value { get; set; }
}
}

337
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<Func<T, bool>> ToLambda<T>(this string jsonFilter)
{
if (string.IsNullOrWhiteSpace(jsonFilter))
{
return p => true;
}
var filterConditions = JsonSerializer.Deserialize<List<Filter>>(jsonFilter);
return filterConditions.ToLambda<T>();
}
public static Expression<Func<T, bool>> ToLambda<T>(this Filter filter)
{
var filterConditions = new List<Filter> { filter };
return filterConditions.ToLambda<T>();
}
public static Expression<Func<T, bool>> ToLambda<T>(this ICollection<Filter> filterConditionList)
{
Expression<Func<T, bool>> condition = null;
try
{
if (!filterConditionList.Any())
{
//创建默认表达式
return p => true;
}
foreach (var filterCondition in filterConditionList)
{
var tempCondition = CreateLambda<T>(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<Func<T, bool>> CreateLambda<T>(Filter filter)
{
Expression<Func<T, bool>> 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<Func<T, bool>>(Expression.Equal(member, constant), parameter);
break;
case "!=":
expression = Expression.Lambda<Func<T, bool>>(Expression.NotEqual(member, constant), parameter);
break;
case ">":
expression = Expression.Lambda<Func<T, bool>>(Expression.GreaterThan(member, constant), parameter);
break;
case "<":
expression = Expression.Lambda<Func<T, bool>>(Expression.LessThan(member, constant), parameter);
break;
case ">=":
expression = Expression.Lambda<Func<T, bool>>(Expression.GreaterThanOrEqual(member, constant), parameter);
break;
case "<=":
expression = Expression.Lambda<Func<T, bool>>(Expression.LessThanOrEqual(member, constant), parameter);
break;
case "like":
expression = GetExpressionLikeMethod<T>("Contains", filter);
break;
case "notlike":
expression = GetExpressionNotLikeMethod<T>("Contains", filter);
break;
case "in":
expression = GetExpressionInMethod<T>("Contains", member.Type, filter);
break;
case "notin":
expression = GetExpressionNotInMethod<T>("Contains", member.Type, filter);
break;
default:
break;
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return expression;
}
/// <summary>
///
/// </summary>
/// <param name="propertyType"></param>
/// <param name="value"></param>
/// <returns></returns>
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<Func<T, bool>> GetExpressionLikeMethod<T>(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<Func<T, bool>>(methodExpression, parameterExpression);
}
private static Expression<Func<T, bool>> GetExpressionNotLikeMethod<T>(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<Func<T, bool>>(notMethodExpression, parameterExpression);
}
/// <summary>
/// 生成guidList.Contains(p=>p.GUId);
/// 除String类型,其他类型涉及到类型转换.如GUID
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="methodName">Contains</param>
/// <param name="propertyType">PropertyType/typeof(GUId)</param>
/// <param name="filter">PropertyName/PropertyValue</param>
/// <returns></returns>
private static Expression<Func<T, bool>> GetExpressionInMethod<T>(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<List<string>>(filter.Value);
// List<object> newValues = new List<object>();
// 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<Func<T, bool>>(methodExpression, parameterExpression);
return expression;
}
else
{
return p => false;
}
}
private static Expression<Func<T, bool>> GetExpressionNotInMethod<T>(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<Func<T, bool>>(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);
}
/// <summary>
/// 生成类似于p=>p.Code.Contains("xxx");的lambda表达式
/// parameterExpression标识p,propertyName表示values,propertyValue表示"Code",methodName表示Contains
/// 仅处理p的属性类型为string这种情况
/// </summary>
/// <param name="methodName"></param>
/// <param name="propertyName"></param>
/// <param name="propertyValue"></param>
/// <param name="parameterExpression"></param>
/// <returns></returns>
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);
}
/// <summary>
/// 默认True条件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static Expression<Func<T, bool>> True<T>()
{
return f => true;
}
/// <summary>
/// 默认False条件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static Expression<Func<T, bool>> False<T>()
{
return f => false;
}
/// <summary>
/// 拼接 OR 条件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="exp"></param>
/// <param name="condition"></param>
/// <returns></returns>
private static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> exp, Expression<Func<T, bool>> condition)
{
var inv = Expression.Invoke(condition, exp.Parameters);
return Expression.Lambda<Func<T, bool>>(Expression.Or(exp.Body, inv), exp.Parameters);
}
/// <summary>
/// 拼接And条件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="exp"></param>
/// <param name="condition"></param>
/// <returns></returns>
private static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> exp, Expression<Func<T, bool>> condition)
{
var inv = Expression.Invoke(condition, exp.Parameters);
return Expression.Lambda<Func<T, bool>>(Expression.And(exp.Body, inv), exp.Parameters);
}
}
}

23
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; }
/// <summary>
/// 条件
/// </summary>
public Condition Condition { get; set; } = new();
}
}
Loading…
Cancel
Save