基於SqlSugar的數據庫訪問處理的封裝,支持多數據庫並使之適應於實際業務開發中(2)
在上篇隨筆《基於SqlSugar的數據庫訪問處理的封裝,支持多數據庫並使之適應於實際業務開發中》中介紹了SqlSugar的基礎用法,以及實現對常規項目中對數據訪問的基類封裝,並通過編寫單元測試覆蓋相關的功能測試,雖然最後編寫單元測試的代碼就是實際調用數據處理的代碼,不過沒有界面不太直觀,本篇隨筆繼續深入SqlSugar的使用介紹,介紹基於Winform項目界面的整合測試。
1、數據訪問層的實現
在上篇隨筆,我們介紹了SqlSugar使用起來還是非常簡單的,首先定義好和數據表對應的實體類信息,通過特性聲明給的方式,聲明表名和字段信息(包括主鍵信息)
如對於數據庫表的標註:
[SugarTable("TB_DictData")] public class DictDataInfo { }
以及對字段信息主鍵的標註
/// <summary> /// 編號 /// </summary> [SugarColumn(IsPrimaryKey = true)] public virtual string ID { get; set; }
或者是自增字段的標註處理
public class Person { //數據庫字段 [SugarColumn(IsPrimaryKey =true,IsIdentity =true)] public int Id { get; set; }
例如我們對於Winform開發框架中的字典數據庫,設計關係如下所示。
我們生成器對應的SQLSugar實體信息如下所示,這些枯燥的工作可以交給配套的代碼生成工具Database2sharp來完成。
/// <summary> /// DictTypeInfo /// </summary> [SugarTable("TB_DictType")] public class DictTypeInfo { /// <summary> /// 默認構造函數(需要初始化屬性的在此處理) /// </summary> public DictTypeInfo() { this.ID = System.Guid.NewGuid().ToString(); this.LastUpdated = System.DateTime.Now; } #region Property Members [SugarColumn(IsPrimaryKey = true)] public virtual string ID { get; set; } /// <summary> /// 類型名稱 /// </summary> public virtual string Name { get; set; } /// <summary> /// 字典代碼 /// </summary> public virtual string Code { get; set; } /// <summary> /// 備註 /// </summary> public virtual string Remark { get; set; } /// <summary> /// 排序 /// </summary> public virtual string Seq { get; set; } /// <summary> /// 編輯者 /// </summary> public virtual string Editor { get; set; } /// <summary> /// 編輯時間 /// </summary> public virtual DateTime LastUpdated { get; set; } /// <summary> /// 分類:0 客房/1 KTV/2 茶室 /// </summary> public virtual string PID { get; set; } #endregion }
同時為了方便條件的分頁處理,我們定義一個分頁的Dto對象,如下所示。
/// <summary> /// 用於根據條件分頁查詢,DTO對象 /// </summary> public class DictTypePagedDto : PagedAndSortedInputDto, IPagedAndSortedResultRequest { /// <summary> /// 默認構造函數 /// </summary> public DictTypePagedDto() : base() { } /// <summary> /// 參數化構造函數 /// </summary> /// <param name="skip /// ">跳過的數量</param> /// <param name="resultCount">最大結果集數量</param> public DictTypePagedDto(int skipCount, int resultCount) { } /// <summary> /// 使用分頁信息進行初始化SkipCount 和 MaxResultCount /// </summary> /// <param name="pagerInfo">分頁信息</param> public DictTypePagedDto(PagerInfo pagerInfo) : base(pagerInfo) { } #region Property Members /// <summary> /// 不包含的對象的ID,用於在查詢的時候排除對應記錄 /// </summary> public virtual string ExcludeId { get; set; } public virtual string Name { get; set; } public virtual string Code { get; set; } public virtual string Remark { get; set; } public virtual string Seq { get; set; } public virtual string PID { get; set; } /// <summary> /// 創建時間-開始 /// </summary> public DateTime? CreationTimeStart { get; set; } /// <summary> /// 創建時間-結束 /// </summary> public DateTime? CreationTimeEnd { get; set; } #endregion }
同理對於字典項目的實體信息,也是類似的定義方式,如下所示。
/// <summary> /// DictDataInfo /// </summary> [SugarTable("TB_DictData")] public class DictDataInfo { /// <summary> /// 默認構造函數(需要初始化屬性的在此處理) /// </summary> public DictDataInfo() { this.ID = System.Guid.NewGuid().ToString(); this.LastUpdated = System.DateTime.Now; } #region Property Members /// <summary> /// 編號 /// </summary> [SugarColumn(IsPrimaryKey = true)] public virtual string ID { get; set; } /// <summary> /// 字典大類 /// </summary> public virtual string DictType_ID { get; set; } /// <summary> /// 字典名稱 /// </summary> public virtual string Name { get; set; } /// <summary> /// 字典值 /// </summary> public virtual string Value { get; set; } /// <summary> /// 備註 /// </summary> public virtual string Remark { get; set; } /// <summary> /// 排序 /// </summary> public virtual string Seq { get; set; } /// <summary> /// 編輯者 /// </summary> public virtual string Editor { get; set; } /// <summary> /// 編輯時間 /// </summary> public virtual DateTime LastUpdated { get; set; } #endregion }
最終我們定義完成實體信息後,需要集成上篇隨筆提到的數據訪問基類,並重寫一下查詢條件處理,排序的規則信息即可,如下代碼所示。
/// <summary> /// 應用層服務接口實現 /// </summary> public class DictTypeService : MyCrudService<DictTypeInfo, string, DictTypePagedDto> { /// <summary> /// 獲取字段中文別名(用於界面顯示)的字典集合 /// </summary> /// <returns></returns> public override Task<Dictionary<string, string>> GetColumnNameAliasAsync() { var dict = new Dictionary<string, string>(); #region 添加別名解析 dict.Add("ID", "編號"); dict.Add("Name", "類型名稱"); dict.Add("Code", "字典代碼"); dict.Add("Remark", "備註"); dict.Add("Seq", "排序"); dict.Add("Editor", "編輯者"); dict.Add("LastUpdated", "編輯時間"); dict.Add("PID", "父ID"); #endregion return Task.FromResult(dict); } /// <summary> /// 自定義條件處理 /// </summary> /// <param name="input">查詢條件Dto</param> /// <returns></returns> protected override ISugarQueryable<DictTypeInfo> CreateFilteredQueryAsync(DictTypePagedDto input) { var query = base.CreateFilteredQueryAsync(input); query = query .WhereIF(!input.ExcludeId.IsNullOrWhiteSpace(), t => t.ID != input.ExcludeId) //不包含排除ID .WhereIF(!string.IsNullOrEmpty(input.Code), t => t.Code == input.Code) .WhereIF(!string.IsNullOrEmpty(input.PID), t => t.PID == input.PID) .WhereIF(!input.Name.IsNullOrWhiteSpace(), t => t.Name.Contains(input.Name)) //如需要精確匹配則用Equals .WhereIF(!input.Remark.IsNullOrWhiteSpace(), t => t.Remark.Contains(input.Remark)) //如需要精確匹配則用Equals .WhereIF(!input.Seq.IsNullOrWhiteSpace(), t => t.Seq.Contains(input.Seq)) //如需要精確匹配則用Equals //創建日期區間查詢 .WhereIF(input.CreationTimeStart.HasValue, s => s.LastUpdated >= input.CreationTimeStart.Value) .WhereIF(input.CreationTimeEnd.HasValue, s => s.LastUpdated <= input.CreationTimeEnd.Value) ; return query; } /// <summary> /// 自定義排序處理 /// </summary> /// <param name="query">可查詢LINQ</param> /// <param name="input">查詢條件Dto</param> /// <returns></returns> protected override ISugarQueryable<DictTypeInfo> ApplySorting(ISugarQueryable<DictTypeInfo> query, DictTypePagedDto input) { return base.ApplySorting(query, input).OrderBy(s => s.Seq); } }
其中MyCrudService 採用了泛型的定義方式,傳入對應的實體類,主鍵類型,以及排序分頁的對象DTO等,方便基類實現強類型的接口處理。
這個子類我們也可以通過代碼生成的方式實現批量生成即可。
整合到項目裏面,把實體類和數據訪問的服務類區分不同的目錄放置,便於管理即可。
2、Winform界面的開發和調用數據操作處理
上面我們完成了數據庫表的實體類和對應數據訪問服務類的處理後,我們接下來的就是設計Winform界面用來處理相關的數據處理。
我這裡把我的基於微軟企業庫訪問模式的Winform界面部分拷貝過來調整一下,如下界面所示。
查看和編輯字典大類界面
編輯字典項目
對於數據訪問類的調用,我們使用了一個工廠類來創建對應的單例應用,如下獲取字典大類列表。
/// <summary> /// 綁定樹的數據源 /// </summary> private async Task BindTree() { var pageDto = new DictTypePagedDto(); var result = await BLLFactory<DictTypeService>.Instance.GetListAsync(pageDto); if (result != null) { this.tree.DataSource = result.Items; this.tree.ExpandAll(); } }
而但我們單擊某個字典大類的時候,應該列出對應大類下的字典項目,因此獲取字典項目的數據操作如下所示。
/// <summary> /// 獲取數據 /// </summary> /// <returns></returns> private async Task<IPagedResult<DictDataInfo>> GetData(string dictType) { //構建分頁的條件和查詢條件 var pagerDto = new DictDataPagedDto(this.winGridViewPager1.PagerInfo) { DictType_ID = dictType }; var result = await BLLFactory<DictDataService>.Instance.GetListAsync(pagerDto);//new DictDataService().GetListAsync(pagerDto); return result; }
我們這裡使用了分頁查詢的條件DictDataPagedDto,如果是需要獲取全部,我們也可以通過調用GetAllAsync()函數來實現,如下導出全部的時候代碼如下所示。
private async void winGridViewPager1_OnStartExport(object sender, EventArgs e) { if (this.winGridViewPager1.IsExportAllPage) { var result = await BLLFactory<DictDataService>.Instance.GetAllAsync(); this.winGridViewPager1.AllToExport = result.Items; } }
這些處理都是基類預先定義好的API,我們通過子類強類型傳入即可,非常方便,也簡化很多代碼。
同樣,我們可以通過Get接口獲取指定ID的實體信息,如下所示。
if (!string.IsNullOrEmpty(ID)) { var info = await BLLFactory<DictDataService>.Instance.GetAsync(ID); if (info != null) { this.txtName.Text = info.Name; this.txtNote.Text = info.Remark; this.txtSeq.Text = info.Seq; this.txtValue.Text = info.Value; } }
在Winform編輯界面中,我們重寫保存更新的代碼如下所示。
public override async Task<bool> SaveUpdated() { var info = await BLLFactory<DictDataService>.Instance.GetAsync(ID); if (info != null) { SetInfo(info); try { return await BLLFactory<DictDataService>.Instance.UpdateAsync(info); } catch (Exception ex) { LogTextHelper.Error(ex); MessageDxUtil.ShowError(ex.Message); } } return false; }
以上是Winform界面中對常規數據處理接口的調用,這些都是通過強類型實體的方式調用基類函數,非常方便快捷,同時以提供了很好的API統一性實現。
最終界面效果和原先Winform開發框架一樣功能。