Ultimate ASP.NET CORE 6.0 Web API --- 读书笔记(17 - 19)
17 Filtering
本文内容来自书籍: Marinko Spasojevic - Ultimate ASP.NET Core Web API - From Zero To Six-Figure Backend Developer (2nd edition)
需要本书和源码的小伙伴可以留下邮箱,有空看到会发送的
过滤查询是指定一个类别来筛选
在前面的文章中,已经有了分页查询的基础,我们只需要在这个参数的基础上,添加需要可以过滤的属性
public class EmployeeParameters : RequestParameters
{
private uint MinAge { get; set; }
private uint MaxAge { get; set; } = int.MaxValue;
public bool ValidAgeRange => MaxAge > MinAge;
}
18 Searching
和过滤差不多,也是可以在分页和过滤的基础上,添加查询参数
public class EmployeeParameters : RequestParameters
{
public uint MinAge { get; set; } = 0;
public uint MaxAge { get; set; } = int.MaxValue;
public bool ValidAgeRange => MaxAge > MinAge;
public string? SearchTerm { get; set; }
}
然后在Repository
中实现逻辑,但是和之前的直接编写在已有的过滤函数中不一样,这次使用扩展方法
// Repository/Extensions/RepositoryEmployeeExtensions
public static class RepositoryEmployeeExtensions
{
public static IQueryable<Employee> FilterEmployees(this IQueryable<Employee> employees, uint minAge, uint maxAge) =>
employees.Where(e => (e.Age >= minAge && e.Age <= maxAge));
public static IQueryable<Employee> Search(this IQueryable<Employee> employees, string? searchTerm)
{
if (string.IsNullOrWhiteSpace(searchTerm)) return employees;
var lowerCaseTerm = searchTerm.Trim().ToLower();
return employees.Where(e => e.Name.ToLower().Contains(lowerCaseTerm));
}
}
// Repository
public async Task<PagedList<Employee>> GetEmployeesAsync(Guid companyId,
EmployeeParameters employeeParameters, bool trackChanges)
{
var employees = await FindByCondition(e => e.CompanyId.Equals(companyId),
trackChanges)
.FilterEmployees(employeeParameters.MinAge, employeeParameters.MaxAge)
.Search(employeeParameters.SearchTerm)
.OrderBy(e => e.Name)
.ToListAsync();
return PagedList<Employee>
.ToPagedList(employees, employeeParameters.PageNumber, employeeParameters.PageSize);
}
这样将逻辑抽取出来之后,对以后可能的扩展,要更加容易修改
19 Sorting
排序也是和查询差不多,也需要增加查询参数,但是排序是可以公共的概念,所以它会存在基类
public abstract class RequestParameters
{
private const int MaxPageSize = 50;
public int PageNumber { get; set; } = 1;
private int _pageSize = 10;
public int PageSize
{
get => _pageSize;
set => _pageSize = value > MaxPageSize ? MaxPageSize : value;
}
public string? OrderBy { get; set; }
}
然后我们希望即使请求中不含有特定的排序要求,也有一个默认参数,所以在实现类中规定默认参数
public class EmployeeParameters : RequestParameters
{
public uint MinAge { get; set; } = 0;
public uint MaxAge { get; set; } = int.MaxValue;
public bool ValidAgeRange => MaxAge > MinAge;
public string? SearchTerm { get; set; }
public EmployeeParameters() => OrderBy = "name";
}
然后是准备实现排序查询的逻辑,在这个之前,我们需要引入动态LINQ的包System.Linq.Dynamic.Core
,接着就是实现一个扩展方法用于构造排序的逻辑
public static IQueryable<Employee> Sort(this IQueryable<Employee> employees, string orderByQueryString)
{
if (string.IsNullOrWhiteSpace(orderByQueryString)) return employees.OrderBy(e => e.Name);
var orderParams = orderByQueryString.Trim().Split(',');
var propertyInfos = typeof(Employee).GetProperties(BindingFlags.Public | BindingFlags.Instance);
var orderQueryBuilder = new StringBuilder();
foreach (var param in orderParams)
{
if (string.IsNullOrWhiteSpace(param)) continue;
var propertyFromQueryName = param.Split(" ")[0];
var objectProperty = propertyInfos.FirstOrDefault(
pi => pi.Name.Equals(propertyFromQueryName, StringComparison.InvariantCultureIgnoreCase));
if (objectProperty == null) continue;
var direction = param.EndsWith(" desc") ? "descending" : "ascending";
orderQueryBuilder.Append($"{objectProperty.Name} {direction},");
}
var orderQuery = orderQueryBuilder.ToString().TrimEnd(',', ' ');
return string.IsNullOrWhiteSpace(orderQuery)
? employees.OrderBy(e => e.Name)
: employees.OrderBy(orderQuery);
}
- 检查传入的排序参数是否为空,如果是空就的就用默认参数
- 切分排序参数
- 反射获取排序对象的所有公开属性
- 然后确定传入的排序参数是不是在排序对象的公开属性中
- 确定是正序还是倒序
- 将组装的查询字段使用
System.Linq.Dynamic.Core
的扩展排序方法
在这些工作做完之后,接下来只需要使用,但是有一个问题就是,我们的代码写死了排序的对象,很明显,在一个Web API中,需要排序的对象很多,所以我们需要对它重构,做类型的范化
- 首先抽取公共代码
public static string CreateOrderQuery<T>(string orderByQueryString)
{
var orderParams = orderByQueryString.Trim().Split(',');
var propertyInfos = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
var orderQueryBuilder = new StringBuilder();
foreach (var param in orderParams)
{
if (string.IsNullOrWhiteSpace(param))
continue;
var propertyFromQueryName = param.Split(" ")[0];
var objectProperty = propertyInfos.FirstOrDefault(pi =>
pi.Name.Equals(propertyFromQueryName, StringComparison.InvariantCultureIgnoreCase));
if (objectProperty == null)
continue;
var direction = param.EndsWith(" desc") ? "descending" : "ascending";
orderQueryBuilder.Append($"{objectProperty.Name}{direction}, ");
}
var orderQuery = orderQueryBuilder.ToString().TrimEnd(',', ' ');
return orderQuery;
}
- 然后修改扩展方法
public static IQueryable<Employee> Sort(this IQueryable<Employee> employees, string? orderByQueryString)
{
if (string.IsNullOrWhiteSpace(orderByQueryString)) return employees.OrderBy(e => e.Name);
var orderQuery = OrderQueryBuilder.CreateOrderQuery<Employee>(orderByQueryString);
return string.IsNullOrWhiteSpace(orderQuery)
? employees.OrderBy(e => e.Name)
: employees.OrderBy(orderQuery);
}
- 最后,我们就可以在不同的对象使用动态参数排序,而我们只是写了一份代码
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?