超簡單的整合表示式樹查詢元件,Sy.ExpressionBuilder 使用說明

noert發表於2022-03-07

    Sy.ExpressionBuilder是一套依賴於表示式樹上的整合的查詢元件。設計的初衷沒別的,就為了少寫程式碼,讓查詢業務可以變得更加模式化。可以從nuget 獲取到該元件(目前支援 .net5 和.net6 版本)。

    來到查詢,查詢實體需要繼承  QueryPageModel或者 QueryModel,從名字也基本可以看出來,一個用於分頁,一個無分頁,你可以根據自己需求選用哪個方式,如下我選了帶分頁的方式。

public partial class AllManagerDto:QueryPageModel

   這樣這個查詢實體就擁有了我們這個外掛的大多數功能。

    屬性名稱約束
    為了方便處理各種屬性型別,我做了一些屬性名稱的約定。 

    時間範圍查詢 =>屬性名稱 以  Start,End 結尾 ,生成條件為 >= 和<=。
    數字範圍查詢 =>屬性名稱 以  Min,Max 結尾 ,生成條件為 >= 和<=。
    字串查詢 => 名字需要和表欄位一致,生成條件為  Contains。

    編號查詢必須是以Id結尾,不然如果編號為支付串的查詢方式會以Contains形式查詢。

    
    例如:

超簡單的整合表示式樹查詢元件,Sy.ExpressionBuilder 使用說明
 /// <summary>
        /// 租戶編號
        /// </summary>
        public virtual int? TenantIdMin { get; set; }
        /// <summary>
        /// 租戶編號
        ///</summary>
        public virtual int? TenantIdMax { get; set; }
        /// <summary>
        /// 建立時間
        /// </summary>
        public virtual DateTime? CreateTimeStart { get; set; }
        /// <summary>
        /// 建立時間
        ///</summary>
        public DateTime? CreateTimeEnd { get; set; }
        /// <summary>
        /// 建立人編號
        /// </summary>
        public virtual string? CreateUserId { get; set; }
View Code

    特性約束
    應用ConditionAttribute 特性,引數為 (欄位名稱,條件,查詢方式)  目前我定義了常用的範圍的約束。範圍型別列舉       

超簡單的整合表示式樹查詢元件,Sy.ExpressionBuilder 使用說明
/// <summary>
    /// 高階搜尋條件
    /// </summary>
    [Description("高階搜尋條件")]
    public enum EnumCondition
    {
        /// <summary>
        /// 包含
        /// </summary>
        [Description("包含")]
        Contains = 0,

        /// <summary>
        /// 等於
        /// </summary>
        [Description("等於")]
        Equal = 1,

        /// <summary>
        /// 大於等於
        /// </summary>
        [Description("大於等於")]
        GtEqual = 2,

        /// <summary>
        /// 大於
        /// </summary>
        [Description("大於")]
        Gt = 3,

        /// <summary>
        /// 小於等於
        /// </summary>
        [Description("小於等於")]
        LtEqual = 4,

        /// <summary>
        /// 小於
        /// </summary>
        [Description("小於")]
        Lt = 5,


        /// <summary>
        /// 不等於
        /// </summary>
        [Description("不等於")]
        NotEqual = 6,


        /// <summary>
        /// SQL(In函式)
        /// </summary>
        [Description("SQL In")]
        In = 7,

        /// <summary>
        /// 在什麼之間
        /// </summary>
        [Description("在什麼之間")]
        Between = 8,

        /// <summary>
        /// 不包含
        /// </summary>
        [Description("不包含")]
        NotContain = 9,

        /// <summary>
        /// 從尾部匹配
        /// </summary>
        [Description("從尾部匹配")]
        EndsWith = 10,

        /// <summary>
        /// 從頭部匹配
        /// </summary>
        [Description("從頭部匹配")]
        StartsWith = 11,

        /// <summary>
        /// 不在範圍內
        /// </summary>
        [Description("不在範圍內")]
        NotIn = 12,

        /// <summary>
        /// 空的
        /// </summary>
        [Description("空的")]
        IsEmpty = 13,

        /// <summary>
        /// 不為空的
        /// </summary>
        [Description("不為空的")]
        IsNotEmpty = 14,

        /// <summary>
        /// 非Null的
        /// </summary>
        [Description("非Null的")]
        IsNotNull = 15,

        /// <summary>
        /// Null的
        /// </summary>
        [Description("Null的")]
        IsNull = 16,

        /// <summary>
        /// IsNullOrWhiteSpace
        /// </summary>
        [Description("IsNullOrWhiteSpace")]
        IsNullOrWhiteSpace = 17,

        /// <summary>
        /// IsNotNullNorWhiteSpace
        /// </summary>
        [Description("IsNotNullNorWhiteSpace")]
        IsNotNullNorWhiteSpace = 18,

        /// <summary>
        /// 
        /// </summary>
        [Description("列舉")]
        HasFlag = 19,

    }
View Code

   當然我們怎麼會少得了查詢方式(且和或)的約束,這就放上來

超簡單的整合表示式樹查詢元件,Sy.ExpressionBuilder 使用說明
  /// <summary>
    /// 當前條件所屬型別
    /// </summary>
    [Description("當前條件所屬型別")]
    public enum EnumConditionType
    {
        /// <summary>
        ////// </summary>
        [Description("")]
        And = 0,

        /// <summary>
        ////// </summary>
        [Description("")]
        Or = 1
    }
View Code

  這樣就構成了我們的查詢約束,一般情況下,當前表欄位查詢的話我們只要屬性名和表欄位名一直即可,例如查詢使用者表的下UserName,如下即可

    /// <summary>
    /// 使用者名稱稱
    /// </summary>
    public string? UserName { get; set; }

  如果有那種不想暴露欄位在外部的,這時我們的特性才會說顯示出使用者,例如我還是要查詢UserName,但是暴露給前端的名稱確是Uname,因為特性中的屬性名優先順序會高於查詢模型中的名稱,那我們可如下處理

   [Condition("UserName", EnumCondition.Contains,EnumConditionType.And)]
    public string?Uname { get; set; }

 又或者我們的某個欄位需要包含一個集合的情況,我們可以如下實現

   [Condition("UserId",EnumCondition.In,EnumConditionType.And)]
   public string?UserIds{ get; set; }

 導航屬性單個查詢,例如我在使用者表,要根據角色名稱查詢,我們只要如下定義即可(導航屬性名【角色表】+“.”+角色表下的角色名稱,注意這個英文的 .,這個才是精髓)

[Condition($"{nameof(Role)}.{nameof(Role.RoleName)}", EnumCondition.Contains,EnumConditionType.And)] 
public string? RoleName { get; set; }

 導航屬性集合查詢,例如我在角色表,要查詢有分配使用者名稱字叫老王的所有角色,我們只要如下定義即可(導航屬性名+“[”+角色表下的角色名稱+"]",注意這個英文的 [], [] 表示這個是個集合)
   

[Condition("Users[UserName]", EnumCondition.Contains,EnumConditionType.And)] 
public string? UserName{ get; set; }

 特別說明,該元件還支援位移列舉的查詢,使用也超級簡單,和一般屬性幾乎無差,查詢模型中如下定義即可。

/// <summary>
/// 性別
/// </summary>
 public EnumGender? Gender { get; set; }

不參與查詢特性

   如果我們有個別引數是作用於別的用途,不直接引數查詢,或者目前該外掛處理不了的,我們可以通過該特性排除,如下使用
 

  /// <summary>
  /// 不參與查詢
  /// </summary>
  [NotQuery]
  public string TreeId { get; set; }

額外擴充套件
  對於排序,有些情況要做到使用者點選表頭,然後由後端進行排序後返回,這裡我也預留了空間,如下(input 為繼承了:QueryPageModel 的模型),第一個參數列示 要排序的欄位名,第二個引數true 表示倒序,false 表示正序。

 

 input.AddOrderByItem(nameof(News.Id),true);

我還加了一個相對顯得雞肋的預設排序,如果前端有傳排序過來的話,這個是無效的,使用方式如下:

input.DefaultOrderBy(nameof(News.CreateTime)

 

當我們瞭解了以上約定,我們定義一個相對完整的查詢模型,如下

超簡單的整合表示式樹查詢元件,Sy.ExpressionBuilder 使用說明
 /// <summary>
    /// 查詢引數實體
    /// </summary>

    public class AllManagerDto : QueryPageModel
    {
        /// <summary>
        /// 建立時間 開始(時間必須以Start結尾)
        /// </summary>
        public DateTime? CreateTimeStart { get; set; }

        /// <summary>
        /// 建立時間  結束(結束時間必須以End結尾)
        /// </summary>
        public DateTime? CreateTimeEnd { get; set; }
      
        /// <summary>
        /// 角色編號
        /// </summary>
        [Condition("Role.Id", EnumCondition.In)]
        public string RoleId { get; set; }

        /// <summary>
        /// 角色名稱
        /// </summary>

        [Condition("Role.RoleName", EnumCondition.Contains)]

        public string RoleName { get; set; }


        /// <summary>
        /// 系統名稱
        /// </summary>

        [Condition("Sys[SysName]", EnumCondition.Contains)]

        public string SysName { get; set; }


        /// <summary>
        /// 名稱
        /// </summary>
        public string UserName { get; set; }


        /// <summary>
        /// 性別
        /// </summary>

        public EnumGender? Gender { get; set; }


        [NotMapped]
        public string TreeId { get; set; }

        /// <summary>
        /// 年齡 開始(必須以Min結尾)
        /// </summary>
        public int? AgeMin { get; set; }

        /// <summary>
        /// 年齡  結尾(必須以Max結尾)
        /// </summary>
        public int? AgeMax{ get; set; }
 


    }
View Code

現在我們就可以進行查詢了,如下(留意這句,input.ToQueryModel<Manager>() 主要是這句把查詢引數轉換成表示式了)

       /// <summary>
        /// 查詢
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static List<Manager> GetAll(AllManagerDto input)
        {
            var list = GetList();
            input.RoleId = "1";
            input.Tel = "18888888888";
            input.UserName = "張三";
            input.Gender = EnumGender.Man;
            input.CreateTimeStart = DateTime.Parse("2021-9-22");
            var query = input.ToQueryModel<Manager>();
            return list.AsQueryable().Where(query).ToList();
        }

放個以前的效果圖

 後續有擴充套件,會在這裡加.....

 好了,到這你又可以去擼程式碼了。

    

    

 

相關文章