热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

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

在实际项目开发中,我们可能会碰到各种各样的项目环境,有些项目需要一个大而全的整体框架来支撑开发,有些中小项目这需要一些简单便捷的系统框架灵活开发。目前大型一点的框架,可以采用ABP

在实际项目开发中,我们可能会碰到各种各样的项目环境,有些项目需要一个大而全的整体框架来支撑开发,有些中小项目这需要一些简单便捷的系统框架灵活开发。目前大型一点的框架,可以采用ABP或者ABP VNext的框架,两者整体思路和基础设计类似,不过ABP侧重于一个独立完整的项目框架,开发的时候统一整合处理;而ABP VNext则是以微服务架构为基础,各个模块独立开发,既可以整合在一个项目中,也可以以微服务进行单独发布,并统一通过网关处理进行交流。不管ABP或者ABP VNext框架,都集合了.NET CORE领域众多技术为一体,并且基础类设计上,错综复杂,关系较多,因此开发学习有一定的门槛,中小型项目应用起来有一定的费劲之处。本系列随笔介绍底层利用SqlSugar来做ORM数据访问模块,设计一个简单便捷一点的框架,本篇从基础开始介绍一些框架内容,参照一些ABP/ABP VNext中的一些类库处理,来承载类似条件分页信息,查询条件处理等处理细节。


1、基于SqlSugar开发框架的架构设计

主要的设计模块场景如下所示。

 

为了避免像ABP VNext框架那样分散几十个项目,我们尽可能聚合内容放在一个项目里面。

1)其中一些常用的类库,以及SqlSugar框架的基类放在框架公用模块里面。

2)Winform开发相关的基础界面以及通用组件内容,放在基础Winform界面库BaseUIDx项目中。

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

4)Winform应用模块,主要就是针对业务开发的WInform界面应用,而WInform开发为了方便,也会将一些基础组件和基类放在了BaseUIDx的Winform专用的界面库里面。

5)WebAPI项目采用基于.net Core6的项目开发,通过调用SugarProjectCore实现相关控制器API的发布,并整合Swagger发布接口,供其他前端界面应用进行调用。

6)纯前端通过API进行调用Web API的接口,纯前端模块可以包含Vue3&Element项目,以及基于EelectronJS应用,发布跨平台的基于浏览器的应用界面,以及其他App或者小程序整合Web API进行业务数据的处理或者展示需要。

如后端开发,我们可以在VS2022中进行管理,管理开发Winform项目、Web API项目等。

Winform界面,我们可以采用基于.net Framework开发或者.net core6进行开发均可,因为我们的SugarProjectCore项目是采用.net Standard模式开发,兼容两者。这里以权限模块来进行演示整合使用。

 

 而纯前端的项目,我们可以基于VSCode或者 HBuilderX等工具进行项目的管理开发工作。

 


2、框架基础类的定义和处理

在开发一个易于使用的框架的时候,主要目的就是减少代码开发,并尽可能通过基类和泛型约束的方式,提高接口的通用性,并通过结合代码生成工具的方式,来提高标准项目的开发效率。

那么我们这里基于SqlSugar的ORM处理,来实现常规数据的增删改查等常规操作的时候,我们是如何进行这些接口的封装处理的呢。

例如,我们对于一个简单的客户信息表,如下所示。

 那么它生成的SqlSugar实体类如下所示。

///


/// 客户信息
/// 继承自Entity,拥有Id主键属性
///

[SugarTable("T_Customer")]
public class CustomerInfo : Entity
{
///
/// 默认构造函数(需要初始化属性的在此处理)
///

public CustomerInfo()
{
this.CreateTime = System.DateTime.Now;
}
#region Property Members
///
/// 姓名
///

public virtual string Name { get; set; }
///
/// 年龄
///

public virtual int Age { get; set; }
///
/// 创建人
///

public virtual string Creator { get; set; }
///
/// 创建时间
///

public virtual DateTime CreateTime { get; set; }
#endregion
}

其中 Entity 是我们根据需要定义一个基类实体对象,主要就是定义一个Id的属性来处理,毕竟对于一般表对象的处理,SqlSugar需要Id的主键定义(非中间表处理)。

[Serializable]
public abstract class Entity : IEntity
{
///


/// 实体类唯一主键
///

[SqlSugar.SugarColumn(IsPrimaryKey = true, ColumnDescription = "主键")]
public virtual TPrimaryKey Id { get; set; }
}

而IEntity定义了一个接口

public interface IEntity
{
///


/// 实体类唯一主键
///

TPrimaryKey Id { get; set; }
}

以上就是实体类的处理,我们一般为了查询信息,往往通过一些条件传入进行处理,那么我们就需要定义一个通用的分页查询对象,供我们精准进行条件的处理。

生成一个以***PageDto的对象类,如下所示。

///


/// 用于根据条件分页查询,DTO对象
///

public class CustomerPagedDto : PagedAndSortedInputDto, IPagedAndSortedResultRequest
{
///
/// 默认构造函数
///

public CustomerPagedDto() : base() { }
///
/// 参数化构造函数
///

///

跳过的数量
///

最大结果集数量
public CustomerPagedDto(int skipCount, int resultCount) : base(skipCount, resultCount)
{
}
///


/// 使用分页信息进行初始化SkipCount 和 MaxResultCount
///

///

分页信息
public CustomerPagedDto(PagerInfo pagerInfo) : base(pagerInfo)
{
}
#region Property Members
///


/// 不包含的对象的ID,用于在查询的时候排除对应记录
///

public virtual string ExcludeId { get; set; }
///
/// 姓名
///

public virtual string Name { get; set; }
///
/// 年龄-开始
///

public virtual int? AgeStart { get; set; }
///
/// 年龄-结束
///

public virtual int? AgeEnd { get; set; }
///
/// 创建时间-开始
///

public DateTime? CreateTimeStart { get; set; }
///
/// 创建时间-结束
///

public DateTime? CreateTimeEnd { get; set; }
#endregion
}

其中PagedAndSortedInputDto, IPagedAndSortedResultRequest都是参考来自于ABP/ABP VNext的处理方式,这样我们可以便于数据访问基类的查询处理操作。

接着我们定义一个基类MyCrudService,并传递如相关的泛型约束,如下所示

///


/// 基于SqlSugar的数据库访问操作的基类对象
///

/// 定义映射的实体类
/// 主键的类型,如int,string等
/// 或者分页信息的条件对象
public abstract class MyCrudService :
IMyCrudService
where TEntity : class, IEntity, new()
where TGetListInput : IPagedAndSortedResultRequest

我们先忽略基类接口的相关实现细节,我们看看对于这个MyCrudService和 IMyCrudService 我们应该如何使用的。

首先我们定义一个应用层的接口ICustomerService如下所示。

///


/// 客户信息服务接口
///

public interface ICustomerService : IMyCrudService, ITransientDependency
{
}

然后实现在CustomerService中实现它的接口。

///


/// 应用层服务接口实现
///

public class CustomerService : MyCrudService, ICustomerService

这样我们对于特定Customer的接口在ICustomer中定义,标准接口直接调用基类即可。

基类MyCrudService提供重要的两个接口,让子类进行重写,以便于进行准确的条件处理和排序处理,如下代码所示。

///


/// 基于SqlSugar的数据库访问操作的基类对象
///

/// 定义映射的实体类
/// 主键的类型,如int,string等
/// 或者分页信息的条件对象
public abstract class MyCrudService :
IMyCrudService
where TEntity : class, IEntity, new()
where TGetListInput : IPagedAndSortedResultRequest
{
///
/// 留给子类实现过滤条件的处理
///

///
protected virtual ISugarQueryable CreateFilteredQueryAsync(TGetListInput input)
{
return EntityDb.AsQueryable();
}
///
/// 默认排序,通过ID进行排序
///

///


///
protected virtual ISugarQueryable ApplyDefaultSorting(ISugarQueryable query)
{
if (typeof(TEntity).IsAssignableTo>())
{
return query.OrderBy(e => e.Id);
}
else
{
return query.OrderBy("Id");
}
}
}

对于Customer特定的业务对象来说,我们需要实现具体的条件查询细节和排序条件,毕竟我们父类没有约束确定实体类有哪些属性的情况下,这些就交给子类做最合适了。

///


/// 应用层服务接口实现
///

public class CustomerService : MyCrudService, ICustomerService
{
///
/// 自定义条件处理
///

///

查询条件Dto
///
protected override ISugarQueryable 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;
}
///


/// 自定义排序处理
///

///

可查询LINQ
///
protected override ISugarQueryable ApplyDefaultSorting(ISugarQueryable query)
{
return query.OrderBy(t => t.CreateTime, OrderByType.Desc);
//先按第一个字段排序,然后再按第二字段排序
//return base.ApplySorting(query, input).OrderBy(s=>s.Customer_ID).OrderBy(s => s.Seq);
}
}

通过 CreateFilteredQueryAsync 的精确条件处理,我们就可以明确实体类的查询条件处理,因此对于CustomerPagedDto来说,就是可以有客户端传入,服务后端的基类进行处理了。

如基类的分页条件查询函数GetListAsync就是根据这个来处理的,它的实现代码如下所示。

///


/// 根据条件获取列表
///

///

分页查询条件
///
public virtual async Task

> 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(
totalCount,
list
);
}

而其中 ApplySorting 就是根据条件决定是否选择子类实现的默认排序进行处理的。

///


/// 记录排序处理
///

///
protected virtual ISugarQueryable ApplySorting(ISugarQueryable query, TGetListInput input)
{
//Try to sort query if available
if (input is ISortedResultRequest sortInput)
{
if (!sortInput.Sorting.IsNullOrWhiteSpace())
{
return query.OrderBy(sortInput.Sorting);
}
}
//IQueryable.Task requires sorting, so we should sort if Take will be used.
if (input is ILimitedResultRequest)
{
return ApplyDefaultSorting(query);
}
//No sorting
return query;
}

对于获取单一对象,我们一般提供一个ID主键获取即可。

///


/// 根据ID获取单一对象
///

///

主键ID
///
public virtual async Task GetAsync(TKey id)
{
return await EntityDb.GetByIdAsync(id);
}

也可以根据用户的Express条件进行处理,在基类我们定义很多这样的Express条件处理,便于子类进行条件处理的调用。如对于删除,可以指定ID,也可以指定条件删除。

///


/// 删除指定ID的对象
///

///

记录ID
///
public virtual async Task DeleteAsync(TKey id)
{
return await EntityDb.DeleteByIdAsync(id);
}

///


/// 根据指定条件,删除集合
///

///

表达式条件
///
public virtual async Task DeleteAsync(Expression> input)
{
var result = await EntityDb.DeleteAsync(input);
return result;
}

 

https://weibo.com/a/hot/7627533502715906_1.html
https://weibo.com/a/hot/7627533505828866_1.html
https://weibo.com/a/hot/7627533508941825_1.html



推荐阅读
  • Windows服务与数据库交互问题解析
    本文探讨了在Windows 10(64位)环境下开发的Windows服务,旨在定期向本地MS SQL Server (v.11)插入记录。尽管服务已成功安装并运行,但记录并未正确插入。我们将详细分析可能的原因及解决方案。 ... [详细]
  • SQL中UPDATE SET FROM语句的使用方法及应用场景
    本文详细介绍了SQL中UPDATE SET FROM语句的使用方法,通过具体示例展示了如何利用该语句高效地更新多表关联数据。适合数据库管理员和开发人员参考。 ... [详细]
  • 本文深入探讨 MyBatis 中动态 SQL 的使用方法,包括 if/where、trim 自定义字符串截取规则、choose 分支选择、封装查询和修改条件的 where/set 标签、批量处理的 foreach 标签以及内置参数和 bind 的用法。 ... [详细]
  • 使用C#开发SQL Server存储过程的指南
    本文介绍如何利用C#在SQL Server中创建存储过程,涵盖背景、步骤和应用场景,旨在帮助开发者更好地理解和应用这一技术。 ... [详细]
  • 本文探讨了适用于Spring Boot应用程序的Web版SQL管理工具,这些工具不仅支持H2数据库,还能够处理MySQL和Oracle等主流数据库的表结构修改。 ... [详细]
  • 本文详细介绍了如何通过多种编程语言(如PHP、JSP)实现网站与MySQL数据库的连接,包括创建数据库、表的基本操作,以及数据的读取和写入方法。 ... [详细]
  • 在当前众多持久层框架中,MyBatis(前身为iBatis)凭借其轻量级、易用性和对SQL的直接支持,成为许多开发者的首选。本文将详细探讨MyBatis的核心概念、设计理念及其优势。 ... [详细]
  • 在使用 DataGridView 时,如果在当前单元格中输入内容但光标未移开,点击保存按钮后,输入的内容可能无法保存。只有当光标离开单元格后,才能成功保存数据。本文将探讨如何通过调用 DataGridView 的内置方法解决此问题。 ... [详细]
  • 本文详细介绍了如何在 Linux 平台上安装和配置 PostgreSQL 数据库。通过访问官方资源并遵循特定的操作步骤,用户可以在不同发行版(如 Ubuntu 和 Red Hat)上顺利完成 PostgreSQL 的安装。 ... [详细]
  • 如何在PostgreSQL中查看数据表
    本文将指导您使用pgAdmin工具连接到PostgreSQL数据库,并展示如何浏览和查找其中的数据表。通过简单的步骤,您可以轻松访问所需的表结构和数据。 ... [详细]
  • 利用存储过程构建年度日历表的详细指南
    本文将介绍如何使用SQL存储过程创建一个完整的年度日历表。通过实例演示,帮助读者掌握存储过程的应用技巧,并提供详细的代码解析和执行步骤。 ... [详细]
  • 本文介绍了如何通过 Maven 依赖引入 SQLiteJDBC 和 HikariCP 包,从而在 Java 应用中高效地连接和操作 SQLite 数据库。文章提供了详细的代码示例,并解释了每个步骤的实现细节。 ... [详细]
  • 在使用SQL Server进行动态SQL查询时,如果遇到LIKE语句无法正确返回预期结果的情况,通常是因为参数传递方式不当。本文将详细探讨这一问题,并提供解决方案及相关的技术背景。 ... [详细]
  • 本文介绍如何通过创建替代插入触发器,使对视图的插入操作能够正确更新相关的基本表。涉及的表包括:飞机(Aircraft)、员工(Employee)和认证(Certification)。 ... [详细]
  • MySQL缓存机制深度解析
    本文详细探讨了MySQL的缓存机制,包括主从复制、读写分离以及缓存同步策略等内容。通过理解这些概念和技术,读者可以更好地优化数据库性能。 ... [详细]
author-avatar
书苑幽香
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有