在一般的檢索界面中,基於界面易用和美觀方便的考慮,我們往往只提供一些常用的條件查詢進行列表數據的查詢,但是有時候一些業務表欄位很多,一些不常見的條件可能在某些場景下也需要用到。因此我們在通用的查詢條件之外,一般可以考慮增加 一個高級查詢的模塊來管理這些不常見條件的查詢處理。本篇隨筆基於這個需求,綜合... ...
在一般的檢索界面中,基於界面易用和美觀方便的考慮,我們往往只提供一些常用的條件查詢進行列表數據的查詢,但是有時候一些業務表欄位很多,一些不常見的條件可能在某些場景下也需要用到。因此我們在通用的查詢條件之外,一般可以考慮增加 一個高級查詢的模塊來管理這些不常見條件的查詢處理。本篇隨筆基於這個需求,綜合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對象即可。