基于SqlSugar的开发框架循序渐进介绍(2)-- 基于中间表的查询处理
在前面介绍的SqlSugar的相关查询处理操作中,我们主要以单表的方式生成相关的实体类,并在查询的时候,对单表的字段进行条件的对比处理,从而返回对应的数据记录。本篇随笔介绍在一些外键或者中间表的处理中,如何遍历查询并获得所需的记录操作。
1、回顾单表的操作查询
我在《基于SqlSugar的开发框架的循序渐进介绍(1)--框架基础类的设计和使用》中介绍过的Customer表信息,就是一个单表的处理。
例如,我们对于一个简单的客户信息表,如下所示。
生成对应的实体对象CustomerInfo外,同时生成 CustomerPagedDto 的分页查询条件对象。
在继承基类后
/// <summary>
/// 应用层服务接口实现
/// </summary>
public class CustomerService : MyCrudService<CustomerInfo, string, CustomerPagedDto>, ICustomerService
{
....
}
并重写 CreateFilteredQueryAsync 函数,从而实现了条件的精确查询处理。
/// <summary>
/// 应用层服务接口实现
/// </summary>
public class CustomerService : MyCrudService<CustomerInfo, string, CustomerPagedDto>, ICustomerService
{
/// <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;
}
在表的对应实体信息没有其他表关联的时候,我们直接通过SqlSugar的基础接口返回对象列表即可。
通过 CreateFilteredQueryAsync 的精确条件处理,我们就可以明确实体类的查询条件处理,因此对于CustomerPagedDto来说,就是可以有客户端传入,服务后端的基类进行处理了。
如基类的分页条件查询函数GetListAsync就是根据这个来处理的,它的实现代码如下所示。
/// <summary>
/// 根据条件获取列表
/// </summary>
/// <param name="input">分页查询条件</param>
/// <returns></returns>
public virtual async Task<PagedResultDto<TEntity>> GetListAsync(TGetListInput input)
{
var query = CreateFilteredQueryAsync(input);
var totalCount = await query.CountAsync(); query = ApplySorting(query, input);
query = ApplyPaging(query, input); var list = await query.ToListAsync(); return new PagedResultDto<TEntity>(
totalCount,
list
);
}
也就是说只要继承了 CustomerService ,我们默认调用基类的 GetListAsync 就可以返回对应的列表记录了。
如在Web API的控制器中调用获取记录返回,调用处理的代码如下所示。
/// <summary>
/// 获取所有记录
/// </summary>
[HttpGet]
[Route("all")]
[HttpGet]public virtual async Task<ListResultDto<TEntity>> GetAllAsync()
{
//检查用户是否有权限,否则抛出MyDenyAccessException异常
base.CheckAuthorized(AuthorizeKey.ListKey); return await _service.GetAllAsync();
}
而对于Winform的调用,我们这里首先利用代码生成工具生成对应的界面和代码
查看其调用的界面代码
而其中GetData中的函数部分内容如下所示。
/// <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;
}
列表界面效果如下所示。
2、基于中间表的查询处理
前面的查询处理,主要就是针对没有任何关系的表实体对象的返回处理,但往往我们开发的时候,会涉及到很多相关的表,单独的表相对来说还是比较少,因此对表的关系遍历处理和中间表的关系转换,就需要在数据操作的时候考虑的了。
例如对于字典大类和字典项目的关系,如下所示。
以及在权限管理系统模块中,用户、角色、机构、权限等存在着很多中间表的关系,如下所示。
如对于字典表关系处理,我们采用Queryable<DictDataInfo, DictTypeInfo>的查询处理方式,可以联合两个表对象实体进行联合查询,如下代码所示。
/// <summary>
/// 根据字典类型名称获取所有该类型的字典列表集合(Key为名称,Value为值)
/// </summary>
/// <param name="dictTypeName">字典类型名称</param>
/// <returns></returns>
public async Task<Dictionary<string, string>> GetDictByDictType(string dictTypeName)
{
var query = this.Client.Queryable<DictDataInfo, DictTypeInfo>(
(d, t) => d.DictType_ID == t.Id && t.Name == dictTypeName)
.Select(d => d); //联合条件获取对象 query = query.OrderBy(d => d.DictType_ID).OrderBy(d => d.Seq);//排序
var list = await query.ToListAsync();//获取列表 var dict = new Dictionary<string, string>();
foreach (var info in list)
{
if (!dict.ContainsKey(info.Name))
{
dict.Add(info.Name, info.Value);
}
}
return dict;
}
其中的Client对象是DbContext对象实例的Client属性,如下图所示。
这个对象是在DbContext对象中构建的,如下所示。
this.Client = new SqlSugarScope(new ConnectionConfig()
{
DbType = this.DbType,
ConnectionString = this.ConnectionString,
InitKeyType = InitKeyType.Attribute,
IsAutoCloseConnection = true, //是否自动关闭连接
AopEvents = new AopEvents
{
OnLogExecuting = (sql, p) =>
{
//Log.Information(sql);
//Log.Information(string.Join(",", p?.Select(it => it.ParameterName + ":" + it.Value)));
}
}
});
我们查看Queryable,可以看到这个SqlSugar基类函数 Queryable 提供了很多重载函数,也就是它们可以提供更多的表对象进行联合查询的,如下所示。
前面介绍的是外键的一对多的关系查询,通过两个对象之间进行的关系连接,从而实现另一个对象属性的对比查询操作的。
对于中间表的处理,也是类似的情况,我们通过对比中间表的属性,从而实现条件的过滤处理。如下是对于角色中相关关系的中间表查询。
/// <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>
/// <param name="ouID">机构的ID</param>
/// <returns></returns>
public async Task<List<RoleInfo>> GetRolesByOu(int ouID)
{
var query = this.Client.Queryable<RoleInfo, OU_RoleInfo>(
(t, m) => t.Id == m.Role_ID && m.Ou_ID == ouID)
.Select(t => t); //联合条件获取对象 query = query.OrderBy(t => t.CreateTime);//排序
var list = await query.ToListAsync();//获取列表
return list;
}
通过联合查询中间表对象信息,可以对它的字段属性进行条件联合,从而获得所需的记录。
这里User_RoleInfo和Ou_RoleInfo表也是根据中间表的属性生成的,不过它们在业务层并没有任何关联操作,也不需要生成对应的Service层,因此只需要生成相关的Model类实体即可。
/// <summary>
/// 用户角色关联
/// </summary>
[SugarTable("T_ACL_User_Role")]
public class User_RoleInfo
{
/// <summary>
/// 用户ID
/// </summary>
[Required]
public virtual int User_ID { get; set; } /// <summary>
/// 角色ID
/// </summary>
[Required]
public virtual int Role_ID { get; set; } }
/// <summary>
/// 机构角色关联
/// </summary>
[SugarTable("T_ACL_OU_Role")]
public class OU_RoleInfo
{
/// <summary>
/// 机构ID
/// </summary>
[Required]
public virtual int Ou_ID { get; set; } /// <summary>
/// 角色ID
/// </summary>
[Required]
public virtual int Role_ID { get; set; }
}
可以看到这两个实体不同于其他实体,它们没有基类继承关系,而一般标准的实体是有的。
/// <summary>
/// 角色信息
/// </summary>
[SugarTable("T_ACL_Role")]
public class RoleInfo : Entity<int> { } /// <summary>
/// 功能菜单
/// </summary>
[SugarTable("T_ACL_Menu")]
public class MenuInfo : Entity<string> { }
所以我们就不需要构建它们的Service层来处理数据,它的存在合理性只是在于能够和其他实体对象进行表的联合查询处理而且。
最后贴上一个整合SqlSugar处理而完成的系统基础框架的Winform端界面,其中包括用户、组织机构、角色管理、权限管理、菜单管理、日志、字典、客户信息等业务表的处理。
以证所言非虚。
基于SqlSugar的开发框架循序渐进介绍(2)-- 基于中间表的查询处理的更多相关文章
- 基于SqlSugar的开发框架循序渐进介绍(3)-- 实现代码生成工具Database2Sharp的整合开发
我喜欢在一个项目开发模式成熟的时候,使用代码生成工具Database2Sharp来配套相关的代码生成,对于我介绍的基于SqlSugar的开发框架,从整体架构确定下来后,我就着手为它们量身定做相关的代码 ...
- 基于SqlSugar的开发框架循序渐进介绍(4)-- 在数据访问基类中对GUID主键进行自动赋值处理
我们在设计数据库表的时候,往往为了方便,主键ID一般采用字符串类型或者GUID类型,这样对于数据库表记录的迁移非常方便,而且有时候可以在处理关联记录的时候,提前对应的ID值.但有时候进行数据记录插入的 ...
- 基于SqlSugar的开发框架循序渐进介绍(5)-- 在服务层使用接口注入方式实现IOC控制反转
在前面随笔,我们介绍过这个基于SqlSugar的开发框架,我们区分Interface.Modal.Service三个目录来放置不同的内容,其中Modal是SqlSugar的映射实体,Interface ...
- 基于SqlSugar的开发框架循序渐进介绍(6)-- 在基类接口中注入用户身份信息接口
在基于SqlSugar的开发框架中,我们设计了一些系统服务层的基类,在基类中会有很多涉及到相关的数据处理操作的,如果需要跟踪具体是那个用户进行操作的,那么就需要获得当前用户的身份信息,包括在Web A ...
- 基于SqlSugar的开发框架循序渐进介绍(8)-- 在基类函数封装实现用户操作日志记录
在我们对数据进行重要修改调整的时候,往往需要跟踪记录好用户操作日志.一般来说,如对重要表记录的插入.修改.删除都需要记录下来,由于用户操作日志会带来一定的额外消耗,因此我们通过配置的方式来决定记录那些 ...
- 基于SqlSugar的开发框架循序渐进介绍(12)-- 拆分页面模块内容为组件,实现分而治之的处理
在早期的随笔就介绍过,把常规页面的内容拆分为几个不同的组件,如普通的页面,包括列表查询.详细资料查看.新增资料.编辑资料.导入资料等页面场景,这些内容相对比较独立,而有一定的代码量,本篇随笔介绍基于V ...
- 基于SqlSugar的开发框架循序渐进介绍(13)-- 基于ElementPlus的上传组件进行封装,便于项目使用
在我们实际项目开发过程中,往往需要根据实际情况,对组件进行封装,以更简便的在界面代码中使用,在实际的前端应用中,适当的组件封装,可以减少很多重复的界面代码,并且能够非常简便的使用,本篇随笔介绍基于El ...
- 基于SqlSugar的开发框架循序渐进介绍(14)-- 基于Vue3+TypeScript的全局对象的注入和使用
刚完成一些前端项目的开发,腾出精力来总结一些前端开发的技术点,以及继续完善基于SqlSugar的开发框架循序渐进介绍的系列文章,本篇随笔主要介绍一下基于Vue3+TypeScript的全局对象的注入和 ...
- 基于SqlSugar的开发框架循序渐进介绍(15)-- 整合代码生成工具进行前端界面的生成
在前面随笔<基于SqlSugar的开发框架循序渐进介绍(12)-- 拆分页面模块内容为组件,实现分而治之的处理>中我们已经介绍过,对于相关的业务表的界面代码,我们已经尽可能把不同的业务逻辑 ...
- 基于SqlSugar的开发框架循序渐进介绍(17)-- 基于CSRedis实现缓存的处理
在一个应用系统的开发框架中,往往很多地方需要用到缓存的处理,有些地方是为了便于记录用户的数据,有些地方是为了提高系统的响应速度,如有时候我们在发送一个短信验证码的时候,可以在缓存中设置几分钟的过期时间 ...
随机推荐
- element-ui 无法对绑定表单的对象中的对象属性进行验证
<el-form-item label="类型" :label-width="formLabelWidth" prop="typeId" ...
- Effective Java —— 覆盖equals时总要覆盖hashCode
本文参考 本篇文章参考自<Effective Java>第三版第十一条"Always override hashCode when you override equals&quo ...
- 使用滑模控制对sin(t)曲线追踪
结合:[Matlab]简单的滑模控制程序及Simulink仿真本片文章观看,此篇文章是在这篇文章的基础上进行修改的 输出u的推导过程 如果不明白控制量输出u的推到过成请看:[控制理论]滑模控制最强解析 ...
- IMWEB 前端面试题汇总
1.什么是盒子模型? CSS中的思维模型,每一个元素都包含margin,padding,boder,content区域,占一个盒子形状,整体称为盒模型. 2.简述一下src与href的区别? Href ...
- Unknown host mirrors.opencas.cn You may need to adjust the proxy settings in Gradle 报错及解决办法
亲测Unknown host mirrors.opencas.cn You may need to adjust the proxy settings in Gradle 解决办法 - 程序员大本营 ...
- java基础-多线程线程池
线程池 * 程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互.而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池.线程池里的每一个线程代 ...
- Python入门-程序结构扩展
deque双端队列 #双端队列,就是生产消费者模式,依赖collections模块 from collections import deque def main(): info = deque((&q ...
- 帝国CMS实现栏目批量修改:是否生成
帝国cms实现栏目批量修改 是否生成:不生成栏目页, 不生成内容页, 不生成JS调用, 标签不调用的修改方本文关键字词:栏目批量设置第一步./e/admin/SetMoreClass.php中查找: ...
- &&与&,||与| 区别
1. &&和&都是表示与,区别是&&只要第一个条件不满足,后面条件就不再判断. 而&要对所有的条件都进行判断. public class Test { ...
- 『忘了再学』Shell基础 — 9、Bash中的特殊符号(一)
目录 1.双单引号 2.双引号 3.$符号 4.反引号 5.$()符号 6.#符号 7.\符号 1.双单引号 '':单引号.在单引号中所有的特殊符号,如$和"`"(反引号)都没有特 ...