我喜歡在一個項目開發模式成熟的時候,使用代碼生成工具Database2Sharp來配套相關的代碼生成,對於我介紹的基於SqlSugar的開發框架,從整體架構確定下來後,我就著手為它們量身定做相關的代碼開發,這樣可以在後續整合項目功能的時候,利用代碼生成工具快速的生成所需要模塊的骨架代碼,然後在這個基... ...
我喜歡在一個項目開發模式成熟的時候,使用代碼生成工具Database2Sharp來配套相關的代碼生成,對於我介紹的基於SqlSugar的開發框架,從整體架構確定下來後,我就著手為它們量身定做相關的代碼開發,這樣可以在後續整合項目功能的時候,利用代碼生成工具快速的生成所需要模塊的骨架代碼,然後在這個基礎上逐漸增加自定義的內容即可,方便快捷。本篇隨筆介紹基於SqlSugar的開發框架,對框架中涉及到的各個分層或者模塊代碼進行生成的處理。
1、回顧項目的架構和模塊內容
在前面幾篇隨筆中,大概介紹過了基於SqlSugar的開發框架主要的設計模塊,場景如下所示。
基礎核心數據模塊SugarProjectCore,主要就是開發業務所需的數據處理和業務邏輯的項目,為了方便,我們區分Interface、Modal、Service三個目錄來放置不同的內容,其中Modal是SqlSugar的映射實體,Interface是定義訪問介面,Service是提供具體的數據操作實現。其中Service裡面一些框架基類和介面定義,統一也放在公用類庫裡面。
Winform界面,我們可以採用基於.net Framework開發或者.net core6進行開發均可,因為我們的SugarProjectCore項目是採用.net Standard模式開發,相容兩者。
這裡以許可權模塊來進行演示整合使用,我在構建代碼生成工具代碼模板的時候,反覆利用項目中測試沒問題的項目代碼指導具體的模板編寫,這樣編寫出來的模板就會完美符合實際的項目需要了。
在項目代碼及模板完成後,利用代碼生成工具快速生成代碼,相互促進情況下,也完成了Winform項目的界面代碼生成,生成包括普通的列表界面,以及主從表Winform界面代碼生成。
最後許可權系統的Winform項目如下所示。
在前面隨筆《基於SqlSugar的開發框架循序漸進介紹(2)-- 基於中間表的查詢處理》中介紹了基礎功能的一些處理,其中也介紹到了Winform界面端的界面效果,這個以SqlSugar底層處理,最終把許可權、字典等模塊整合到一起,完成一個項目開發所需要的框架結構內容。整個系統包括用戶管理、組織機構管理、角色管理、功能許可權管理、菜單管理、欄位許可權管理、黑白名單、操作日誌、字典管理、客戶信息等模塊內容。
在代碼生成工具中,我們整合了基於SqlSugar的開發框架的項目代碼生成,包括框架基礎的代碼生成,以及Winform界面代碼生成兩個部分,框架項目及Winform界面效果如上圖所示。
2、整合代碼生成工具Database2Sharp進行SqlSugar框架代碼生成
前面隨筆介紹過基於SqlSugar核心Core項目的組成。
基礎核心數據模塊SugarProjectCore,主要就是開發業務所需的數據處理和業務邏輯的項目,為了方便,我們區分Interface、Modal、Service三個目錄來放置不同的內容,其中Modal是SqlSugar的映射實體,Interface是定義訪問介面,Service是提供具體的數據操作實現。
對於Modal層的類代碼生成,常規的普通表(非中間表),我們根據項目所需要,生成如下代碼。目的是利用它定義好對應的主鍵Id,並通過介面約束實體類。
/// <summary> /// 客戶信息 /// 繼承自Entity,擁有Id主鍵屬性 /// </summary> [SugarTable("T_Customer")] public class CustomerInfo : Entity<string>
而對於中間表,我們不要它的繼承繼承關係。
/// <summary> /// 用戶角色關聯 /// </summary> [SugarTable("T_ACL_User_Role")] public class User_RoleInfo { }
只需要簡單的標註好SugarTable屬性,讓他可以和其他業務表進行關聯查詢即可。
/// <summary> /// 根據用戶ID獲取對應的角色列表 /// </summary> /// <param name="userID">用戶ID</param> /// <returns></returns> private async Task<List<RoleInfo>> GetByUser(int userID) { var query = this.Client.Queryable<RoleInfo, User_RoleInfo>( (t, m) => t.Id == m.Role_ID && m.User_ID == userID) .Select(t => t); //聯合條件獲取對象 query = query.OrderBy(t => t.CreateTime);//排序 var list = await query.ToListAsync();//獲取列表 return list; }
對於介面層的類,我們只需要按固定的繼承關係處理好,以及類的名稱變化即可。
/// <summary> /// 系統用戶信息,應用層服務介面定義 /// </summary> public interface IUserService : IMyCrudService<UserInfo, int, UserPagedDto>, ITransientDependency { }
其中 IMyCrudService 是我們定義的基類介面,保存常規的增刪改查等的處理基類,通過傳入泛型進行約束介面參數類型和返回值。
基類介面儘可能滿足實際項目介面所需,這樣可以減少子類的代碼編寫,以及獲得統一調用基類函數的便利。
對於中間表,我們除了生成實體類外,不需要生成其他介面和介面實現層,因為我們不單獨調用它們。
對於具體業務對象對應的介面實現,我們除了確定它的繼承關係外,我們還會重寫它們的一些基類函數,從而實現更加精準的處理。
介面實現類的定義如下所示。
/// <summary> /// 應用層服務介面實現 /// </summary> public class CustomerService : MyCrudService<CustomerInfo, string, CustomerPagedDto>, ICustomerService { }
一般情況下,我們至少需要在子類重寫 CreateFilteredQueryAsync 和 ApplyDefaultSorting 兩個函數,前者是條件的查詢處理,後者是預設的排序處理操作。
/// <summary> /// 自定義條件處理 /// </summary> /// <param name="input">查詢條件Dto</param> /// <returns></returns> protected override ISugarQueryable<CustomerInfo> CreateFilteredQueryAsync(CustomerPagedDto input) { var query = base.CreateFilteredQueryAsync(input); query = query .WhereIF(!input.ExcludeId.IsNullOrWhiteSpace(), t => t.Id != input.ExcludeId) //不包含排除ID .WhereIF(!input.Name.IsNullOrWhiteSpace(), t => t.Name.Contains(input.Name)) //如需要精確匹配則用Equals //年齡區間查詢 .WhereIF(input.AgeStart.HasValue, s => s.Age >= input.AgeStart.Value) .WhereIF(input.AgeEnd.HasValue, s => s.Age <= input.AgeEnd.Value) //創建日期區間查詢 .WhereIF(input.CreateTimeStart.HasValue, s => s.CreateTime >= input.CreateTimeStart.Value) .WhereIF(input.CreateTimeEnd.HasValue, s => s.CreateTime <= input.CreateTimeEnd.Value) ; return query; } /// <summary> /// 自定義排序處理 /// </summary> /// <param name="query">可查詢LINQ</param> /// <returns></returns> protected override ISugarQueryable<CustomerInfo> ApplyDefaultSorting(ISugarQueryable<CustomerInfo> query) { return query.OrderBy(t => t.CreateTime, OrderByType.Desc); //如果先按第一個欄位排序,然後再按第二欄位排序,示例代碼 //return base.ApplySorting(query, input).OrderBy(s=>s.Customer_ID).OrderBy(s => s.Seq); }
根據這些規則,編寫我們所需的模板代碼,讓我們選擇的資料庫表名稱、註釋,以及表欄位的名稱、類型、註釋,外鍵主鍵關係等信息為我們模板所用。
如下所示代碼是NVelocity模板代碼,用於生成上面的條件查詢處理的,可以稍作瞭解。
/// <summary> /// 自定義條件處理 /// </summary> /// <param name="input">查詢條件Dto</param> /// <returns></returns> protected override ISugarQueryable<${ClassName}Info> CreateFilteredQueryAsync(${ClassName}PagedDto input) { var query = base.CreateFilteredQueryAsync(input); query = query #if(${PrimaryKeyNetType}=="string") .WhereIF(!input.ExcludeId.IsNullOrWhiteSpace(), t=>t.Id != input.ExcludeId) //不包含排除ID #else .WhereIF(input.ExcludeId.HasValue, t=>t.Id != input.ExcludeId) //不包含排除ID #end #foreach($EntityProperty in $EntityPropertyList) #if(${EntityProperty.ColumnInfo.IsForeignKey} || ${EntityProperty.PropertyName} == "Status" || ${EntityProperty.PropertyName} == "State" || ${EntityProperty.PropertyName} == "PID" || ${EntityProperty.PropertyName} == "Deleted") .WhereIF(#if(${EntityProperty.ColumnInfo.IsNumeric})input.${EntityProperty.PropertyName}.HasValue#else!input.${EntityProperty.PropertyName}.IsNullOrWhiteSpace()#end, s => s.${EntityProperty.PropertyName} == input.${EntityProperty.PropertyName}) #elseif(${EntityProperty.ColumnInfo.IsDateTime} || ${EntityProperty.ColumnInfo.IsNumeric}) //${EntityProperty.Description}區間查詢 .WhereIF(input.${EntityProperty.PropertyName}Start.HasValue, s => s.${EntityProperty.PropertyName} >= input.${EntityProperty.PropertyName}Start.Value) .WhereIF(input.${EntityProperty.PropertyName}End.HasValue, s => s.${EntityProperty.PropertyName} <= input.${EntityProperty.PropertyName}End.Value) #elseif(${EntityProperty.ColumnInfo.NetType.Alias.ToLower()} != "byte[]" && ${EntityProperty.ColumnInfo.Name.Name.ToString()} != "AttachGUID") .WhereIF(#if(${EntityProperty.NetType.EndsWith("?")})input.${EntityProperty.PropertyName}.HasValue, t => t.${EntityProperty.PropertyName} == input.${EntityProperty.PropertyName}#else!input.${EntityProperty.PropertyName}.IsNullOrWhiteSpace(), t => t.${EntityProperty.PropertyName}.Contains(input.${EntityProperty.PropertyName})#end) //如需要精確匹配則用Equals #end ##endif #end #if(${HasCreationTime}) //創建日期區間查詢 .WhereIF(input.CreationTimeStart.HasValue, s => s.CreationTime >= input.CreationTimeStart.Value) .WhereIF(input.CreationTimeEnd.HasValue, s => s.CreationTime <= input.CreationTimeEnd.Value) #else //創建日期區間查詢(參考) //.WhereIF(input.CreationTimeStart.HasValue, s => s.CreationTime >= input.CreationTimeStart.Value) //.WhereIF(input.CreationTimeEnd.HasValue, s => s.CreationTime <= input.CreationTimeEnd.Value) #end; return query; }
當我們完成所需的模板代碼開發後,就在代碼生成工具主體界面中整合相關的生成功能菜單,界面效果如下所示。
通過菜單選擇【SqlSugar框架代碼生成】,進一步選擇資料庫中的表進行生成,一步步處理即可,最後列出所選資料庫表,並確認生成操作,即可生成SqlSugar框架核心項目的代碼,如下圖所示。
選擇表進行生成後,生成的實體模型類如下所示,包括生成了中間表的實體類。
而介面實現則是根據具體的業務對象規則進行生成。
3、SqlSugar項目中Winform界面的生成
Winform界麵包括普通列表/編輯界面處理,以及主從表界面處理兩個部分,如下圖所示。
生成的簡單業務表界面,包括分頁列表展示界面,在列表界面中整合查看、編輯、新增、刪除、導入、導出、查詢/高級查詢等功能,整合的編輯界面也是依據資料庫表的信息進行生成的。
列表界面和編輯界面效果如下所示。
而主從表界面生成的效果如下所示。
我們看看生成的Winform列表界面代碼,如下所示。
另外我們把一些常用的處理邏輯放在函數中統一處理,如AddData、EditData、DeleteData、BindData、GetData、ImportData、ExportData等等,如下所示。
在獲取數據的時候,我們根據用戶的條件,構建一個分頁查詢對象傳遞,調用介面獲得數據後,進行分頁控制項的綁定處理即可。
/// <summary> /// 獲取數據 /// </summary> /// <returns></returns> private async Task<IPagedResult<CustomerInfo>> GetData() { CustomerPagedDto pagerDto = null; if (advanceCondition != null) { //如果有高級查詢,那麼根據輸入信息構建查詢條件 pagerDto = new CustomerPagedDto(this.winGridViewPager1.PagerInfo); pagerDto = dlg.GetPagedResult(pagerDto); } else { //構建分頁的條件和查詢條件 pagerDto = new CustomerPagedDto(this.winGridViewPager1.PagerInfo) { //添加所需條件 Name = this.txtName.Text.Trim(), }; //日期和數值範圍定義 //年齡,需在CustomerPagedDto中添加 int? 類型欄位AgeStart和AgeEnd var Age = new ValueRange<int?>(this.txtAge1.Text, this.txtAge2.Text); //數值類型 pagerDto.AgeStart = Age.Start; pagerDto.AgeEnd = Age.End; //創建時間,需在CustomerPagedDto中添加 DateTime? 類型欄位CreationTimeStart和CreationTimeEnd var CreationTime = new TimeRange(this.txtCreationTime1.Text, this.txtCreationTime2.Text); //日期類型 pagerDto.CreateTimeStart = CreationTime.Start; pagerDto.CreateTimeEnd = CreationTime.End; } var result = await BLLFactory<CustomerService>.Instance.GetListAsync(pagerDto); return result; }
如果是高級查詢,我們則是根據傳入分頁查詢對象的屬性在高級查詢對話框中進行賦值,然後獲得對象後進行查詢獲得記錄的。
在代碼生成工具中,我們根據實際項目的代碼,定義好對應的模板文件,如下所示。
最後在生成代碼的時候,整合這些NVelocity的模板文件,根據表對象的信息,生成對應的文件供我們開發使用即可。
#region Model 實體部分 string entityTemplateFile = ProjectPath + "Templates/Entity.cs.vm"; var entityAdapter = new SugarEntityAdapter(databaseInfo, selectedTableNames, entityTemplateFile); entityAdapter.DirectoryOfOutput = mainSetting.RootNameSpace + "/Core/Modal"; entityAdapter.Execute(); #endregion #region Interface部分和Application部分 var appInterface = new SugarServiceInterfaceAdapter(databaseInfo, selectedTableNames, ProjectPath + "Templates/IService.cs.vm", databaseTypeName); appInterface.DirectoryOfOutput = mainSetting.RootNameSpace + "/Core/Interface"; appInterface.Execute(); var appService = new SugarServiceAdapter(databaseInfo, selectedTableNames, ProjectPath + "Templates/Service.cs.vm", databaseTypeName); appService.DirectoryOfOutput = mainSetting.RootNameSpace + "/Core/Service"; appService.Execute(); #endregion #region Web API Controller 部分 var controller = new SugarControllerAdapter(databaseInfo, selectedTableNames, ProjectPath + "Templates/Controller.cs.vm", databaseTypeName); controller.DirectoryOfOutput = mainSetting.RootNameSpace + "/Controller"; controller.Execute(); #endregion
系列文章:
《基於SqlSugar的開發框架的循序漸進介紹(1)--框架基礎類的設計和使用》
《基於SqlSugar的開發框架循序漸進介紹(2)-- 基於中間表的查詢處理》
《基於SqlSugar的開發框架循序漸進介紹(3)-- 實現代碼生成工具Database2Sharp的整合開發》
主要研究技術:代碼生成工具、會員管理系統、客戶關係管理軟體、病人資料管理軟體、Visio二次開發、酒店管理系統、倉庫管理系統等共用軟體開發
專註於Winform開發框架/混合式開發框架、Web開發框架、Bootstrap開發框架、微信門戶開發框架的研究及應用。
轉載請註明出處:
撰寫人:伍華聰 http://www.iqidi.com