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);
    }
  1. 检查传入的排序参数是否为空,如果是空就的就用默认参数
  2. 切分排序参数
  3. 反射获取排序对象的所有公开属性
  4. 然后确定传入的排序参数是不是在排序对象的公开属性中
  5. 确定是正序还是倒序
  6. 将组装的查询字段使用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);
    }
  • 最后,我们就可以在不同的对象使用动态参数排序,而我们只是写了一份代码
posted @   huang1993  阅读(66)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示