Sy.ExpressionBuilder 動態查詢新體驗

noert發表於2024-08-14

省流模式,看下程式碼量對比

                //常規查詢
                var query = users
                    .WhereIf(m => m.UserName.Contains(input.UserName), !string.IsNullOrEmpty(input.UserName))
                    .WhereIf(m => input.RoleIds.Contains(m.RoleId), input.RoleIds?.Any() ?? false)
                    .WhereIf(m => m.Role.RoleName.Contains(input.RoleName), !string.IsNullOrEmpty(input.RoleName))
                    .WhereIf(m => m.Sys.Any(p => p.SysName.Contains(input.SysName)), !string.IsNullOrEmpty(input.SysName))
                    .WhereIf(m => m.Gender == input.Gender, input.Gender.HasValue)
                    .WhereIf(m => m.CreateTime >= input.CreateTimeStart, input.CreateTimeStart.HasValue)
                    .WhereIf(m => m.CreateTime <= input.CreateTimeEnd, input.CreateTimeEnd.HasValue);
                var list = query
                    .OrderBy(m => m.RoleId).ThenBy(m => m.Tel)
                    .Skip((input.Page - 1) * input.PageSize).Take(input.PageSize)
                    .ToList();
                var total = query.Count();
            } {
                //構造器查詢方式1
                var (list, total) =
                    users.ToPageList(input.AddMultipleOrderBy(false, nameof(User.RoleId), nameof(User.Tel)));
                //構造器查詢方式2
                var (list2, total2) = users
                    .WhereExt(input) //.Where(input.ToExpression<User>())
                    .OrderBy(false, nameof(User.RoleId), nameof(User.Tel)
                    )
                    .Page(input);
            }

放個構建的表示式效果圖


沒興趣?那本部落格到此結束。小夥伴可以到別處看看了(吐槽下,部落格園的這個首頁釋出機制屬實是看人臉皮和自信度了,我屬於臉皮厚的)。

1.該表示式外掛透過模型繼承獲取對應的能力。目前提供的模型有

  1. QueryModel :基礎查詢類

  2. PageModel:分頁查詢引數類(預設每頁分頁20條)

  3. FullQueryModel:查詢模型,對比QueryModel 多了查詢集合QueryItems和過濾FilterFields。前端可以在後端給與的基礎上,新增查詢引數,對於前端來說許可權有點大,慎用(建議高階查詢的時候用)。

  4. FullPageModel:查詢模型,對比PageModel多了查詢集合QueryItems和過濾FilterFields。慎用(原因同上)。

2.屬性名約束

  1. 編號查詢:編號查詢最好是以Id結尾,不然如果編號為字串的話,查詢方式會以Contains形式查詢。

  2. 時間格式:以 Start,End 結尾 ,生成條件為 >= 和<=。

  3. 數字範圍:屬性名稱 以 Min,Max 結尾 ,生成條件為 >= 和<=。

  4. 字串查詢: 名字需要和表欄位一致,生成條件為 Contains

3.特性約束。這部分主要為了彌補屬性名的不足。可以使用特性ConditionAttribute。下面給出引數介紹以及使用例子。

  • 屬性名:PropertyName

  • 屬性值:Value

  • 查詢方法:Condition

  • 查詢型別:ConditionType(and/or)

  • 是否唯一屬性名稱:IsSinglePropertyName

  • 是否區分大小寫:IsCaseSensitive

使用範例
  1. 導航父類查詢:父類類名+英文符號.+父類的屬性名。如

  [Condition($"{nameof(NewType)}.{nameof(NewType.Name)}")]
  public string Name { get; set; }
  1. 導航子類查詢:子類類名+括號[子類屬性名]。如

  [Condition($"{nameof(NewType)}[{nameof(NewType.Name)}]")]
  public string Name { get; set; }
  1. 預設的屬性名不符合自己的規則的查詢:例如字串想要正值匹配,可透過特性配置

 [Condition("Role.RoleName", EnumCondition.Equal, EnumConditionType.And)]
  public string Name { get; set; }

4.排序處理

  1. 預設排序方法 DefaultOrderBy(string propName, bool isDesc = true, bool isClearReserved = true)。引數解析:屬性名、降序升序、是否清掉之前的排序

  2. 預設排序方法 AddOrderBy(string propName, bool isDesc = true)。引數解析:屬性名、降序升序。可新增多個,優先順序按照,新增的順序來。

/// <summary>
    /// 設定預設的排序條件,並清空之前保留的排序條件(如果isClearReserved為true)。
    /// </summary>
    /// <param name="input">當前的QueryModel例項。</param>
    /// <param name="propName">要排序的屬性名。</param>
    /// <param name="isDesc">排序方向,true為降序,false為升序。</param>
    /// <param name="isClearReserved">是否清空之前保留的排序條件。</param>
    /// <returns>返回修改後的QueryModel例項。</returns>
    public static QueryModel DefaultOrderBy(this QueryModel input, string propName, bool isDesc, bool isClearReserved)
    {
         // 方法實現
    }
​
    /// <summary>
    /// 向QueryModel中新增一個排序條件。
    /// </summary>
    /// <param name="input">當前的QueryModel例項。</param>
    /// <param name="propName">要排序的屬性名。</param>
    /// <param name="isDesc">排序方向,true為降序,false為升序。</param>
    /// <returns>返回修改後的QueryModel例項。</returns>
    public static QueryModel AddOrderBy(this QueryModel input, string propName, bool isDesc)
    {
         // 方法實現
    }
​
    /// <summary>
    /// 向QueryModel中新增兩個排序條件。
    /// </summary>
    /// <param name="input">當前的QueryModel例項。</param>
    /// <param name="propName">第一個要排序的屬性名。</param>
    /// <param name="isDesc">第一個排序的方向,true為降序,false為升序。</param>
    /// <param name="propName2">第二個要排序的屬性名。</param>
    /// <param name="isDesc2">第二個排序的方向,true為降序,false為升序。</param>
    /// <returns>返回修改後的QueryModel例項。</returns>
    public static QueryModel AddOrderBy(this QueryModel input, string propName, bool isDesc, string propName2, bool isDesc2)
    {
          // 方法實現
    }
​
    /// <summary>
    /// 向QueryModel中新增三個排序條件。
    /// </summary>
    /// <param name="input">當前的QueryModel例項。</param>
    /// <param name="propName">第一個要排序的屬性名。</param>
    /// <param name="isDesc">第一個排序的方向,true為降序,false為升序。</param>
    /// <param name="propName2">第二個要排序的屬性名。</param>
    /// <param name="isDesc2">第二個排序的方向,true為降序,false為升序。</param>
    /// <param name="propName3">第三個要排序的屬性名。</param>
    /// <param name="isDesc3">第三個排序的方向,true為降序,false為升序。</param>
    /// <returns>返回修改後的QueryModel例項。</returns>
    public static QueryModel AddOrderBy(this QueryModel input, string propName, bool isDesc, string propName2, bool isDesc2, string propName3, bool isDesc3)
    {
        // 方法實現
    }
    
    /// <summary>
    /// 向QueryModel例項中新增一組排序條件。
    /// </summary>
    /// <param name="input">當前的QueryModel例項。</param>
    /// <param name="orderByItems">包含排序資訊的OrderByItem列表。</param>
    /// <returns>返回修改後的QueryModel例項(在空實現中,實際上只是返回傳入的例項)。</returns>
    public static QueryModel AddOrderBy(this QueryModel input, List<OrderByItem> orderByItems)
    {
       // 方法實現
    }
​
    /// <summary>
    /// 向QueryModel例項中新增多個排序條件,所有條件使用相同的排序方向。
    /// </summary>
    /// <param name="input">當前的QueryModel例項。</param>
    /// <param name="isDesc">排序方向,true為降序,false為升序。</param>
    /// <param name="propNames">要排序的屬性名陣列。</param>
    /// <returns>返回修改後的QueryModel例項(在空實現中,實際上只是返回傳入的例項)。</returns>
    public static QueryModel AddMultipleOrderBy(this QueryModel input, bool isDesc, params string[] propNames)
    {
         // 方法實現
    }

5.分組查詢

  1. 使用特性GroupAttribute。使用分組序號,如Group(1)、如Group(1,EnumConditionType.Or)。

   [Group(1)]
   public string Name { get; set; }
    
   [Group(1, EnumConditionType.Or)]
   public string UserName { get; set; }

6.時間跨度查詢約束

  1. 使用特性DurationAttribute。在時間的兩個屬性之間中的一個打上這個特性即可。如時間跨度三個月的。

    /// <summary>
    /// 建立時間 開始
    /// </summary>
    [Duration(3, EnumTimeType.Month)]
    public DateTime? CreateTimeStart { get; set; }
    /// <summary>
    /// 建立時間  結束
    /// </summary>
    public DateTime? CreateTimeEnd { get; set; }

7.分頁配置

  1. 沒啥好說的:PageIndex 當前頁 ,PageSize 顯示數。

8.引數忽略

  1. 提供了特性NotQueryAttribute,在對於屬性新增即可忽略構建查詢。

9.構建表示式

  1. 只要類繼承了QueryModel等這四個模型,即可以透過ToExpression<TSource>(),構建對於自己模型的表示式。

10.查詢擴充套件

  1. 常規擴充套件,根據傳入的4大模型過濾。例如

 /// <summary>
    /// 查詢擴充套件
    /// </summary>
    public static IQueryable<T> WhereExt<T>(this IQueryable<T> source, QueryModel input)
    {
        // 
    }
    /// <summary>
    /// 查詢擴充套件,並排序
    /// </summary>
    public static IQueryable<T> WhereExt<T>(this IQueryable<T> source, Expression<Func<T,       bool>> expression, params OrderByItem[] orderByItems)
    {
      // 
    }  
  1. 部分查詢擴充套件,可返回傳入的模型類(如果屬性匹配得上的話)

        /// <summary>
        /// Select擴充套件方法
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TTarget"></typeparam>
        /// <param name="query"></param>
        /// <returns></returns>
        public static IQueryable<TTarget> Select<TSource, TTarget>(this IQueryable<TSource> query)
        {
             
        }
  1. IQueryable擴充套件方法

複製程式碼
/// <summary>
/// 根據QueryModel中的條件過濾IQueryable<T>並返回結果列表。 
/// </summary>
/// <param name="source">待處理的IQueryable<T>。</param>
/// <param name="input">包含過濾條件的QueryModel。</param>
/// <returns>未經過濾的T型別列表。</returns>
public static List<T> ToList<T>(this IQueryable<T> source, QueryModel input)
{
    // 實現
}

/// <summary>
/// 根據FullQueryModel中的複雜條件過濾IQueryable<T>並返回結果列表。 
/// </summary>
/// <param name="source">待處理的IQueryable<T>。</param>
/// <param name="input">包含複雜過濾條件的FullQueryModel。</param>
/// <returns>未經過濾的T型別列表。</returns>
public static List<T> ToList<T>(this IQueryable<T> source, FullQueryModel input)
{
    // 實現
}

/// <summary>
/// 根據PageModel中的分頁引數對IQueryable<T>進行分頁處理,並返回包含資料列表和總記錄數的元組。
/// </summary>
/// <param name="query">待分頁處理的IQueryable<T>。</param>
/// <param name="input">包含分頁引數的PageModel。</param>
/// <returns>包含未分頁資料列表和未計算總記錄數的元組。</returns>
public static (List<T> list, int total) ToPageList<T>(this IQueryable<T> query, PageModel input)
{
    // 實現
}

/// <summary>
/// 根據FullPageModel中的分頁和過濾條件對IQueryable<T>進行處理,並返回包含資料列表和總記錄數的元組
/// </summary>
/// <param name="query">待處理的IQueryable<T>。</param>
/// <param name="input">包含分頁和過濾條件的FullPageModel。</param>
/// <returns>包含未分頁且未過濾的資料列表和未計算總記錄數的元組。</returns>
public static (List<T> list, int total) ToPageList<T>(this IQueryable<T> query, FullPageModel input)
{
    // 實現
}

/// <summary>
/// 對IQueryable<T>進行分頁處理,並返回包含資料列表和總記錄數的元組。
/// </summary>
/// <param name="query">待分頁處理的IQueryable<T>。</param>
/// <param name="input">包含分頁引數的PageModel。</param>
/// <returns>包含未分頁資料列表和未計算總記錄數的元組。</returns>
public static (List<T> list, int total) Page<T>(this IQueryable<T> query, PageModel input)
{
    // 實現
}

/// <summary>
/// 根據FullPageModel中的分頁和過濾條件對IQueryable<T>進行處理,並返回包含資料列表和總記錄數的元組。
/// </summary>
/// <param name="query">待處理的IQueryable<T>。</param>
/// <param name="input">包含分頁和過濾條件的FullPageModel。</param>
/// <returns>包含未分頁且未過濾的資料列表和未計算總記錄數的元組。</returns>
public static (List<T> list, int total) Page<T>(this IQueryable<T> query, FullPageModel input)
{
    // 實現
}
/// <summary>
/// 根據PageModel中的分頁和排序引數對IQueryable<T>進行分頁和排序處理,並返回包含排序後資料列表和總記錄數的元組。
/// </summary>
/// <param name="query">待分頁和排序處理的IQueryable<T>。</param>
/// <param name="input">包含分頁和排序引數的PageModel。</param>
/// <returns>包含未分頁且未排序的資料列表和未計算總記錄數的元組。</returns>
public static (List<T> list, int total) OrderPageList<T>(this IQueryable<T> query, PageModel input)
{
    // 實現
}

/// <summary>
/// 根據FullPageModel中的分頁、排序和過濾條件對IQueryable<T>進行處理,並返回包含排序後資料列表和總記錄數的元組。
/// </summary>
/// <param name="query">待處理的IQueryable<T>。</param>
/// <param name="input">包含分頁、排序和過濾條件的FullPageModel。</param>
/// <returns>包含未分頁、未排序且未過濾的資料列表和未計算總記錄數的元組。</returns>
public static (List<T> list, int total) OrderPageList<T>(this IQueryable<T> query, FullPageModel input)
{
    // 實現
}
複製程式碼
  1. IQueryable擴充套件方法之排序

複製程式碼
/// <summary>
/// 根據單個屬性名對IQueryable<TEntity>進行排序。
/// </summary>
/// <param name="input">待排序的IQueryable<TEntity>。</param>
/// <param name="propName">要排序的屬性名。</param>
/// <param name="isDesc">指示排序是否為降序。</param>
/// <returns>未應用排序的IQueryable<TEntity>。</returns>
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> input, string propName, bool isDesc)
{
  // 實現
}

/// <summary>
/// 根據兩個屬性名對IQueryable<TEntity>進行排序。
/// </summary>
/// <param name="input">待排序的IQueryable<TEntity>。</param>
/// <param name="propName">第一個要排序的屬性名。</param>
/// <param name="isDesc">第一個排序是否為降序。</param>
/// <param name="propName2">第二個要排序的屬性名。</param>
/// <param name="isDesc2">第二個排序是否為降序。</param>
/// <returns>未應用排序的IQueryable<TEntity>。</returns>
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> input, string propName, bool isDesc, string propName2, bool isDesc2)
{
  // 實現
}

/// <summary>
/// 根據三個屬性名對IQueryable<TEntity>進行排序。
/// </summary>
/// <param name="input">待排序的IQueryable<TEntity>。</param>
/// <param name="propName">第一個要排序的屬性名。</param>
/// <param name="isDesc">第一個排序是否為降序。</param>
/// <param name="propName2">第二個要排序的屬性名。</param>
/// <param name="isDesc2">第二個排序是否為降序。</param>
/// <param name="propName3">第三個要排序的屬性名。</param>
/// <param name="isDesc3">第三個排序是否為降序。</param>
/// <returns>未應用排序的IQueryable<TEntity>。</returns>
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> input, string propName, bool isDesc, string propName2, bool isDesc2, string propName3, bool isDesc3)
{
  // 實現
}

/// <summary>
/// 根據多個屬性名對IQueryable<TEntity>進行排序。屬性名和排序方向透過陣列提供。
/// </summary>
/// <param name="input">待排序的IQueryable<TEntity>。</param>
/// <param name="isDesc">是否所有排序都應為降序,此引數在可變引數場景下可能不被直接使用,而是作為方法過載的標識。</param>
/// <param name="propNames">要排序的屬性名陣列。</param>
/// <returns>未應用排序的IQueryable<TEntity>。</returns>
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> input, bool isDesc, params string[] propNames)
{
  // 實現
}

/// <summary>
/// 根據IQueryModel中的排序資訊對IQueryable<TEntity>進行排序。
/// </summary>
/// <param name="input">待排序的IQueryable<TEntity>。</param>
/// <param name="queryModel">包含排序資訊的IQueryModel。</param>
/// <returns>未應用排序的IQueryable<TEntity>。</returns>
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> input, IQueryModel queryModel)
{
   // 實現
}

/// <summary>
/// 根據OrderByItem陣列中的排序項對IQueryable<TEntity>進行排序。
/// </summary>
/// <param name="input">待排序的IQueryable<TEntity>。</param>
/// <param name="orderByItems">包含排序屬性和排序方向的OrderByItem陣列。</param>
/// <returns>未應用排序的IQueryable<TEntity>。</returns>
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> input, params OrderByItem[] orderByItems)
{
   // 實現
}

/// <summary>
/// 根據OrderByItem列表中的排序項對IQueryable<TEntity>進行排序。
/// </summary>
/// <param name="input">待排序的IQueryable<TEntity>。</param>
/// <param name="orderByItems">包含排序屬性和排序方向的OrderByItem列表。</param>
/// <returns>未應用排序的IQueryable<TEntity>。</returns>
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> input, List<OrderByItem> orderByItems)
{
   // 實現
}

相關文章