一、引言

在前面几篇文章中,我们描述了net框架、http管道、配置、serilog日志等,对net框架进行了一个基础的描述。在本篇中,我们进入数据库操作部分。

在NET中操作数据库的几种常见方式有ADO.NET、ORM框架。ADO.NET属于底层的数据库访问技术,通过SQL语句直接操作数据库;ORM(对象关系映射)则是通过将数据库表映射为程序类,以面向对象的方式操作数据库,无需编写SQL语句。
ORM数据库访问有EF Core、Dapper、Sqlsugar等方式,sqlsugar是介于EF Core和Dapper中间的实现方式,高性能、兼容多种数据库、语法简洁。是web快速开发的优秀工具。

在前面几篇文章中,我们对 .NET 框架进行了较为全面的基础性描述,涵盖了矿建搭建、 HTTP 管道、配置管理以及 Serilog 日志等多个重要方面,勾勒除了一个基础的.NET技术生态轮廓。
本篇我们将开始net数据库操作方面的知识。在 .NET 中操作数据库有几种常见方式,其中 ADO.NET 和 ORM(对象关系映射)框架是最主要的两种。 ADO.NET 属于底层的数据库访问技术,通过直接执行 SQL 语句来操作数据库,这种方式赋予开发者极大的灵活性,但在复杂项目中可能导致大量重复性代码。相比之下,ORM 框架通过将数据库表映射为程序中的类,使开发者能够以面向对象的方式操作数据库,无需手动编写繁琐的 SQL 语句,从而提高开发效率并减少错误。
在众多 ORM 框架中,EF Core、Dapper 和 SqlSugar 是较为突出的几种。 SqlSugar 作为介于 EF Core 和 Dapper 之间的实现方式,兼具高性能、兼容多种数据库以及语法简洁等优势,是 web 快速开发的优秀工具。它在开发效率、代码可维护性和安全性方面表现出色,特别适合快速开发和中小型项目。然而,在处理复杂查询时可能需要手动优化,且存在一定的学习成本和灵活性限制。

数据库操作结构图

在这里插入图片描述

ORM框架对比

框架 发布机构 是否开源 优点 缺点
EF Core 微软(国外) 1. 官方支持,与.NET生态深度集成
2. 支持LINQ强类型查询
3. 跨平台兼容性好,支持多种数据库
4. 提供Code First和Database First模式
1. 复杂查询性能较低,生成的SQL不够理想
2. 对低版本数据库支持较弱
3. 学习曲线陡峭,配置复杂
Dapper Stack Overflow(国外) 1. 高性能(接近原生ADO.NET)
2. 轻量级(编译后仅40KB)
3. 支持多种数据库(SQL Server、MySQL等)
4. 灵活性高,适合直接编写SQL
1. 需手动处理复杂对象关系映射
2. 事务操作需手动管理
3. 复杂查询需大量手写SQL,维护成本高
SqlSugar 果糖大数据科技团队(国产) 1. 高性能(优于Dapper的部分场景)
2. 多库无缝切换(支持30+数据库)
3. 简单易用,支持自动分表、多租户
4. 提供AOT兼容和低代码开发支持
1. 高级功能需学习特定语法(如分表配置)
2. 文档相对分散,部分高级特性示例较少

二、SqlSugar引入

1、依赖包安装

nuget包 功能描述
SqlSugarCore SqlSugar依赖包

2、数据库配置

在系统配置中添加数据库连接配置,参照上篇配置文件读取系统配置

    "AppSettings": {
        //用户库连接信息
        "MySqlUserConnection": "server=127.0.0.1;Database=DBUser;Uid=system;Pwd=123456;CharSet=utf8mb4;SslMode=none",
        //业务库连接信息
        "MySqlDataConnection": "server=127.0.0.1;Database=DBData;Uid=system;Pwd=123456;CharSet=utf8mb4;SslMode=none",
        //配置库连接信息
        "MySqlConfigConnection": "server=127.0.0.1;Database=DBConfig;Uid=system;Pwd=123456;CharSet=utf8mb4;SslMode=none"
    }

3、数据库服务引入

我们使用SqlSugarScope 单例模式访问数据库,对新手更加友好。

        //在program.cs中注册服务
	    builder.Services.AddSqlsugarSetup();
        
		//编写sqlsugar注册函数
        public static void AddSqlsugarSetup(this IServiceCollection services)
        {
            //查询配置服务
            AppSettingService appSetting =services?.BuildServiceProvider()?.GetService<AppSettingService>();
            //数据库链接,修改配置里面的SqlServerConnection的字符串
            var UserConnectionString = appSetting?.MySqlUserConnection;
            var DataConnectionString = appSetting?.MySqlDataConnection;
            var ConficConnectionString = appSetting?.MySqlConfigConnection;



            //这边是SqlSugarScope用AddSingleton单例,适合多租户
            #region 创建连接参数
            var connections = new List<ConnectionConfig>() {
                new ConnectionConfig
                {
                    //数据库类型
                    DbType =  SqlSugar.DbType.MySql,
                    //连接字符串
                    ConnectionString = UserConnectionString,
                    //自动释放和关闭数据库连接
                    IsAutoCloseConnection = true,
                    //多租户唯一标识
                    ConfigId = "user"
                },
                new ConnectionConfig
                {
                    DbType = SqlSugar.DbType.MySql,
                    ConnectionString = DataConnectionString,
                    IsAutoCloseConnection = true,
                    ConfigId = "data"
                }
                ,new ConnectionConfig
                {
                    DbType = SqlSugar.DbType.MySql,
                    ConnectionString = ConficConnectionString,
                    IsAutoCloseConnection = true,
                    ConfigId = "config"
                }
            };
            #endregion

            #region 注册单例服务-SqlSugarScope
            services.AddSingleton<ISqlSugarClient>(s => {
                SqlSugarScope Db = new SqlSugarScope(connections, db => {
                    connections.ForEach(con =>
                    {
                        var tdb = db.GetConnection(con.ConfigId);
                        // 打印SQL语句
                        tdb.Aop.OnLogExecuting = (sql, parameters) =>
                        {
                            var originColor = Console.ForegroundColor;
                            if (sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
                                Console.ForegroundColor = ConsoleColor.Green;
                            if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) ||
                                sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
                                Console.ForegroundColor = ConsoleColor.Yellow;
                            if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase))
                                Console.ForegroundColor = ConsoleColor.Red;
                            Console.WriteLine("【" + DateTime.Now + "——执行SQL】\r\n" +
                                              UtilMethods.GetSqlString(db.CurrentConnectionConfig.DbType, sql, parameters) +
                                              "\r\n");
                            Console.ForegroundColor = originColor;
                        };
                        //数据处理:增、删 和 改
                        tdb.Aop.DataExecuting = (oldValue, entityInfo) =>
                        {
                            try
                            {
                                if (entityInfo.OperationType == DataFilterType.InsertByObject)
                                {
                                }
                                else if (entityInfo.OperationType == DataFilterType.UpdateByObject)
                                {
                                }
                            }
                            catch
                            {
                            }
                        };
                        //查询事件 
                        tdb.Aop.DataExecuted = (value, entity) => { };
                        tdb.Aop.OnError = ex =>
                        {
                            if (ex.Parametres == null) return;
                            var originColor = Console.ForegroundColor;
                            Console.ForegroundColor = ConsoleColor.DarkRed;
                            var pars = db.Utilities.SerializeObject(
                                ((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value));
                            Console.ForegroundColor = originColor;
                            Console.WriteLine("【" + DateTime.Now + "——执行SQL异常】\r\n" + pars + " \r\n");
                        };

                        //监控所有超过1秒的Sql 
                        tdb.Aop.OnLogExecuted = (sql, p) =>
                        {
                            if (db.Ado.SqlExecutionTime.TotalSeconds > 1)
                            {
                                //代码CS文件名
                                var fileName = db.Ado.SqlStackTrace.FirstFileName;
                                //代码行数
                                var fileLine = db.Ado.SqlStackTrace.FirstLine;
                                //方法名
                                var FirstMethodName = db.Ado.SqlStackTrace.FirstMethodName;
                                //db.Ado.SqlStackTrace.MyStackTraceList[1].xxx 获取上层方法的信息

                                Console.WriteLine("【" + DateTime.Now + "——执行SQL超时】\r\n" + fileName + " \r\n");
                            }

                            ;
                        };

                    });
                });
                return Db;

            });
            #endregion
        }

三、SqlSugar 实体定义

1、定义数据实体

定义一个数据仓储实体,对标数据库表结构

/// <summary>
/// t_sys_user:数据库映射类
/// TenantAttribute config 为配置存储库  data为数据存储库
/// </summary>
[Serializable]  //标记序列化
[SugarTable("t_sys_user")] //标记数据表
[TenantAttribute("user")] //标记数据库
public class TSysUser
{
	/// <summary>
	/// 主键
	/// </summary>
	[SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true,ColumnDescription ="自增长ID")]
    public int Id { get; set; }

	/// <summary>
	/// 用户名
	/// </summary>
	[SugarColumn(ColumnName = "Name")]
    public string Name { get; set; }

	/// <summary>
	/// 密码
	/// </summary>
	[SugarColumn(ColumnName = "PassWord")]
    public string PassWord { get; set; }

	/// <summary>
	/// 角色ID
	/// </summary>
	[SugarColumn(ColumnDescription = "角色IDs", IsJson = true)]
	public List<int> UserRolesId { get; set; }

	/// <summary>
	/// 状态(1正常,0冻结)
	/// 默认值:1
	/// </summary>
	[SugarColumn(ColumnName = "Status", DefaultValue = "1")]
    public int Status { get; set; }

	/// <summary>
	/// 备注
	/// </summary>
	[SugarColumn(ColumnName = "Remark")]
    public string Remark { get; set; }

    /// <summary>
    /// 创建时间
    /// </summary>
    [SugarColumn(ColumnName = "CreateDT",ColumnDescription ="创建时间",IsOnlyIgnoreUpdate =true)]
    public DateTime CreateDT { get; set; }
    /// <summary>
    /// 更新时间
    /// </summary>
    [SugarColumn(ColumnName = "UpdateDT", ColumnDescription = "更新时间", IsOnlyIgnoreInsert = true)]
    public DateTime UpdateDT { get; set; }

}

2、定义泛型仓储

我们定义一个数据仓储的实现类,泛型结构。
将数据结构实现封装起来,用于简化实现。示例代码仅展示查询部分,完整代码在资源中上传。

    //定义数据仓储实现类,泛型
    public class SqlSugarRepository<TEntity>where TEntity : class, new()
    {

        #region 属性

        /// <summary>
        /// 初始化 SqlSugar 客户端
        /// </summary>
        private readonly ISqlSugarClient _db;

        /// <summary>
        /// 数据库上下文
        /// </summary>
        public ISqlSugarClient Context { get; }

        /// <summary>
        /// 独立数据库上下文
        /// </summary>
        private SqlSugarScopeProvider EntityContext { get; }

        /// <summary>
        /// 实体集合
        /// </summary>
        private ISugarQueryable<TEntity> Entities => _db.AsTenant().GetConnectionScopeWithAttr<TEntity>().Queryable<TEntity>();

        /// <summary>
        /// 原生 Ado 对象
        /// </summary>
        private IAdo Ado { get; }

        #endregion

        #region 构造函数
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="db"></param>
        public SqlSugarRepository(ISqlSugarClient db)
        {
            try
            {
                Context = _db = db.AsTenant().GetConnectionScopeWithAttr<TEntity>();
                // 命名空间中包含.CreateTable 就不会创建表
                string s1 = typeof(TEntity).FullName;
                if (typeof(TEntity).FullName.Contains(".CreateTable"))
                {
                    _db.CodeFirst.InitTables<TEntity>();
                }
                EntityContext = _db.AsTenant().GetConnectionScopeWithAttr<TEntity>();
                Ado = EntityContext.Ado;
            }
            catch (Exception ex)
            {
                string s = ex.Message;
            }

        }
        #endregion

        #region 查询

        /// <summary>
        /// 获取总数
        /// </summary>
        /// <param name="whereExpression"></param>
        /// <returns></returns>
        public int Count(Expression<Func<TEntity, bool>> whereExpression)
        {
            return Entities.Count(whereExpression);
        }

        /// <summary>
        /// 获取总数
        /// </summary>
        /// <param name="whereExpression"></param>
        /// <returns></returns>
        public Task<int> CountAsync(Expression<Func<TEntity, bool>> whereExpression)
        {
            return Entities.CountAsync(whereExpression);
        }

        /// <summary>
        /// 检查是否存在
        /// </summary>
        /// <param name="whereExpression"></param>
        /// <returns></returns>
        public bool Any(Expression<Func<TEntity, bool>> whereExpression)
        {
            return Entities.Any(whereExpression);
        }

        /// <summary>
        /// 检查是否存在
        /// </summary>
        /// <param name="whereExpression"></param>
        /// <returns></returns>
        public async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> whereExpression)
        {
            return await Entities.AnyAsync(whereExpression);
        }

        /// <summary>
        /// 通过主键获取实体
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public TEntity Single(dynamic Id)
        {
            return Entities.InSingle(Id);
        }

        /// <summary>
        /// 获取一个实体
        /// </summary>
        /// <param name="whereExpression"></param>
        /// <returns></returns>
        public TEntity Single(Expression<Func<TEntity, bool>> whereExpression)
        {
            return Entities.Single(whereExpression);
        }

        /// <summary>
        /// 获取一个实体
        /// </summary>
        /// <param name="whereExpression"></param>
        /// <returns></returns>
        public async Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> whereExpression)
        {
            return await Entities.SingleAsync(whereExpression);
        }

        /// <summary>
        /// 获取一个实体
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<TEntity> SingleAsync(dynamic id)
        {
            return await Entities.InSingleAsync(id);
        }

        /// <summary>
        /// 获取一个实体
        /// </summary>
        /// <param name="whereExpression"></param>
        /// <returns></returns>
        public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> whereExpression)
        {
            return Entities.First(whereExpression);
        }

        /// <summary>
        /// 获取一个实体
        /// </summary>
        /// <param name="whereExpression"></param>
        /// <returns></returns>
        public async Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> whereExpression)
        {
            return await Entities.FirstAsync(whereExpression);
        }

        /// <summary>
        /// 获取列表
        /// </summary>
        /// <returns></returns>
        public List<TEntity> ToList()
        {
            return Entities.ToList();
        }

        /// <summary>
        /// 获取列表
        /// </summary>
        /// <param name="whereExpression"></param>
        /// <returns></returns>
        public List<TEntity> ToList(Expression<Func<TEntity, bool>> whereExpression)
        {
            return Entities.Where(whereExpression).ToList();
        }

        /// <summary>
        /// 获取列表
        /// </summary>
        /// <param name="whereExpression"></param>
        /// <param name="orderByExpression"></param>
        /// <param name="orderByType"></param>
        /// <returns></returns>
        public List<TEntity> ToList(Expression<Func<TEntity, bool>> whereExpression,
            Expression<Func<TEntity, object>> orderByExpression = null, OrderByType orderByType = OrderByType.Asc)
        {
            return Entities.OrderByIF(orderByExpression != null, orderByExpression, orderByType).Where(whereExpression)
                .ToList();
        }

        /// <summary>
        /// 获取列表
        /// </summary>
        /// <returns></returns>
        public Task<List<TEntity>> ToListAsync()
        {
            return Entities.ToListAsync();
        }

        /// <summary>
        /// 获取列表
        /// </summary>
        /// <param name="whereExpression"></param>
        /// <returns></returns>
        public Task<List<TEntity>> ToListAsync(Expression<Func<TEntity, bool>> whereExpression)
        {
            return Entities.Where(whereExpression).ToListAsync();
        }

        /// <summary>
        /// 获取列表
        /// </summary>
        /// <param name="whereExpression"></param>
        /// <param name="orderByExpression"></param>
        /// <param name="orderByType"></param>
        /// <returns></returns>
        public Task<List<TEntity>> ToListAsync(Expression<Func<TEntity, bool>> whereExpression,
            Expression<Func<TEntity, object>> orderByExpression = null, OrderByType orderByType = OrderByType.Asc)
        {
            return Entities.OrderByIF(orderByExpression != null, orderByExpression, orderByType).Where(whereExpression)
                .ToListAsync();
        }

        #endregion
}

3、仓储注册、实体使用

在 .NET 依赖注入(DI)中,使用 services.AddScoped(typeof(SqlSugarRepository<>)) 注册开放泛型类型时,并不会自动注入所有可能的封闭泛型类型(如 SqlSugarRepository<User>SqlSugarRepository<TSysUser> 等)。
但依赖注入容器(如 ASP.NET Core 的 ServiceCollection)会动态处理泛型类型的解析,容器会根据已注册的开放泛型类型动态生成 SqlSugarRepository<User> 的实例,无需显式注册每个封闭泛型类型。

1.在 Program.cs 或依赖注入配置中,注册开放泛型仓储:

    //注册数据仓储(开放泛型类型)
	builder.Services.AddScoped(typeof(SqlSugarRepository<>));

2.直接在构造函数或属性中注入具体类型的仓储

	public class UserService
	{
		private readonly SqlSugarRepository<User> _userRepository;

		public UserService(SqlSugarRepository<User> userRepository)
		{
			_userRepository = userRepository; // 容器自动解析并注入
		}
	}

四、SqlSugar数据初始化

        /// <summary>
        /// 数据库初始化
        /// </summary>
        public static void InitDB()
        {
            if (appSetting.IsInitDB)  //数据库初始化标志
            {
                try
                {
                    //提取服务
                    ISqlSugarClient sugarClient = services.BuildServiceProvider().GetRequiredService<ISqlSugarClient>();
                    //创建数据库
                    sugarClient.DbMaintenance.CreateDatabase();
                    //获取仓储表对象,批量创建数据表
                    var types = GetSugarTableTypes();
                    sugarClient.CodeFirst.SetStringDefaultLength(200).InitTables(types.ToArray());
                }
                catch (Exception ex)
                {
                    throw ex;
                }

            }
        }
		
        /// <summary>
        /// 提取命名空间下的 带有SugarTable标识的类
        /// </summary>
        /// <returns></returns>
        public static List<Type> GetSugarTableTypes()
        {
            //提取此命名空间的所有定义
            var assemblies = AssemblyHelper.GetAssemblies("Mis2.Net9.WebAPI.Repositories");

            var types = new List<Type>();

            foreach (var assembly in assemblies)
            {
                // 获取所有类型
                var assemblyTypes = assembly.GetTypes();

                // 筛选出带有SugarTable特性的非抽象类
                types.AddRange(assemblyTypes.Where(type =>
                        !type.IsAbstract &&
                        type.IsDefined(typeof(SugarTable), true) // true 表示从继承层次结构中搜索特性
                ));
            }

            return types;
        }

五、时序图

开发者 NuGet .NET Core服务容器 SqlSugar组件 数据库 业务服务 1.安装SqlSugarCore包 包安装完成 2.注册ISqlSugarClient单例服务 创建多库托管配置 返回服务实例 3.定义泛型仓储SqlSugarRepository<T>和实体TSysUser 4.注册SqlSugarRepository<T>服务 4.1 根据标志创建数据库 执行CREATE DATABASE 返回创建结果 4.2 初始化数据表结构 返回表创建结果 5.注入SqlSugarRepository<TSysUser> 6.1 执行CRUD操作 执行对应SQL 返回操作结果 6.2 执行复杂SQL 执行自定义SQL 返回数据结果 开发者 NuGet .NET Core服务容器 SqlSugar组件 数据库 业务服务

六、总结

在本次技术文章中,我们深入探讨了在.NET 开发中使用 SqlSugar 实现 ORM 数据库操作的相关内容。SqlSugar 作为一种功能强大的 ORM 框架,在提升开发效率、增强代码可维护性和安全性方面具有显著优势,特别适合快速开发和中小型项目。然而,在使用过程中也需要注意其性能开销、学习成本和灵活性受限等问题。通过合理选择适用场景并充分了解其优缺点,开发人员可以更好地利用 SqlSugar 实现高效的数据库操作,提升项目整体质量和开发效率。

1、适用场景
SqlSugar 适用于快速开发和中小型项目,特别是那些需要频繁操作数据库的应用场景,例如 Web 应用和企业管理系统。
2、优点
(1) 开发效率高 :能够自动生成 SQL 语句,减少手动编写 SQL 的工作量,从而显著提升开发效率。
(2) 代码可维护性高 :通过对象模型操作数据库,使代码更易读、扩展和重构,便于后期维护。
(3)跨数据库支持*:支持多种数据库,切换数据库时仅需修改配置,降低了数据库迁移成本。
(4) 安全性增强*:内置防 SQL 注入机制,有效减少安全风险,保障数据安全。
3、缺点
(1)性能开销 :抽象层可能引入延迟,在处理复杂查询时可能需要手动优化,如使用原生 SQL。
(2) 学习成本 :使用该框架需要掌握其特定的特性和配置,如 Hibernate 的缓存配置等。
(3)灵活性受限 :在极端复杂场景下,可能需要绕过 ORM 来实现特定功能。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐