1. 程式人生 > >ABP開發框架前後端開發系列---(13)高階查詢功能及介面的處理

ABP開發框架前後端開發系列---(13)高階查詢功能及介面的處理

在一般的檢索介面中,基於介面易用和美觀方便的考慮,我們往往只提供一些常用的條件查詢進行列表資料的查詢,但是有時候一些業務表字段很多,一些不常見的條件可能在某些場景下也需要用到。因此我們在通用的查詢條件之外,一般可以考慮增加 一個高階查詢的模組來管理這些不常見條件的查詢處理。本篇隨筆基於這個需求,綜合ABP框架的特點,整合了高階查詢模組功能的處理。

1、高階查詢模組的回顧

我們知道,在介面佈局中,一般常見的查詢條件不能太多,否則會顯得臃腫而且佔用太多空間,非常不美觀,因此常見的查詢都是提供寥寥幾個的輸出條件進行列表記錄的查詢的。

又或者一些更多內容的介面,我們也是僅僅提供多幾個條件,其他的想辦法通過高階查詢介面進行查詢管理。

在早期部落格裡面《Winform開發框架之通用高階查詢模組》,我曾經介紹過一款通用的高階查詢介面處理,用在Winform框架裡面,可以對資料表更多的欄位進行統一的查詢處理。

對於內容較多的查詢,我們可以在主介面增加一個高階查詢按鈕入口,如上圖所示,單擊後,顯示一個所有欄位的列表,如下介面。

一般來說,查詢條件分為文字輸入,如姓名,郵件,名稱等這些。

日期型別條件輸入介面:

數字型別條件輸入介面:

 輸入以上幾種條件後,高階查詢介面裡面會顯示友好的條件內容,確保使用者能夠看懂輸入的條件,如下所示是輸入幾個不同型別的條件的顯示內容。

以上是高階查詢模組的思路,整體介面和處理邏輯雖然可以採用,但是在ABP框架模式下,以前的處理方式有所不同了,下面詳細介紹一下如何在ABP框架模組下整合這個高階查詢模組的內容。

 

2、ABP框架模組下的高階查詢處理

我們先來了解一下最終在ABP框架下整合的高階查詢模組介面如下所示。

可以設定一些模糊查詢條件,以及一些區間的查詢值,如下所示。

這個模組是以ABP框架的Web API獲取資料,並通過Winform介面進行呼叫,從而形成了一個ABP+Winform的框架體系。

前面ABP框架系列介紹過,我們一般使用GetAll和分頁條件DTO進行資料的檢索,如下是產品分頁DTO的定義

    /// <summary>
    /// 用於根據條件分頁查詢,DTO物件
    /// </summary>
    public class ProductPagedDto : PagedAndSortedInputDto

而PagedAndSortedInputDto也是自定義的類,它主要用來承載一些分頁和排序的資訊,如下所示

    /// <summary>
    /// 帶有排序物件的分頁基類
    /// </summary>
    public class PagedAndSortedInputDto : PagedInputDto, ISortedResultRequest
    {
        /// <summary>
        /// 排序資訊
        /// </summary>
        public string Sorting { get; set; }

其中的PagedInputDto也是自定義類,主要承載分頁資訊。

    /// <summary>
    /// 分頁物件
    /// </summary>
    public class PagedInputDto : IPagedResultRequest
    {
        [Range(1, int.MaxValue)]
        public int MaxResultCount { get; set; }

        [Range(0, int.MaxValue)]
        public int SkipCount { get; set; }

        public PagedInputDto()
        {
            MaxResultCount = int.MaxValue;
        }
    }

這樣的構建,我們可以傳遞分頁和排序資訊,因此在GetAll函式裡面,就可以根據這些條件進行資料查詢了。

而我們通過重寫過濾條件和排序處理,就可以實現資料的分頁查詢了。對於產品資訊的過濾處理和排序處理,我們重寫函式如下所示。

        /// <summary>
        /// 自定義條件處理
        /// </summary>
        /// <param name="input">查詢條件Dto</param>
        /// <returns></returns>
        protected override IQueryable<Product> CreateFilteredQuery(ProductPagedDto input)
        {
            return base.CreateFilteredQuery(input)
                .WhereIf(!input.ExcludeId.IsNullOrWhiteSpace(), t => t.Id != input.ExcludeId) //不包含排除ID

                .WhereIf(!input.ProductNo.IsNullOrWhiteSpace(), t => t.ProductNo.Contains(input.ProductNo)) //如需要精確匹配則用Equals
                .WhereIf(!input.BarCode.IsNullOrWhiteSpace(), t => t.BarCode.Contains(input.BarCode)) //如需要精確匹配則用Equals
                .WhereIf(!input.MaterialCode.IsNullOrWhiteSpace(), t => t.MaterialCode.Contains(input.MaterialCode)) //如需要精確匹配則用Equals
                .WhereIf(!input.ProductType.IsNullOrWhiteSpace(), t => t.ProductType.Contains(input.ProductType)) //如需要精確匹配則用Equals
                .WhereIf(!input.ProductName.IsNullOrWhiteSpace(), t => t.ProductName.Contains(input.ProductName)) //如需要精確匹配則用Equals
                .WhereIf(!input.Unit.IsNullOrWhiteSpace(), t => t.Unit.Contains(input.Unit)) //如需要精確匹配則用Equals
                .WhereIf(!input.Note.IsNullOrWhiteSpace(), t => t.Note.Contains(input.Note)) //如需要精確匹配則用Equals
                .WhereIf(!input.Description.IsNullOrWhiteSpace(), t => t.Description.Contains(input.Description)) //如需要精確匹配則用Equals
                 
                 //狀態
                .WhereIf(input.Status.HasValue, t => t.Status==input.Status)
                                                                                              
                 //成本價區間查詢
                .WhereIf(input.PriceStart.HasValue, s => s.Price >= input.PriceStart.Value)
                .WhereIf(input.PriceEnd.HasValue, s => s.Price <= input.PriceEnd.Value)

                //銷售價區間查詢
                .WhereIf(input.SalePriceStart.HasValue, s => s.SalePrice >= input.SalePriceStart.Value)
                .WhereIf(input.SalePriceEnd.HasValue, s => s.SalePrice <= input.SalePriceEnd.Value)

                //特價區間查詢
                .WhereIf(input.SpecialPriceStart.HasValue, s => s.SpecialPrice >= input.SpecialPriceStart.Value)
                .WhereIf(input.SpecialPriceEnd.HasValue, s => s.SpecialPrice <= input.SpecialPriceEnd.Value)
                .WhereIf(input.IsUseSpecial.HasValue, t => t.IsUseSpecial == input.IsUseSpecial) //如需要精確匹配則用Equals
                                                                                                  
                //最低折扣區間查詢
                .WhereIf(input.LowestDiscountStart.HasValue, s => s.LowestDiscount >= input.LowestDiscountStart.Value)
                .WhereIf(input.LowestDiscountEnd.HasValue, s => s.LowestDiscount <= input.LowestDiscountEnd.Value)

                //建立日期區間查詢
                .WhereIf(input.CreationTimeStart.HasValue, s => s.CreationTime >= input.CreationTimeStart.Value)
                .WhereIf(input.CreationTimeEnd.HasValue, s => s.CreationTime <= input.CreationTimeEnd.Value);
        }

        /// <summary>
        /// 自定義排序處理
        /// </summary>
        /// <param name="query">可查詢LINQ</param>
        /// <param name="input">查詢條件Dto</param>
        /// <returns></returns>
        protected override IQueryable<Product> ApplySorting(IQueryable<Product> query, ProductPagedDto input)
        {
            //按建立時間倒序排序
            return base.ApplySorting(query, input).OrderByDescending(s => s.CreationTime);//時間降序
        }

雖然我們一般在介面上不會放置所有的條件,但是高階查詢模組倒是可以把分頁條件DTO裡面的條件全部擺上去的。

高階查詢模組的條件如下所示。

 我們高階查詢裡面的條件還是以GetAll裡面的物件分頁查詢Dto裡面的屬性,我們需要根據這些條件進行構建,也需要以這些屬性的型別進行一個控制元件的選擇。

因此我們需要一個屬性的名稱說明,以及在高階查詢模組的列表介面中對顯示那些欄位進行控制,如下程式碼所示。

        private FrmAdvanceSearch dlg;
        /// <summary>
        /// 高階查詢的操作
        /// </summary>        
        private async void AdvanceSearch()
        {
            if (dlg == null)
            {
                dlg = new FrmAdvanceSearch();
                dlg.SetFieldTypeList<ProductPagedDto>();//通過分頁物件獲取查詢屬性和型別 
                dlg.ColumnNameAlias = await ProductApiCaller.Instance.GetColumnNameAlias();                
                dlg.DisplayColumns = "ProductNo,BarCode,MaterialCode,ProductType,ProductName,Unit,Price,SalePrice,SpecialPrice,IsUseSpecial,LowestDiscount,Note,Description,Status,CreatorUserId,CreationTime";

通過 SetFieldTypeList<ProductPagedDto> 的處理,我們把分頁物件的查詢屬性和型別賦值給了高階查詢模組,讓它根據型別來建立不同的輸入顯示,如常規的字串、數值區段、日期區段,下拉列表等等。

對於下拉列表,我們需要繫結它的資料來源,如下程式碼所示。

 dlg.AddColumnListItem("ProductType", await DictItemUtil.GetDictListItemByDictType("產品型別"));//字典列表
 dlg.AddColumnListItem("Status", await DictItemUtil.GetDictListItemByDictType("產品狀態"));//字典列表

而對於一些常規的固定列表,也可以以類似的方式加入下拉列表

    //固定轉義的列表
    var specialList = new List<CListItem>() { new CListItem("特價", "True"), new CListItem("一般", "False") };
    dlg.AddColumnListItem("IsUseSpecial", specialList);

或者

    dlg.AddColumnListItem("Sex", "男,女");//固定列表

因此整個呼叫高階查詢模組的程式碼如下所示

    private FrmAdvanceSearch dlg;
    /// <summary>
    /// 高階查詢的操作
    /// </summary>        
    private async void AdvanceSearch()
    {
        if (dlg == null)
        {
            dlg = new FrmAdvanceSearch();
            dlg.SetFieldTypeList<ProductPagedDto>();//通過分頁物件獲取查詢屬性和型別 
            dlg.ColumnNameAlias = await ProductApiCaller.Instance.GetColumnNameAlias();                
            dlg.DisplayColumns = "ProductNo,BarCode,MaterialCode,ProductType,ProductName,Unit,Price,SalePrice,SpecialPrice,IsUseSpecial,LowestDiscount,Note,Description,Status,CreatorUserId,CreationTime";

            #region 下拉列表資料

            dlg.AddColumnListItem("ProductType", await DictItemUtil.GetDictListItemByDictType("產品型別"));//字典列表
            dlg.AddColumnListItem("Status", await DictItemUtil.GetDictListItemByDictType("產品狀態"));//字典列表

            //固定轉義的列表
            var specialList = new List<CListItem>() { new CListItem("特價", "True"), new CListItem("一般", "False") };
            dlg.AddColumnListItem("IsUseSpecial", specialList);

            //dlg.AddColumnListItem("Sex", "男,女");//固定列表
            //dlg.AddColumnListItem("Credit", await ProductApiCaller.Instance.GetFieldList("Credit"));//動態列表

            #endregion

            dlg.ConditionChanged += new FrmAdvanceSearch.ConditionChangedEventHandler(dlg_ConditionChanged);
        }
        dlg.ShowDialog();
    }

在處理獲取資料GetData函式的時候,我們需要根據高階查詢進行一定的切換,以便顯示正確的過濾條件,如下程式碼所示是獲取資料的處理。

        /// <summary>
        /// 獲取資料
        /// </summary>
        /// <returns></returns>
        private async Task<IPagedResult<ProductDto>> GetData()
        {
            ProductPagedDto pagerDto = null;
            if (advanceCondition != null)
            {
                pagerDto = new ProductPagedDto(this.winGridViewPager1.PagerInfo);
                pagerDto = dlg.GetPagedResult(pagerDto);               
            }
            else
            {
                //構建分頁的條件和查詢條件
                pagerDto = new ProductPagedDto(this.winGridViewPager1.PagerInfo)
                {
                    //新增所需條件
                    ProductNo = this.txtProductNo.Text.Trim(),
                    BarCode = this.txtBarCode.Text.Trim(),
                    MaterialCode = this.txtMaterialCode.Text.Trim(),
                    ProductType = this.txtProductType.Text.Trim(),
                    ProductName = this.txtProductName.Text.Trim(),
                    Description = this.txtDescription.Text.Trim(),
                };

                //日期和數值範圍定義
                //建立時間,需在ProductPagedDto中新增DateTime?型別欄位CreationTimeStart和CreationTimeEnd
                var CreationTime = new TimeRange(this.txtCreationTime1.Text, this.txtCreationTime2.Text); //日期型別
                pagerDto.CreationTimeStart = CreationTime.Start;
                pagerDto.CreationTimeEnd = CreationTime.End;

            }

            var result = await ProductApiCaller.Instance.GetAll(pagerDto);
            return result;
        }

在高階查詢的處理方式下,我們是傳入一個列表的分頁物件屬性,然後傳入一個分頁DTO物件,就可以構建出我們需要的分頁查詢條件,傳遞給Web API端獲取對應條件的資料了。

    pagerDto = new ProductPagedDto(this.winGridViewPager1.PagerInfo);
    pagerDto = dlg.GetPagedResult(pagerDto);        

而高階查詢模組,所需要處理的邏輯就是需要根據不同的屬性型別,賦值常規的屬性值或者區段屬性值,從而構建出分頁對應的屬性條件即可。

如果是區段(包括日期或者數值)的,我們分頁查詢條件裡面,會有一個ABCStart,ABCEnd的物件屬性,依照這個規則,獲取到對應的使用者輸入,採用反射方式賦值DTO物件即可。

&n