我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。
1、數據的列表展示界面
在一個頁面模塊中,列表界面往往是我們的入口界面,其中包含一些查詢條件,數據展示,以及如新建、導入、導出、編輯等功能的入口,如下是我們常規的系統界面,使用固定的表欄位來創建的常規化界面。
這個模塊是一個標準的列表展示模塊,由於不同的業務模塊,我們生成不同的界面,因此,其中的欄位確定的,因此界面也是可以做到個性化的處理,基本上都是強類型的處理了。
雖然這種方式查詢比較快,界面比較個性化,不過缺點是每個界面需要單獨處理,即使利用我們的代碼生成工具,也只是加快開發後端代碼和前端界面代碼的開發,模式並未變化。
如果我們採用EAV模型的處理,由於我們的業務表和欄位是動態定義的,因此針對所有的業務界面都是一個通用的窗體來演化的,也就是新增業務模塊,代碼不用變化,統一展示處理和保存處理。
那這樣,我們的查詢欄位就可能需要動態化了,但是WInform沒有BS前端那樣比較容易定製界面佈局,因此我們換一種思路來解決這個問題,把查詢欄位通過列表選擇的方式,條件根據不同的數據類型來創建不同的輸入控制項來實現,如下所示。
2、查詢欄位和條件輸入的處理
通過下拉列表的方式選擇不同的欄位進行查詢,如果是數值型或者日期,我們可以根據不同類型來創建輸入控制項組,來實現區間數值或者日期的輸入。
我們的EAV模型設計中,實體類型和屬性的定義是放在關係型資料庫(如SqlServer、Oracle等)中出存儲的,如下所示。
而實體記錄的數據,則是根據屬性不同的類型,保存在MongoDB的NoSQL資料庫中的。
對於查詢,我們通用的處理,就是從關係型資料庫中獲取屬性的定義信息,然後填充到下拉列表中即可。
/// <summary> /// 初始化屬性列表 /// </summary> /// <returns></returns> public async Task InitProperties() { if (EntityType_ID.IsNullOrEmpty()) { return; } //獲取實體類的屬性列表 var attrList = await BLLFactory<IAttributeService>.Instance.GetAllEnableByType(EntityType_ID); if (attrList?.Count > 0) { var attrItems = attrList.Select(s => new CListItem(s.Name, s.Id)).ToArray(); this.txtAttribute.Properties.Items.Clear(); this.txtAttribute.Properties.Items.AddRange(attrItems); //預設選擇 if (attrItems.Length > 0) { this.txtAttribute.SelectedIndex = 0; } } }
為了使得創建界面更加方便,我們創建一個用戶控制項來處理這些內容的變化,以及根據不同屬性值類型的變化,顯示不同的輸入控制項(如數值型、日期型、文本型)。
由於不同的控制項組的界面長度不同,因此我們可以使用一個StackPanel的方式,來動態添加控制項到面板中,這樣顯示更加緊湊好看。
在欄位的變化後,觸發添加的輸入控制項組,如下代碼所示。
private void txtAttribute_SelectedIndexChanged(object sender, System.EventArgs e) { var attributeId = this.txtAttribute.GetComboBoxValue(); var attributeInfo = GetAttributeInfo(attributeId); if(attributeInfo != null) { AddFilterControl(attributeInfo); } }
如對於日期類型的數據,我們動態添加兩個不同的起始日期控制項輸入,可以讓用戶輸入開始日期,結束日期的區間進行查詢。
case EAVValueType.DATETIME: { var dateStart = CreateDateEdit("dateStart", info); this.panel1.Controls.Add(dateStart); var labelDate = new LabelControl(); labelDate.Text = "~"; this.panel1.Controls.Add(labelDate); var dateEnd = CreateDateEdit("dateEnd", info); this.panel1.Controls.Add(dateEnd); }
在我們觸發查詢按鈕的事件的時候,根據面板的控制項內容,進行查詢的處理代碼如下所示。
界面效果如下所示。
3、自定義條件查詢
上面的條件是針對一個欄位條件進行查詢的,有時候為了定位出符合條件的記錄,可能需要輸入多個不同的條件進行組合查詢,那麼應該如何處理這個需求呢。
因為WInform上創建自定義佈局,確實不太好處理,因此變通一下,採用一個構建不同自定義條件作為一個用戶自定義控制項的方式,讓用戶添加多個條件進行組合即可。
而且組合條件需要動態化創建,不需要的條件可以隨時移除,最好能夠緊湊型的,那麼可以採用StackPanel來堆疊不同的條件,在任何位置移除一個,都會順序顯示。
首先創建一個主窗體面板,來承載添加和顯示多個條件以及觸發查詢的處理按鈕,如下所示。
每個條件我們根據屬性欄位的名稱,以及屬性值的類型來動態創建,因此需要自定義一個用戶控制項比較好,整塊作為一個模塊,添加移除也方便。
自定義用戶控制項的一項條件,如下所示。
整個界面的面板中可以添加多個條件或者移除任何一個條件,如下界面所示。
創建的只定義條件查詢,觸發條件通過只定義事件進行處理即可。
/// <summary> /// 動態生成條件組合的界面 /// </summary> public partial class FrmEAVCondition : BaseForm { /// <summary> /// 實體類型ID /// </summary> public string EntityType_ID { get; set; } /// <summary> /// 自定義條件變化委托 /// </summary> /// <param name="condition"></param> public delegate void ConditionChangedEventHandler(FilterDefinition<BsonDocument> condition); /// <summary> /// 查詢條件觸發的事件 /// </summary> public event ConditionChangedEventHandler ConditionChanged;
查詢控制項按鈕觸發查詢處理,代碼如下所示。
/// <summary> /// 觸發查詢按鈕,根據多個條件組合查詢 /// </summary> private void btnSearch_Click(object sender, EventArgs e) { var filterControls = new List<FilterDefinition<BsonDocument>>(); foreach (Control control in panel1.Controls) { if (control is EAVFilterControl filterControl) { var filter = filterControl.GetCondition(); if (filter != null) { filterControls.Add(filter); } } } var filterList = Builders<BsonDocument>.Filter.Empty; if (filterControls.Count > 0) { filterList &= Builders<BsonDocument>.Filter.And(filterControls); } // 處理數據查詢後的事件觸發 if (ConditionChanged != null && filterList != null) { ConditionChanged(filterList); } }
這樣在列表展示的界面中調用這個只定義條件窗體,併在條件變化後,重新根據選擇條件,刷新列表即可,如下代碼所示。
/// <summary> /// 自定義條件查詢 /// </summary> private void btnCondtion_Click(object sender, EventArgs e) { if (this.EntityType_ID.IsNullOrWhiteSpace()) { MessageDxUtil.ShowTips("請設置實體類ID"); return; } var dlg = new FrmEAVCondition(); dlg.EntityType_ID = this.EntityType_ID; dlg.ConditionChanged += async (condition) => { this.advanceCondition = null; this.filterCondition = condition; await BindData(); }; dlg.ShowDialog(); }
另外,我們也可以根據相同的處理方式,構建一個高級查詢模塊的處理。
我根據已有的高級查詢處理界面效果,改進了一下,適應於EAV模型的高級查詢,比較容易操作一些。
高級查詢的界面彈出一個窗體,在裡面的條件雙擊內容輸入即可,一般文本是模糊查詢,數值和日期是區間查詢。如下界面所示。
以上就是EAV模型設計裡面,關於動態化查詢處理的一些界面心得,主要就是為了適應所有的模塊處理,彈性化的界面設計,可以有效的屏蔽一些差異。
如果你有好的建議,也可以在留言區一起交流,感謝抽出寶貴的時間進行閱讀及支持。
專註於代碼生成工具、.Net/.NetCore 框架架構及軟體開發,以及各種Vue.js的前端技術應用。著有Winform開發框架/混合式開發框架、微信開發框架、Bootstrap開發框架、ABP開發框架、SqlSugar開發框架等框架產品。轉載請註明出處:撰寫人:伍華聰 http://www.iqidi.com