我喜欢在一个项目开发模式成熟的时候,使用代码生成工具Database2Sharp来配套相关的代码生成,对于我介绍的基于SqlSugar的开发框架,从整体架构确定下来后,我就着手为它们量身定做相关的代码开发,这样可以在后续整合项目功能的时候,利用代码生成工具快速的生成所需要模块的骨架代码,然后在这个基础上逐渐增加自定义的内容即可,方便快捷。本篇随笔介绍基于SqlSugar的开发框架,对框架中涉及到的各个分层或者模块代码进行生成的处理。

1、回顾项目的架构和模块内容

在前面几篇随笔中,大概介绍过了基于SqlSugar的开发框架主要的设计模块,场景如下所示。

基础核心数据模块SugarProjectCore,主要就是开发业务所需的数据处理和业务逻辑的项目,为了方便,我们区分Interface、Modal、Service三个目录来放置不同的内容,其中Modal是SqlSugar的映射实体,Interface是定义访问接口,Service是提供具体的数据操作实现。其中Service里面一些框架基类和接口定义,统一也放在公用类库里面。

Winform界面,我们可以采用基于.net Framework开发或者.net core6进行开发均可,因为我们的SugarProjectCore项目是采用.net Standard模式开发,兼容两者。

这里以权限模块来进行演示整合使用,我在构建代码生成工具代码模板的时候,反复利用项目中测试没问题的项目代码指导具体的模板编写,这样编写出来的模板就会完美符合实际的项目需要了。

在项目代码及模板完成后,利用代码生成工具快速生成代码,相互促进情况下,也完成了Winform项目的界面代码生成,生成包括普通的列表界面,以及主从表Winform界面代码生成。

最后权限系统的Winform项目如下所示。

在前面随笔《基于SqlSugar的开发框架循序渐进介绍(2)-- 基于中间表的查询处理》中介绍了基础功能的一些处理,其中也介绍到了Winform界面端的界面效果,这个以SqlSugar底层处理,最终把权限、字典等模块整合到一起,完成一个项目开发所需要的框架结构内容。整个系统包括用户管理、组织机构管理、角色管理、功能权限管理、菜单管理、字段权限管理、黑白名单、操作日志、字典管理、客户信息等模块内容。

在代码生成工具中,我们整合了基于SqlSugar的开发框架的项目代码生成,包括框架基础的代码生成,以及Winform界面代码生成两个部分,框架项目及Winform界面效果如上图所示。

2、整合代码生成工具Database2Sharp进行SqlSugar框架代码生成

前面随笔介绍过基于SqlSugar核心Core项目的组成。

基础核心数据模块SugarProjectCore,主要就是开发业务所需的数据处理和业务逻辑的项目,为了方便,我们区分Interface、Modal、Service三个目录来放置不同的内容,其中Modal是SqlSugar的映射实体,Interface是定义访问接口,Service是提供具体的数据操作实现。

对于Modal层的类代码生成,常规的普通表(非中间表),我们根据项目所需要,生成如下代码。目的是利用它定义好对应的主键Id,并通过接口约束实体类。

    /// <summary>
/// 客户信息
/// 继承自Entity,拥有Id主键属性
/// </summary>
[SugarTable("T_Customer")]
public class CustomerInfo : Entity<string>

而对于中间表,我们不要它的继承继承关系。

    /// <summary>
/// 客户信息
/// 继承自Entity,拥有Id主键属性
/// </summary>
[SugarTable("T_Customer")]
public class CustomerInfo : Entity<string>

只需要简单的标注好SugarTable属性,让他可以和其他业务表进行关联查询即可。

        /// <summary>
/// 根据用户ID获取对应的角色列表
/// </summary>
/// <param name="userID">用户ID</param>
/// <returns></returns>
private async Task<List<RoleInfo>> GetByUser(int userID)
{
var query = this.Client.Queryable<RoleInfo, User_RoleInfo>(
(t, m) => t.Id == m.Role_ID && m.User_ID == userID)
.Select(t => t); //联合条件获取对象 query = query.OrderBy(t => t.CreateTime);//排序
var list = await query.ToListAsync();//获取列表
return list;
}

对于接口层的类,我们只需要按固定的继承关系处理好,以及类的名称变化即可。

    /// <summary>
/// 系统用户信息,应用层服务接口定义
/// </summary>
public interface IUserService : IMyCrudService<UserInfo, int, UserPagedDto>, ITransientDependency
{
}

其中 IMyCrudService 是我们定义的基类接口,保存常规的增删改查等的处理基类,通过传入泛型进行约束接口参数类型和返回值。

基类接口尽可能满足实际项目接口所需,这样可以减少子类的代码编写,以及获得统一调用基类函数的便利。

对于中间表,我们除了生成实体类外,不需要生成其他接口和接口实现层,因为我们不单独调用它们。

对于具体业务对象对应的接口实现,我们除了确定它的继承关系外,我们还会重写它们的一些基类函数,从而实现更加精准的处理。

接口实现类的定义如下所示。

    /// <summary>
/// 应用层服务接口实现
/// </summary>
public class CustomerService : MyCrudService<CustomerInfo, string, CustomerPagedDto>, ICustomerService
{ }

一般情况下,我们至少需要在子类重写 CreateFilteredQueryAsync 和 ApplyDefaultSorting 两个函数,前者是条件的查询处理,后者是默认的排序处理操作。

        /// <summary>
/// 自定义条件处理
/// </summary>
/// <param name="input">查询条件Dto</param>
/// <returns></returns>
protected override ISugarQueryable<CustomerInfo> CreateFilteredQueryAsync(CustomerPagedDto input)
{
var query = base.CreateFilteredQueryAsync(input); query = query
.WhereIF(!input.ExcludeId.IsNullOrWhiteSpace(), t => t.Id != input.ExcludeId) //不包含排除ID
.WhereIF(!input.Name.IsNullOrWhiteSpace(), t => t.Name.Contains(input.Name)) //如需要精确匹配则用Equals
//年龄区间查询
.WhereIF(input.AgeStart.HasValue, s => s.Age >= input.AgeStart.Value)
.WhereIF(input.AgeEnd.HasValue, s => s.Age <= input.AgeEnd.Value) //创建日期区间查询
.WhereIF(input.CreateTimeStart.HasValue, s => s.CreateTime >= input.CreateTimeStart.Value)
.WhereIF(input.CreateTimeEnd.HasValue, s => s.CreateTime <= input.CreateTimeEnd.Value)
; return query;
} /// <summary>
/// 自定义排序处理
/// </summary>
/// <param name="query">可查询LINQ</param>
/// <returns></returns>
protected override ISugarQueryable<CustomerInfo> ApplyDefaultSorting(ISugarQueryable<CustomerInfo> query)
{
return query.OrderBy(t => t.CreateTime, OrderByType.Desc); //如果先按第一个字段排序,然后再按第二字段排序,示例代码
//return base.ApplySorting(query, input).OrderBy(s=>s.Customer_ID).OrderBy(s => s.Seq);
}

根据这些规则,编写我们所需的模板代码,让我们选择的数据库表名称、注释,以及表字段的名称、类型、注释,外键主键关系等信息为我们模板所用。

如下所示代码是NVelocity模板代码,用于生成上面的条件查询处理的,可以稍作了解。

        /// <summary>
/// 自定义条件处理
/// </summary>
/// <param name="input">查询条件Dto</param>
/// <returns></returns>
protected override ISugarQueryable<${ClassName}Info> CreateFilteredQueryAsync(${ClassName}PagedDto input)
{
var query = base.CreateFilteredQueryAsync(input);
query = query
#if(${PrimaryKeyNetType}=="string")
.WhereIF(!input.ExcludeId.IsNullOrWhiteSpace(), t=>t.Id != input.ExcludeId) //不包含排除ID
#else
.WhereIF(input.ExcludeId.HasValue, t=>t.Id != input.ExcludeId) //不包含排除ID
#end
#foreach($EntityProperty in $EntityPropertyList)
#if(${EntityProperty.ColumnInfo.IsForeignKey} || ${EntityProperty.PropertyName} == "Status" || ${EntityProperty.PropertyName} == "State" || ${EntityProperty.PropertyName} == "PID" || ${EntityProperty.PropertyName} == "Deleted")
.WhereIF(#if(${EntityProperty.ColumnInfo.IsNumeric})input.${EntityProperty.PropertyName}.HasValue#else!input.${EntityProperty.PropertyName}.IsNullOrWhiteSpace()#end, s => s.${EntityProperty.PropertyName} == input.${EntityProperty.PropertyName})
#elseif(${EntityProperty.ColumnInfo.IsDateTime} || ${EntityProperty.ColumnInfo.IsNumeric})
//${EntityProperty.Description}区间查询
.WhereIF(input.${EntityProperty.PropertyName}Start.HasValue, s => s.${EntityProperty.PropertyName} >= input.${EntityProperty.PropertyName}Start.Value)
.WhereIF(input.${EntityProperty.PropertyName}End.HasValue, s => s.${EntityProperty.PropertyName} <= input.${EntityProperty.PropertyName}End.Value)
#elseif(${EntityProperty.ColumnInfo.NetType.Alias.ToLower()} != "byte[]" && ${EntityProperty.ColumnInfo.Name.Name.ToString()} != "AttachGUID")
.WhereIF(#if(${EntityProperty.NetType.EndsWith("?")})input.${EntityProperty.PropertyName}.HasValue, t => t.${EntityProperty.PropertyName} == input.${EntityProperty.PropertyName}#else!input.${EntityProperty.PropertyName}.IsNullOrWhiteSpace(), t => t.${EntityProperty.PropertyName}.Contains(input.${EntityProperty.PropertyName})#end) //如需要精确匹配则用Equals
#end ##endif
#end #if(${HasCreationTime})
//创建日期区间查询
.WhereIF(input.CreationTimeStart.HasValue, s => s.CreationTime >= input.CreationTimeStart.Value)
.WhereIF(input.CreationTimeEnd.HasValue, s => s.CreationTime <= input.CreationTimeEnd.Value)
#else
//创建日期区间查询(参考)
//.WhereIF(input.CreationTimeStart.HasValue, s => s.CreationTime >= input.CreationTimeStart.Value)
//.WhereIF(input.CreationTimeEnd.HasValue, s => s.CreationTime <= input.CreationTimeEnd.Value)
#end;
return query;
}

当我们完成所需的模板代码开发后,就在代码生成工具主体界面中整合相关的生成功能菜单,界面效果如下所示。

通过菜单选择【SqlSugar框架代码生成】,进一步选择数据库中的表进行生成,一步步处理即可,最后列出所选数据库表,并确认生成操作,即可生成SqlSugar框架核心项目的代码,如下图所示。

选择表进行生成后,生成的实体模型类如下所示,包括生成了中间表的实体类。

而接口实现则是根据具体的业务对象规则进行生成。

3、SqlSugar项目中Winform界面的生成

Winform界面包括普通列表/编辑界面处理,以及主从表界面处理两个部分,如下图所示。

生成的简单业务表界面,包括分页列表展示界面,在列表界面中整合查看、编辑、新增、删除、导入、导出、查询/高级查询等功能,整合的编辑界面也是依据数据库表的信息进行生成的。

列表界面和编辑界面效果如下所示。

而主从表界面生成的效果如下所示。

我们看看生成的Winform列表界面代码,如下所示。

另外我们把一些常用的处理逻辑放在函数中统一处理,如AddData、EditData、DeleteData、BindData、GetData、ImportData、ExportData等等,如下所示。

在获取数据的时候,我们根据用户的条件,构建一个分页查询对象传递,调用接口获得数据后,进行分页控件的绑定处理即可。

        /// <summary>
/// 获取数据
/// </summary>
/// <returns></returns>
private async Task<IPagedResult<CustomerInfo>> GetData()
{
CustomerPagedDto pagerDto = null;
if (advanceCondition != null)
{
//如果有高级查询,那么根据输入信息构建查询条件
pagerDto = new CustomerPagedDto(this.winGridViewPager1.PagerInfo);
pagerDto = dlg.GetPagedResult(pagerDto);
}
else
{
//构建分页的条件和查询条件
pagerDto = new CustomerPagedDto(this.winGridViewPager1.PagerInfo)
{
//添加所需条件
Name = this.txtName.Text.Trim(),
}; //日期和数值范围定义
//年龄,需在CustomerPagedDto中添加 int? 类型字段AgeStart和AgeEnd
var Age = new ValueRange<int?>(this.txtAge1.Text, this.txtAge2.Text); //数值类型
pagerDto.AgeStart = Age.Start;
pagerDto.AgeEnd = Age.End; //创建时间,需在CustomerPagedDto中添加 DateTime? 类型字段CreationTimeStart和CreationTimeEnd
var CreationTime = new TimeRange(this.txtCreationTime1.Text, this.txtCreationTime2.Text); //日期类型
pagerDto.CreateTimeStart = CreationTime.Start;
pagerDto.CreateTimeEnd = CreationTime.End;
} var result = await BLLFactory<CustomerService>.Instance.GetListAsync(pagerDto);
return result;
}

如果是高级查询,我们则是根据传入分页查询对象的属性在高级查询对话框中进行赋值,然后获得对象后进行查询获得记录的。

在代码生成工具中,我们根据实际项目的代码,定义好对应的模板文件,如下所示。

最后在生成代码的时候,整合这些NVelocity的模板文件,根据表对象的信息,生成对应的文件供我们开发使用即可。

            #region Model 实体部分

            string entityTemplateFile = ProjectPath + "Templates/Entity.cs.vm";
var entityAdapter = new SugarEntityAdapter(databaseInfo, selectedTableNames, entityTemplateFile);
entityAdapter.DirectoryOfOutput = mainSetting.RootNameSpace + "/Core/Modal";
entityAdapter.Execute(); #endregion #region Interface部分和Application部分 var appInterface = new SugarServiceInterfaceAdapter(databaseInfo, selectedTableNames, ProjectPath + "Templates/IService.cs.vm", databaseTypeName);
appInterface.DirectoryOfOutput = mainSetting.RootNameSpace + "/Core/Interface";
appInterface.Execute(); var appService = new SugarServiceAdapter(databaseInfo, selectedTableNames, ProjectPath + "Templates/Service.cs.vm", databaseTypeName);
appService.DirectoryOfOutput = mainSetting.RootNameSpace + "/Core/Service";
appService.Execute(); #endregion #region Web API Controller 部分 var controller = new SugarControllerAdapter(databaseInfo, selectedTableNames, ProjectPath + "Templates/Controller.cs.vm", databaseTypeName);
controller.DirectoryOfOutput = mainSetting.RootNameSpace + "/Controller";
controller.Execute(); #endregion

系列文章:

基于SqlSugar的开发框架的循序渐进介绍(1)--框架基础类的设计和使用

基于SqlSugar的开发框架循序渐进介绍(2)-- 基于中间表的查询处理

基于SqlSugar的开发框架循序渐进介绍(3)-- 实现代码生成工具Database2Sharp的整合开发

基于SqlSugar的开发框架循序渐进介绍(3)-- 实现代码生成工具Database2Sharp的整合开发的更多相关文章

  1. 基于SqlSugar的开发框架循序渐进介绍(4)-- 在数据访问基类中对GUID主键进行自动赋值处理

    我们在设计数据库表的时候,往往为了方便,主键ID一般采用字符串类型或者GUID类型,这样对于数据库表记录的迁移非常方便,而且有时候可以在处理关联记录的时候,提前对应的ID值.但有时候进行数据记录插入的 ...

  2. 基于SqlSugar的开发框架循序渐进介绍(5)-- 在服务层使用接口注入方式实现IOC控制反转

    在前面随笔,我们介绍过这个基于SqlSugar的开发框架,我们区分Interface.Modal.Service三个目录来放置不同的内容,其中Modal是SqlSugar的映射实体,Interface ...

  3. 基于SqlSugar的开发框架循序渐进介绍(6)-- 在基类接口中注入用户身份信息接口

    在基于SqlSugar的开发框架中,我们设计了一些系统服务层的基类,在基类中会有很多涉及到相关的数据处理操作的,如果需要跟踪具体是那个用户进行操作的,那么就需要获得当前用户的身份信息,包括在Web A ...

  4. 基于SqlSugar的开发框架循序渐进介绍(8)-- 在基类函数封装实现用户操作日志记录

    在我们对数据进行重要修改调整的时候,往往需要跟踪记录好用户操作日志.一般来说,如对重要表记录的插入.修改.删除都需要记录下来,由于用户操作日志会带来一定的额外消耗,因此我们通过配置的方式来决定记录那些 ...

  5. 基于SqlSugar的开发框架循序渐进介绍(12)-- 拆分页面模块内容为组件,实现分而治之的处理

    在早期的随笔就介绍过,把常规页面的内容拆分为几个不同的组件,如普通的页面,包括列表查询.详细资料查看.新增资料.编辑资料.导入资料等页面场景,这些内容相对比较独立,而有一定的代码量,本篇随笔介绍基于V ...

  6. 基于SqlSugar的开发框架循序渐进介绍(13)-- 基于ElementPlus的上传组件进行封装,便于项目使用

    在我们实际项目开发过程中,往往需要根据实际情况,对组件进行封装,以更简便的在界面代码中使用,在实际的前端应用中,适当的组件封装,可以减少很多重复的界面代码,并且能够非常简便的使用,本篇随笔介绍基于El ...

  7. 基于SqlSugar的开发框架循序渐进介绍(14)-- 基于Vue3+TypeScript的全局对象的注入和使用

    刚完成一些前端项目的开发,腾出精力来总结一些前端开发的技术点,以及继续完善基于SqlSugar的开发框架循序渐进介绍的系列文章,本篇随笔主要介绍一下基于Vue3+TypeScript的全局对象的注入和 ...

  8. 基于SqlSugar的开发框架循序渐进介绍(15)-- 整合代码生成工具进行前端界面的生成

    在前面随笔<基于SqlSugar的开发框架循序渐进介绍(12)-- 拆分页面模块内容为组件,实现分而治之的处理>中我们已经介绍过,对于相关的业务表的界面代码,我们已经尽可能把不同的业务逻辑 ...

  9. 基于SqlSugar的开发框架循序渐进介绍(17)-- 基于CSRedis实现缓存的处理

    在一个应用系统的开发框架中,往往很多地方需要用到缓存的处理,有些地方是为了便于记录用户的数据,有些地方是为了提高系统的响应速度,如有时候我们在发送一个短信验证码的时候,可以在缓存中设置几分钟的过期时间 ...

随机推荐

  1. SaltStack项目实战(二)

    架构图: 配置思路 (1).系统初始化 Base环境下存放所有系统都要执行的状态,调整内核参数,dns,装zabbix-agent等 (2).功能模块(如:上面的haproxy) 如上面的haprox ...

  2. STM32 标准库

    CMSIS 标准及库层次关系 因为基于Cortex 系列芯片采用的内核都是相同的,区别主要为核外的片上外设的差异,这些差异却导致软件在同内核,不同外设的芯片上移植困难.为了解决不同的芯片厂商生产的Co ...

  3. 摩拜单车微信小程序开发技术总结

    前言 摩拜单车小程序已于微信小程序上线第一天正式发布,刷爆微博媒体朋友圈.本文主要讲讲技术方向的总结,在段时间的开发周期内内如何一步步从学习到进阶. 思维转变 微信小程序没有HTML的常用标签,而是类 ...

  4. 前端面试题整理——手写bind函数

    var arr = [1,2,3,4,5] console.log(arr.slice(1,4)) console.log(arr) Function.prototype.bind1 = functi ...

  5. python-使用函数求余弦函数的近似值

    本题要求实现一个函数,用下列公式求cos(x)近似值,精确到最后一项的绝对值小于eps(绝对值小于eps的项不要加): cos(x)=0!x0​−2!x2​+4!x4​−6!x6​+... 函数接口定 ...

  6. DOM节点详解

    @ 目录 学习总结 1. 什么是 DOM 2. HTMLDOM 3. 元素获取 元素获取方式: 元素节点的属性操作 4. Node 对象的属性和方法 常用属性 常用方法 5. 事件处理 事件驱动编程 ...

  7. JavaScript实现有农历和节气节假日的日历

    运行效果: 源代码: 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content ...

  8. 彻底理解synchronized

    1. synchronized简介 在学习知识前,我们先来看一个现象: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public ...

  9. vue日历(纯 js,没用任何插件和组件)

    效果图: 代码:   <template> <div class="calender"> <div class="top"> ...

  10. CommonsCollection4反序列化链学习

    CommonsCollection4 1.前置知识 由于cc4没有新的知识点,主要是用cc2,然后稍微cc3结合了,所以我们可以看ysoserial源码,自己尝试构造一下,把cc2通过获取Invoke ...