wuhuacong(伍华聪)的专栏

wuhuacong(伍华聪)的专栏

在上篇随笔《基于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的数据库访问处理的封装,支持多数据库并使之适应于实际业务开发中(2)-LMLPHP

我们生成器对应的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等,方便基类实现强类型的接口处理。

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

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

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

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

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

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

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

 查看和编辑字典大类界面

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

  编辑字典项目

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

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

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

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

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

基于SqlSugar的数据库访问处理的封装,支持多数据库并使之适应于实际业务开发中(2)-LMLPHP主要研究技术:代码生成工具、会员管理系统、客户关系管理软件、病人资料管理软件、Visio二次开发、酒店管理系统、仓库管理系统等共享软件开发
专注于Winform开发框架/混合式开发框架Web开发框架Bootstrap开发框架微信门户开发框架的研究及应用
  转载请注明出处:
基于SqlSugar的数据库访问处理的封装,支持多数据库并使之适应于实际业务开发中(2)-LMLPHP撰写人:伍华聪  http://www.iqidi.com 
    
03-11 12:58