2

基于SqlSugar的数据库访问处理的封装,支持多数据库并使之适应于实际业务开发中(2)

 2 years ago
source link: https://www.cnblogs.com/wuhuacong/p/15992688.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在上篇随笔《基于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等,方便基类实现强类型的接口处理。

这个子类我们也可以通过代码生成的方式实现批量生成即可。

整合到项目里面,把实体类和数据访问的服务类区分不同的目录放置,便于管理即可。

8867-20220311102931677-1268008683.png

2、Winform界面的开发和调用数据操作处理

上面我们完成了数据库表的实体类和对应数据访问服务类的处理后,我们接下来的就是设计Winform界面用来处理相关的数据处理。

我这里把我的基于微软企业库访问模式的Winform界面部分拷贝过来调整一下,如下界面所示。

8867-20220311110636615-1869814847.png

 查看和编辑字典大类界面

8867-20220311110754923-1968267752.png

  编辑字典项目

8867-20220311110836271-922464147.png

 对于数据访问类的调用,我们使用了一个工厂类来创建对应的单例应用,如下获取字典大类列表。

        /// <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开发框架一样功能。

8867-20220311113424305-1932190480.png

 

8867-20220311113455887-1463596119.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK