基於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開發框架一樣功能。

 

 

 

Tags: