在上篇随笔《基于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开发框架一样功能。
主要研究技术:代码生成工具、会员管理系统、客户关系管理软件、病人资料管理软件、Visio二次开发、酒店管理系统、仓库管理系统等共享软件开发
专注于Winform开发框架/混合式开发框架、Web开发框架、Bootstrap开发框架、微信门户开发框架的研究及应用。
转载请注明出处:
撰写人:伍华聪 http://www.iqidi.com