RuoVea.ExSugar 10.0.1.2

dotnet add package RuoVea.ExSugar --version 10.0.1.2
                    
NuGet\Install-Package RuoVea.ExSugar -Version 10.0.1.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RuoVea.ExSugar" Version="10.0.1.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RuoVea.ExSugar" Version="10.0.1.2" />
                    
Directory.Packages.props
<PackageReference Include="RuoVea.ExSugar" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add RuoVea.ExSugar --version 10.0.1.2
                    
#r "nuget: RuoVea.ExSugar, 10.0.1.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package RuoVea.ExSugar@10.0.1.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=RuoVea.ExSugar&version=10.0.1.2
                    
Install as a Cake Addin
#tool nuget:?package=RuoVea.ExSugar&version=10.0.1.2
                    
Install as a Cake Tool

🍬 RuoVea.ExSugar

SqlSugar ORM 一站式扩展库 — 集成 DI 注册、DbContext/Repository 模式、审计字段自动填充、全局查询过滤器、工作单元事务、差异日志记录、SM2 连接字符串加密,覆盖从 DataExecuting AOP 到分页查询的完整数据访问层抽象。


📖 目录


📋 概览

RuoVea.ExSugar 为 .NET 开发者提供 SqlSugar ORM 的开箱即用增强封装,通过 DI 容器统一管理数据库连接、审计字段、查询过滤器和事务边界。

 ┌─────────────────────────────────────────────────────────┐
 │                    RuoVea.ExSugar                        │
 ├─────────────────────────────────────────────────────────┤
 │  DI 注册              实体基类            仓储模式       │
 │  ├─ AddSqlSugarSetup   ├─ EntityBase      ├─ SugarRepository<T>
 │  ├─ AddDbContextSetup   ├─ EntityTenant    └─ SimpleClient<T>
 │  └─ AddInjectServiceSetup├─ AutoKeyBase                  │
 │                          └─ AutoKeyEntityBase             │
 │                                                           │
 │  审计 AOP             全局过滤器           工作单元       │
 │  ├─ DataExecuting      ├─ 软删除过滤器     ├─ UnitOfWorkAttribute
 │  ├─ 创建时间自动填充    ├─ 用户Id过滤器     └─ UnitOfWorkFilter
 │  ├─ 修改时间自动填充    ├─ 租户Id过滤器                    │
 │  ├─ 雪花Id自动生成      └─ moreFilter                     │
 │  └─ 租户Id自动设置                                        │
 │                                                           │
 │  差异日志             连接安全             扩展方法       │
 │  ├─ OnDiffLogEvent     ├─ SM2 加密解密     ├─ SqlSugarClient
 │  ├─ UpdateWithDiffLog  └─ IsEncrypt        │   扩展 (40+ 方法)
 │  └─ InsertWithDiffLog                       └─ ISugarQueryable
 │                                               分页扩展     │
 │                                                           │
 │  敏感数据加密          TypeHandler                       │
 │  ├─ SM4-CBC 加解密     ├─ SensitiveDataConverter        │
 │  └─ SM4-ECB 加解密     └─ ISugarDataConverter           │
 ├─────────────────────────────────────────────────────────┤
 │  国际化: zh-CN                                         │
 └─────────────────────────────────────────────────────────┘

设计原则

原则 说明
约定优于配置 AddSqlSugarSetup() 零参数启动,自动从 appsettings.json 读取 ConnectionConfigsDataAuditing
AOP 透明拦截 审计字段在 DataExecuting 事件中自动填充,业务代码无需手动设置 CreateTime / ModifyTime
过滤器可选 软删除、用户隔离、租户隔离均可通过 DbConnectionConfig 按连接独立开关
单例安全 ISqlSugarClient 注册为 Singleton,SqlSugar 内部自行处理线程安全

📦 安装

.NET CLI

# .NET 8.0
dotnet add package RuoVea.ExSugar --version 8.0.0.33

# .NET 10.0
dotnet add package RuoVea.ExSugar --version 10.0.0.7

Package Manager

Install-Package RuoVea.ExSugar -Version 8.0.0.33

PackageReference

<PackageReference Include="RuoVea.ExSugar" Version="8.0.0.33" />

框架引用要求

RuoVea.ExSugar 需要 Microsoft.AspNetCore.App 框架引用(自动包含在 ASP.NET Core 项目中):

<FrameworkReference Include="Microsoft.AspNetCore.App" />

支持的 Target Framework

TFM 最低版本 关键依赖
net8.0 8.0.0.33 SqlSugarCore 5.1.*, Mapster 7.4.0, System.Linq.Dynamic.Core 1.7.2
net10.0 10.0.0.7 SqlSugarCore 5.1.*, Mapster 10.0.7, System.Linq.Dynamic.Core 1.7.2

传递依赖

包名 用途
SqlSugarCore (>= 5.1.*) ORM 核心
Mapster 对象映射(DbContext 模式 / 分页 DTO 转换)
System.Linq.Dynamic.Core 动态 LINQ 表达式
System.Text.RegularExpressions 正则表达式支持
Microsoft.Extensions.DependencyModel 运行时程序集扫描
RuoVea.ExCache 缓存抽象
RuoVea.ExDto DTO 基类(PageParam, ICurrentUser, ITenantEntity, IAuditableEntity, IDeletedEntity
RuoVea.ExIdGen 雪花 Id 生成器
RuoVea.ExUtil 通用工具(校验、字符串扩展)
RuoVea.SM 国密 SM2 加密(连接字符串解密)

⚡ 30 秒快速开始

1. 配置 appsettings.json

{
  "ConnectionConfigs": [
    {
      "ConfigId": "1300000000001",
      "DbType": "MySql",
      "ConnectionString": "Server=127.0.0.1;Database=MyDb;Uid=root;Pwd=123456;",
      "IsAutoCloseConnection": true,
      "EnableDiffLog": false,
      "EnableUnderLine": true,
      "IsEncrypt": false,
      "IsDeleteFilter": true,
      "IsUserIdFilter": false,
      "IsTenantIdFilter": false,
      "CommandTimeOut": 30
    }
  ],
  "DataAuditing": {
    "CreateTime": "CreateTime",
    "ModifyTime": "ModifyTime",
    "Creator": "Creator",
    "Modifier": "Modifier",
    "TenantId": "TenantId",
    "IsDelete": "IsDelete"
  }
}

💡 提示: DataAuditing 各审计字段默认值即驼峰字段名(TenantId 默认值已修正为 "TenantId"),如需映射为下划线等自定义列名再在此覆写。

2. 注册服务

// Program.cs / Startup.cs
using RuoVea.ExSugar;

// <inheritdoc cref="SqlSugarSetup.AddSqlSugarSetup(IServiceCollection, bool, ICacheService, ServiceLifetime, Action{SqlSugarScopeProvider})"/>
// 方式一:从 appsettings.json 自动读取(最简)
builder.Services.AddSqlSugarSetup();

// 方式二:显式传入 IConfiguration
builder.Services.AddSqlSugarSetup(builder.Configuration);

// 方式三:代码构建配置
builder.Services.AddSqlSugarSetup(configs =>
{
    configs.Add(new DbConnectionConfig
    {
        ConfigId = "1300000000001",
        DbType = SqlSugar.DbType.MySql,
        ConnectionString = "Server=127.0.0.1;Database=MyDb;Uid=root;Pwd=123456;",
        EnableUnderLine = true
    });
});

3. 使用 SugarRepository

// <inheritdoc cref="SugarRepository{T}"/>
public class UserService
{
    private readonly SugarRepository<MyUser> _userRepo;

    public UserService(SugarRepository<MyUser> userRepo)
    {
        _userRepo = userRepo;
    }

    public async Task<List<MyUser>> GetAllAsync()
    {
        return await _userRepo.GetListAsync(u => u.IsDelete == IsDelete.N, u => u.Id);
    }

    public async Task<MyUser?> GetByIdAsync(long id)
    {
        return await _userRepo.GetFirstAsync(u => u.Id == id, u => u.Id);
    }
}

30 秒内你完成了: 配置文件编写 → DI 注册 → 使用泛型仓储查询数据。审计字段自动填充、软删除过滤、连接池管理全部由底层 AOP 接管。


🧩 核心场景

场景一:DbContext 模式

┌──────────────────┐
│  自定义 DbContext  │   继承 abstract class DbContext
│  (e.g. MyDbCtx)  │   实现 IDbContext, IDisposable
└────────┬─────────┘
         │ 注入
         ▼
┌──────────────────┐
│  services.       │   AddDbContextSetup<MyDbCtx>(sp => new MyDbCtx(config))
│  AddDbContextSetup│
└──────────────────┘
// <inheritdoc cref="DbContext.DbContext(DbConnectionConfig, int, ICurrentUser, IRestFulLog, bool, bool, bool, Action{SqlSugarScopeProvider})"/>
/// <summary>
/// 自定义数据库上下文,继承抽象基类 DbContext
/// </summary>
public class MyDbContext : DbContext
{
    public MyDbContext(DbConnectionConfig config,
        ICurrentUser currentUser = null,
        IRestFulLog restFulLog = null)
        : base(config, commandTimeOut: 30,
              currentUser: currentUser,
              restFulLog: restFulLog,
              userIdFilter: false,
              tenantIdFilter: false,
              deleteFilter: true)
    {
    }

    // 使用 db 字段访问 SqlSugarClient
    public List<MyUser> GetActiveUsers() =>
        db.ToList<MyUser>(u => u.IsDelete == IsDelete.N);
}

// 注册
builder.Services.AddDbContextSetup(sp =>
{
    var config = new DbConnectionConfig
    {
        ConfigId = "1300000000001",
        DbType = SqlSugar.DbType.MySql,
        ConnectionString = "Server=127.0.0.1;Database=MyDb;Uid=root;Pwd=123456;"
    };
    return new MyDbContext(config,
        sp.GetService<ICurrentUser>(),
        sp.GetService<IRestFulLog>());
});

⚠️ 注意: DbContext 构造函数内有两个重载 —— 一个接收 DbConnectionConfig,另一个接收基类 ConnectionConfig。推荐使用 DbConnectionConfig 重载以启用完整功能(差异日志、过滤器等)。第一个重载中存在 Bug:SugarUtil.SqlSugarClientUtil(db, ...)db 参数传入了未初始化的字段而非局部变量 _db


场景二:Repository 仓储模式

┌─────────────────────────┐
│  SugarRepository<T>      │   继承 SimpleClient<T>
│  ─────────────────────  │   自动排除 SystemTable 实体
│  ├─ GetPageResultAsync   │
│  ├─ GetFirstAsync        │
│  ├─ GetListAsync         │
│  ├─ SumAsync             │
│  ├─ Insert / InsertAsync │
│  └─ Update / UpdateAsync │
└─────────────────────────┘
// <inheritdoc cref="SugarRepository{T}.GetPageResultAsync"/>
// 分页查询
var (list, total, totalPage) = await _repo.GetPageResultAsync(
    where: u => u.IsDelete == IsDelete.N,
    pageNo: 1,
    pageSize: 20,
    order: u => u.CreateTime,
    orderEnum: SordEnum.Desc
);

// <inheritdoc cref="SugarRepository{T}.GetFirstAsync"/>
// 获取最新一条
var latest = await _repo.GetFirstAsync(
    where: u => u.Status == 1,
    order: u => u.CreateTime,
    orderEnum: SordEnum.Desc
);

// <inheritdoc cref="SugarRepository{T}.GetListAsync"/>
// 条件列表查询
var list = await _repo.GetListAsync(
    where: u => u.Creator == userId,
    order: u => u.Id
);

// <inheritdoc cref="SugarRepository{T}.SumAsync"/>
// 求和
var totalAmount = await _repo.SumAsync(
    where: u => u.IsDelete == IsDelete.N,
    expression: u => u.Amount
);

// <inheritdoc cref="SugarRepository{T}.Insert(T, bool, bool, object)"/>
// 插入(可选差异日志)
_repo.Insert(entity, ignoreNullValues: true, enableDiffLog: true);

// <inheritdoc cref="SugarRepository{T}.Update(T, bool, bool, object)"/>
// 更新(可选差异日志)
_repo.Update(entity, ignoreNullValues: true, enableDiffLog: true);

性能提示: SugarRepository<T> 构造函数中会检查 SystemTableAttribute,如果实体标记了该特性则跳过特殊初始化。对于不需要租户/分库逻辑的简单实体,建议添加 [SystemTable] 特性以加速构造。


场景三:审计字段自动填充

┌──────────────────────────────────────────────────────────┐
│                    DataExecuting AOP                      │
├──────────────────────────────────────────────────────────┤
│  InsertByObject:                                          │
│    ├─ long Id == 0 → IdGenerator.Id (雪花Id)              │
│    ├─ CreateTime == null → DateTime.Now                   │
│    ├─ IsDelete == null → IsDelete.N                       │
│    ├─ TenantId == null/0 → currentUser.TenantId           │
│    └─ Creator == null/0 → currentUser.UserId              │
│                                                           │
│  UpdateByObject:                                          │
│    ├─ ModifyTime == null → DateTime.Now                   │
│    └─ Modifier == null/0 → currentUser.UserId             │
└──────────────────────────────────────────────────────────┘
// 实体定义
public class Order : EntityBase  // 继承后自动获得审计能力
{
    [SugarColumn(ColumnDescription = "订单编号")]
    public string OrderNo { get; set; }

    [SugarColumn(ColumnDescription = "订单金额")]
    public decimal Amount { get; set; }
}

// 业务代码 —— 无需手动设置审计字段
var order = new Order
{
    OrderNo = "ORD-20240623-001",
    Amount = 1280.00m
    // CreateTime / Creator / IsDelete / TenantId 由 AOP 自动填充
};

_sqlSugarClient.Insert(order);  // AOP 自动注入审计值

order.Amount = 2560.00m;
_sqlSugarClient.Update(order);  // ModifyTime / Modifier 自动更新

性能提示: DataExecuting 事件每次数据库操作都会触发。对于批量插入/更新的高性能场景,建议使用集合重载(Insert(List<T>) / Update(List<T>))以减少 AOP 回调次数,而非逐条调用。


场景四:全局查询过滤器

┌──────────────────────────────────────────┐
│           QueryFilter 自动注入             │
├──────────────────────────────────────────┤
│  IsDeleteFilter = true (默认)              │
│    → WHERE IsDelete = N                   │
│                                           │
│  IsUserIdFilter = true (默认关闭)          │
│    → WHERE Creator = currentUser.UserId   │
│                                           │
│  IsTenantIdFilter = true (默认关闭)        │
│    → WHERE TenantId = currentUser.TenantId│
│                                           │
│  ⚠️ 超级管理员 (IsSuperAdmin = true) 跳过   │
│     所有过滤器                             │
└──────────────────────────────────────────┘
// 配置每个连接的过滤器
var config = new DbConnectionConfig
{
    ConfigId = "1300000000001",
    DbType = SqlSugar.DbType.MySql,
    ConnectionString = "...",
    IsDeleteFilter = true,       // 开启软删除过滤
    IsUserIdFilter = false,      // 关闭用户隔离
    IsTenantIdFilter = true      // 开启租户隔离
};

// 业务代码 —— 无感知过滤
var users = await _sqlSugarClient.ToListAsync<User>();
// 实际 SQL 自动追加: WHERE IsDelete = 0 AND TenantId = @tenantId

// 超级管理员查全量数据
if (currentUser.IsSuperAdmin)
{
    // 所有过滤器被跳过,可查询所有 IsDelete=Y 的记录
}

⚠️ 超级管理员判断: 过滤器在 SugarUtil.ApplyQueryFilters 中通过 currentUser == null || currentUser.IsSuperAdmin 判断是否跳过。确保 ICurrentUser 实现正确返回 IsSuperAdmin,否则所有用户的过滤器都不会生效。


场景五:工作单元事务

┌──────────────────────────────────────────────┐
│  [UnitOfWork(IsolationLevel.ReadCommitted)]   │
│  ┌─────────────────────────────────────────┐ │
│  │  Controller Action                       │ │
│  │  ├─ repo1.Insert(order)                 │ │
│  │  ├─ repo2.Update(inventory)             │ │
│  │  └─ repo3.Insert(log)                   │ │
│  │       ↓                                 │ │
│  │  无异常 → CommitTran()                   │ │
│  │  有异常 → RollbackTran() → Dispose()    │ │
│  └─────────────────────────────────────────┘ │
└──────────────────────────────────────────────┘

方式一:特性标注(推荐)

// <inheritdoc cref="UnitOfWorkAttribute"/>
[ApiController]
[Route("api/[controller]")]
public class OrderController : ControllerBase
{
    private readonly SugarRepository<Order> _orderRepo;
    private readonly SugarRepository<Inventory> _inventoryRepo;

    public OrderController(
        SugarRepository<Order> orderRepo,
        SugarRepository<Inventory> inventoryRepo)
    {
        _orderRepo = orderRepo;
        _inventoryRepo = inventoryRepo;
    }

    /// <summary>
    /// 创建订单并扣减库存 —— 事务保证原子性
    /// </summary>
    [HttpPost]
    [UnitOfWork(IsolationLevel.ReadCommitted)]  // 标注即启用事务
    public async Task<IActionResult> CreateOrder(OrderDto dto)
    {
        var order = new Order { /* ... */ };
        await _orderRepo.InsertAsync(order);

        var inventory = await _inventoryRepo.GetFirstAsync(
            i => i.ProductId == dto.ProductId, i => i.Id);

        inventory.Stock -= dto.Quantity;
        await _inventoryRepo.UpdateAsync(inventory);

        return Ok();
        // 方法正常返回 → 自动 CommitTran
        // 任意异常   → 自动 RollbackTran + Dispose
    }
}

方式二:全局启用

// <inheritdoc cref="SqlSugarSetup.AddSqlSugarSetup(IServiceCollection, bool, ICacheService, ServiceLifetime, Action{SqlSugarScopeProvider})"/>
// isAllUnitOfWork = true 时,所有 Controller Action 自动包裹事务
builder.Services.AddSqlSugarSetup(isAllUnitOfWork: true);

⚠️ 注册顺序: 全局事务通过 services.AddMvcCore(options => options.Filters.Add(typeof(UnitOfWorkFilter))) 注入,UnitOfWorkFilterOrder = 100。如果你的项目有自定义 Filter 且依赖事务执行顺序,请注意调序。

性能提示: 全局事务模式下,即使是 GET 请求(只读操作)也会走事务流程(BeginTrannext()CommitTran),增加不必要的数据库开销。建议仅在写操作 Controller 上使用 [UnitOfWork] 特性标注,而非全局启用。


场景六:差异日志记录

┌──────────────────────────────────────────┐
│           EnableDiffLog = true            │
├──────────────────────────────────────────┤
│  Insert / Update / Delete 触发            │
│       ↓                                  │
│  OnDiffLogEvent 回调                      │
│       ↓                                  │
│  IRestFulLog.DiffLog(dbType, diffData)   │
│       ↓                                  │
│  输出: BeforeData / AfterData / Sql       │
│        / DiffType / Elapsed              │
└──────────────────────────────────────────┘
// <inheritdoc cref="SqlSugarExtensions.UpdateWithDiffLog{T}(ISqlSugarClient, T, bool)"/>
// 更新并输出差异数据
_sqlSugarClient.UpdateWithDiffLog(entity);
// 输出:
// *****差异日志开始*****
// {
//   "AfterData":  [{"Columns": [{"ColumnName": "Amount", "Value": 2560}]}],
//   "BeforeData": [{"Columns": [{"ColumnName": "Amount", "Value": 1280}]}],
//   "BusinessData": null,
//   "DiffType": "update",
//   "Sql": "UPDATE `Order` SET `Amount`=@Amount WHERE `Id`=@Id",
//   "Elapsed": 12
// }
// *****差异日志结束*****

// <inheritdoc cref="SqlSugarExtensions.InsertWithDiffLog{T}(ISqlSugarClient, T)"/>
_sqlSugarClient.InsertWithDiffLog(entity);

// <inheritdoc cref="SqlSugarExtensions.UpdateWithDiffLogAsync{T}(ISqlSugarClient, T, bool)"/>
await _sqlSugarClient.UpdateWithDiffLogAsync(entity);

自定义差异日志实现

// <inheritdoc cref="SqlSugarSetup.AddRestFulLogSetup{TFilterType}"/>
public class MyRestFulLog : RestFulLog
{
    private readonly ILogger<MyRestFulLog> _logger;

    public MyRestFulLog(ILogger<MyRestFulLog> logger) => _logger = logger;

    public override void DiffLog(DbType dbType, DiffLogModel diffLogData)
    {
        // 写入结构化日志而非 Console
        _logger.LogInformation("差异日志 {DiffType}: {Sql}",
            diffLogData.DiffType,
            UtilMethods.GetSqlString(dbType, diffLogData.Sql, diffLogData.Parameters));
    }

    public override void SugarExecutingLog(string sql, SugarParameter[] pars, string fullSql)
    {
        _logger.LogDebug("SQL 执行: {FullSql}", fullSql);
    }

    public override void SugarErrorLog(DbType dbType, string sql, SugarParameter[] pars, string message)
    {
        _logger.LogError("SQL 异常 [{DbType}]: {Message}\n{Sql}", dbType, message,
            UtilMethods.GetSqlString(dbType, sql, pars));
    }
}

// 注册自定义日志
builder.Services.AddRestFulLogSetup<MyRestFulLog>();

性能提示: RestFulLog.DiffLogString 中通过双层嵌套循环比较 afterDatabeforeData 的每一列,时间复杂度为 O(n*m)。在列数较多的表(>50 列)上启用差异日志可能对性能产生可感知的影响。


场景七:连接字符串 SM2 加密

// <inheritdoc cref="DbConnectionConfig.IsEncrypt"/>
var config = new DbConnectionConfig
{
    ConfigId = "1300000000001",
    DbType = SqlSugar.DbType.MySql,
    ConnectionString = "加密后的连接字符串(Base64)",
    IsEncrypt = true,                              // 启用 SM2 解密
    DbSecurity = "your-sm2-private-key"            // SM2 私钥
};

// 内部自动调用:
// string dbString = SMEncryption.SM2Decrypt(item.ConnectionString, item.DbSecurity);
// connection.ConnectionString = dbString;

⚠️ 安全提示: DbSecurity 密钥切勿硬编码在代码中。建议通过环境变量、密钥管理服务(如 Azure Key Vault)或加密的配置文件注入。


场景八:敏感数据国密加密

对身份证、手机号等敏感字段做透明加解密:数据库存 SM4 密文,实体操作保持明文,读写自动转换。基于 SqlSugar ISugarDataConverter 自定义转换器实现,算法由配置控制(支持 SM4-CBC / SM4-ECB)。

写入: 实体.IdCard=明文 → SensitiveDataConverter → SM4CBC加密 → hex密文 → INSERT
读取: SELECT hex密文 → SensitiveDataConverter → SM4CBC解密 → 实体.IdCard=明文
查询: Where(x => x.IdCard == v) → 参数自动加密 → 密文比对 ✅
1. 配置 appsettings.json
"SensitiveData": {
  "Algorithm": "SM4CBC",          // 或 "SM4ECB"
  "Key":  "16字节密钥的32位hex",   // SM4GenerateKey(true) 生成
  "IV":   "16字节IV的32位hex",     // SM4GenerateIV(true) 生成;ECB 模式忽略
  "HexFormat": true
}

⚠️ 安全提示: 生产环境密钥切勿写入仓库,建议通过环境变量或密钥管理服务(KMS)注入。

2. 实体标注
using RuoVea.ExSugar.TypeHandlers;

public class SysUser : EntityBase
{
    [SugarColumn(ColumnDataType = "varchar(200)", SqlParameterDbType = typeof(SensitiveDataConverter))]
    public string IdCard { get; set; }   // 身份证:库中存密文,实体用明文

    [SugarColumn(ColumnDataType = "varchar(200)", SqlParameterDbType = typeof(SensitiveDataConverter))]
    public string Phone { get; set; }    // 手机号
}
3. 业务代码 —— 无感知使用
// 写入(实体填明文即可)
db.Insert(new SysUser { IdCard = "110101199001011234", Phone = "13800138000" });

// 读取(自动解密为明文)
var user = db.Single<SysUser>(x => x.IdCard == "110101199001011234");
// user.IdCard == "110101199001011234"

// 等值查询可用(参数自动加密后比对)
var hit = db.Single<SysUser>(x => x.Phone == "13800138000");
边界与注意事项
场景 说明
等值查询 / 唯一索引 SM4-CBC 固定 Key+IV(确定性)下可用;ECB 模式同为确定性
模糊查询 LIKE '%xx%' 不可用,密文无法模糊匹配;此类字段不要加密,用列权限 + 脱敏
排序 / 范围比较 对密文无意义,业务上避免
历史明文数据 转换器解密失败自动回退原值,不中断业务;需写一次性迁移脚本(读出→加密→回写)
切换算法 切到新算法后旧密文无法解密,必须配套迁移脚本
差异日志 EnableDiffLogEvent 记录的是密文,不会泄露明文

⚙️ 配置选项详解

DbConnectionConfig

参数 类型 默认值 说明
IsAutoCloseConnection bool true 操作完成后自动关闭数据库连接
EnableDiffLog bool false 是否启用差异日志(Insert/Update/Delete 前后对比)
EnableUnderLine bool false 是否启用驼峰转下划线(CreateTimecreate_time
IsEncrypt bool false 连接字符串是否经过 SM2 加密
DbSecurity string null SM2 解密私钥(IsEncrypt=true 时必填)
IsDeleteFilter bool true 是否启用软删除过滤(实体需实现 IDeletedEntity
IsUserIdFilter bool false 是否启用用户 Id 过滤(实体需实现 ICreatorFilter 或继承 EntityBase
IsTenantIdFilter bool false 是否启用租户 Id 过滤(实体需实现 ITenantIdFilter
CommandTimeOut int 30 SQL 命令超时时间(秒)

⚠️ CommandTimeOut 单位是而非毫秒。默认 30 秒对于大多数 OLTP 操作足够,但对于复杂报表查询可能需要调大。

DataAuditing 审计字段映射

属性 默认值 说明
CreateTime "CreateTime" 创建时间字段名(驼峰)
ModifyTime "ModifyTime" 修改时间字段名(驼峰)
Creator "Creator" 创建者字段名(驼峰)
Modifier "Modifier" 修改者字段名(驼峰)
TenantId "TenantId" 租户 Id 字段名(驼峰)
IsDelete "IsDelete" 软删除标记字段名(驼峰)

DbInitConfig

参数 类型 默认值 说明
InitTable bool true 是否自动初始化数据库表结构(CodeFirst)
InitSeedData bool true 是否自动初始化种子数据

SqlSugarConst 常量

常量 配置键 默认值 说明
MainConfigId SqlSugarConst:MainConfigId "1300000000001" 默认主库标识
LogConfigId SqlSugarConst:LogConfigId "1300000000002" 默认日志库标识
PrimaryKey SqlSugarConst:PrimaryKey "Id" 默认主键名
DefaultTenantId SqlSugarConst:DefaultTenantId 1300000000001 默认租户 Id

🏗️ 实体基类体系

EntityBaseId                     AutoKeyBase
├─ Id : long (雪花Id)            ├─ Id : int (自增)
└─ Check()                       └─ [SugarColumn(IsIdentity=true)]

      ↓                                ↓
EntityBase : EntityBaseId         AutoKeyEntityBase : AutoKeyBase
├─ CreateTime : DateTime?         ├─ CreateTime : DateTime?
├─ ModifyTime : DateTime?         ├─ ModifyTime : DateTime?
├─ Creator : long?                ├─ Creator : long?
├─ Modifier : long?               ├─ Modifier : long?
├─ IsDelete : IsDelete?           ├─ IsDelete : IsDelete?
├─ IDeletedEntity                 ├─ IDeletedEntity
└─ IAuditableEntity               └─ IAuditableEntity

      ↓
EntityTenant : EntityBase         EntityTenantId : EntityBaseId
├─ TenantId : long?               ├─ TenantId : long?
├─ CheckTenantId()                ├─ CheckTenantId()
└─ ITenantEntity                  └─ ITenantEntity
基类 主键类型 审计字段 租户 软删除 适用场景
EntityBaseId long (雪花Id) -- -- -- 纯主键实体
EntityBase long (雪花Id) -- 推荐 标准业务实体
EntityTenant long (雪花Id) 多租户业务实体
EntityTenantId long (雪花Id) -- -- 仅需租户隔离的实体
AutoKeyBase int (自增) -- -- -- 纯自增主键实体
AutoKeyEntityBase int (自增) -- 自增主键 + 审计的实体

⚠️ 已知问题: EntityBaseData(数据权限实体基类,含 CreateOrgId)和 IOrgIdFilter 接口已完全注释掉,如需数据权限过滤需自行实现。

实体接口

接口 成员 说明
IDeletedEntity IsDelete 软删除标记
IAuditableEntity CreateTime, ModifyTime, Creator, Modifier 审计字段
ITenantEntity TenantId 租户隔离
ICreatorFilter Creator 创建者过滤
ITenantIdFilter TenantId 租户过滤
ICustormerEntityFilter AddEntityFilter() 自定义过滤器扩展(返回 IEnumerable<TableFilterItem<object>>

📐 SQL 扩展方法

所有扩展方法均基于 SqlSugarClient(非 ISqlSugarClient),需在 using RuoVea.ExSugar; 后可用。

查询方法

方法 签名 说明
Count Count<T>(Expression<Func<T,bool>>) 获取总数
CountAsync CountAsync<T>(Expression<Func<T,bool>>) 异步总数
Any Any<T>(Expression<Func<T,bool>>) 是否存在
AnyAsync AnyAsync<T>(Expression<Func<T,bool>>) 异步是否存在
Single Single<T>(dynamic Id) 主键查询
Single Single<T>(Expression<Func<T,bool>>) 条件单条
SingleAsync SingleAsync<T>(Expression<Func<T,bool>>) 异步条件单条
FirstOrDefault FirstOrDefault<T>(Expression<Func<T,bool>>) 条件首条
FirstOrDefaultAsync FirstOrDefaultAsync<T>(Expression<Func<T,bool>>) 异步条件首条
ToList ToList<T>() 获取全部
ToList ToList<T>(Expression<Func<T,bool>>) 条件列表
ToList ToList<T>(Expression, Expression, OrderByType) 条件 + 排序列表
ToListAsync ToListAsync<T>() 异步全部
ToListAsync ToListAsync<T>(Expression<Func<T,bool>>) 异步条件列表
ToListAsync ToListAsync<T>(Expression, Expression, OrderByType) 异步条件 + 排序
AsQueryable AsQueryable<T>() / AsQueryable<T>(predicate) 返回 ISugarQueryable<T>
AsEnumerable AsEnumerable<T>() / AsEnumerable<T>(predicate) 返回 List<T>
AsAsyncEnumerable AsAsyncEnumerable<T>() / AsAsyncEnumerable<T>(predicate) 异步返回 List<T>
IsExists IsExists<T>(Expression) 表级存在判断
IsExistsAsync IsExistsAsync<T>(Expression) 异步表级存在判断
IsTableExists IsTableExists<T>(string tableName) 判断数据表是否存在

写入方法

方法 签名 说明
Insert Insert<T>(T entity, bool IgnoreColumns=true) 插入单条
Insert Insert<T>(bool IgnoreColumns, params T[]) 插入多条
Insert Insert<T>(IEnumerable<T>, bool IgnoreColumns) 插入集合
InsertAsync InsertAsync<T>(T entity, bool IgnoreColumns) 异步插入单条
InsertAsync InsertAsync<T>(bool IgnoreColumns, params T[]) 异步插入多条
InsertAsync InsertAsync<T>(IEnumerable<T>, bool IgnoreColumns) 异步插入集合
InsertReturnIdentity InsertReturnIdentity<T>(T, bool) 插入并返回自增 Id (int)
InsertReturnIdentityAsync InsertReturnIdentityAsync<T>(T, bool) 异步插入并返回自增 Id (long)
Update Update<T>(T entity, bool IgnoreColumns, string title) 更新单条
Update Update<T>(bool IgnoreColumns, string title, params T[]) 更新多条
Update Update<T>(IEnumerable<T>, bool IgnoreColumns, string title) 更新集合
UpdateAsync UpdateAsync<T>(T entity, bool IgnoreColumns, string title) 异步更新单条
UpdateAsync UpdateAsync<T>(bool IgnoreColumns, string title, params T[]) 异步更新多条
UpdateAsync UpdateAsync<T>(IEnumerable<T>, bool IgnoreColumns, string title) 异步更新集合
UpdateNoPrimaryKey UpdateNoPrimaryKey<T>(T, Expression columns, bool, string) 无主键更新
UpdateNoPrimaryKeyAsync UpdateNoPrimaryKeyAsync<T>(T, Expression columns, bool, string) 异步无主键更新
Delete Delete<T>(T entity, string title) 删除实体
Delete Delete<T>(object key, string title) 主键删除
Delete Delete<T>(params object[] keys) 批量主键删除
Delete Delete<T>(Expression<Func<T,bool>>, string title) 条件删除
DeleteAsync DeleteAsync<T>(T entity, string title) 异步删除
DeleteAsync DeleteAsync<T>(object key, string title) 异步主键删除
DeleteAsync DeleteAsync<T>(params object[] keys) 异步批量删除
DeleteAsync DeleteAsync<T>(Expression<Func<T,bool>>, string title) 异步条件删除

假删除(软删除)

// <inheritdoc cref="SqlSugarExtensions.FakeDelete{T}(ISqlSugarClient, T)"/>
// 实体必须继承 EntityBase
_sqlSugarClient.FakeDelete(entity);
// 效果: UPDATE xxx SET IsDelete='Y', ModifyTime=NOW(), Modifier=@userId WHERE Id=@id

// 或通过仓储调用
_repo.FakeDelete(entity);

// 异步版本
await _sqlSugarClient.FakeDeleteAsync(entity);
await _repo.FakeDeleteAsync(entity);

差异日志写入

方法 说明
UpdateWithDiffLog<T>(T, bool) 更新并记录变更前后数据
UpdateWithDiffLogAsync<T>(T, bool) 异步更新并记录差异
InsertWithDiffLog<T>(T) 插入并记录差异
InsertWithDiffLogAsync<T>(T) 异步插入并记录差异

排序构建器

// <inheritdoc cref="SqlSugarExtensions.OrderBuilder{T, Input}(ISugarQueryable{T}, Input, string, bool)"/>
var query = _sqlSugarClient.AsQueryable<Order>()
    .OrderBuilder(pageParam, defualtSortField: "Id", descSort: true);
// 自动从 pageParam.Sidx / pageParam.Sord 读取排序规则

📄 分页查询

ToPageAsync(返回 PageResult)

// <inheritdoc cref="PagedExtensions.ToPageAsync{T}(ISugarQueryable{T}, int, int, bool)"/>
// 基础分页
var result = await _sqlSugarClient.AsQueryable<Order>()
    .ToPageAsync(pageIndex: 1, pageSize: 20);
// result.Rows, result.TotalRows, result.PageIndex, result.PageSize

// <inheritdoc cref="PagedExtensions.ToPageAsync{T, Dto}(ISugarQueryable{T}, int, int)"/>
// 分页 + DTO 映射(自动 Mapster 转换)
var dtoResult = await _sqlSugarClient.AsQueryable<Order>()
    .ToPageAsync<Order, OrderDto>(pageIndex: 1, pageSize: 20);

// <inheritdoc cref="PagedExtensions.ToPageAsync{T}(ISugarQueryable{T}, PageParam)"/>
// 从 PageParam 读取分页参数
var result = await _sqlSugarClient.AsQueryable<Order>()
    .ToPageAsync(new PageParam { PageNo = 1, PageSize = 20 });

ToPagedList / ToPagedListAsync(返回 SqlSugarPagedList)

// <inheritdoc cref="SqlSugarPagedExtensions.ToPagedList{TEntity}(ISugarQueryable{TEntity}, int, int)"/>
var paged = _sqlSugarClient.AsQueryable<Order>().ToPagedList(1, 20);
// paged.Page, paged.PageSize, paged.Total, paged.TotalPages
// paged.Items, paged.HasPrevPage, paged.HasNextPage

// <inheritdoc cref="SqlSugarPagedExtensions.ToPagedListAsync{TEntity}(ISugarQueryable{TEntity}, int, int)"/>
var paged = await _sqlSugarClient.AsQueryable<Order>().ToPagedListAsync(1, 20);

SugarRepository 分页

// <inheritdoc cref="SugarRepository{T}.GetPageResultAsync"/>
var (list, total, totalPage) = await _repo.GetPageResultAsync(
    where: u => u.IsDelete == IsDelete.N,
    strWhere: "Amount > 100",           // 可选:原生 SQL 条件
    pageNo: 1,
    pageSize: 20,
    order: u => u.CreateTime,
    orderEnum: SordEnum.Desc
);

🧵 线程安全与生命周期

组件 生命周期 线程安全 说明
ISqlSugarClient Singleton ✅ 是 SqlSugarCore 内部自行管理线程安全,无需担心并发
SugarRepository<T> Scoped (默认) ✅ 是 默认跟随请求作用域,可通过 serviceLifetime 参数调整
DbContext Scoped (默认) ⚠️ 非线程安全 内部持有 SqlSugarClient 实例引用,单例注册会导致并发问题
IRestFulLog Singleton ⚠️ 视实现而定 默认 RestFulLog 使用 Console.Write,无共享状态
UnitOfWorkFilter Scoped ✅ 是 ASP.NET Core Filter 天然按请求隔离
IHttpContextAccessor Singleton ✅ 是 ASP.NET Core 内置线程安全

重要: ISqlSugarClient 以 Singleton 注册,与传统的 Scoped DbContext 模式不同。这是故意的设计选择 —— SqlSugar 的 SqlSugarScope 内部使用线程静态或 AsyncLocal 来隔离不同请求的数据库操作。不要将 ISqlSugarClient 注册改为 Scoped,这会导致 SugarRepository<T> 构造函数注入失败。


⚠️ 已知问题与注意事项

严重

问题 影响 解决方案
DataAuditing 默认值需按实际列名配置 审计字段默认按驼峰(CreateTime/TenantId 等)映射,若数据库为下划线等自定义列名需在配置中覆写 appsettings.json 中按需覆写 "DataAuditing": { ... }
EntityBaseData / IOrgIdFilter 已注释 数据权限(按部门过滤)功能不可用 需自行实现相关过滤逻辑
SqlSugarFilter 整体注释 机构范围过滤器(SetOrgEntityFilter)和自定义实体过滤器(SetCustomEntityFilter)不可用 如需使用,取消注释并适配当前版本
DbContext DbConnectionConfig 构造函数 Bug 第 64 行 SugarUtil.SqlSugarClientUtil(db, config, ...) 传入了未赋值的 db 字段而非局部变量 _db 使用 ConnectionConfig 重载或自行修复

一般

问题 影响 解决方案
SetDbConfig 中 column.IsIgnore 仅处理 TenantId 只根据 IsTenantIdFilter 控制 TenantId 字段的 IsIgnore,不对其他字段做动态忽略 其他字段的忽略逻辑自行通过 ConfigSugarColumn 特性控制
IgnoreColumns=true 参数实际含义 Insert/Update 方法的 IgnoreColumns=true 实际上是 ignoreAllNullColumns: true,即忽略值为 null 的列,而非忽略所有列 注意参数语义,确保传入非 null 的字段才会被写入
EnableUnderLine 依赖 UtilMethods.ToUnderLine 驼峰转下划线依赖 SqlSugar 内置方法,行为跟随 SqlSugar 版本变化 升级 SqlSugar 时注意兼容性

性能建议

建议 说明
避免全局事务 isAllUnitOfWork: true 会让所有请求(包括 GET)走事务,建议仅在写操作上使用 [UnitOfWork] 特性
批量操作使用集合重载 Insert(IEnumerable<T>) / Update(IEnumerable<T>) 比逐个调用高效,减少 AOP 回调次数
按需启用差异日志 EnableDiffLog = trueDiffLogString 有 O(n*m) 列比较开销,仅对审计敏感的表启用
非租户实体添加 [SystemTable] SugarRepository<T> 构造函数会对有 SystemTableAttribute 的实体跳过特殊初始化逻辑

🗺️ 版本迁移指南

从原生 SqlSugarCore 迁移

旧代码模式 迁移到 RuoVea.ExSugar
new SqlSugarClient(connectionConfig) 手动管理 services.AddSqlSugarSetup() DI 注入 ISqlSugarClient
手动设置 entity.CreateTime = DateTime.Now 继承 EntityBase → AOP 自动填充
db.Queryable<T>().Where(u => u.IsDelete == N) 每次手写 配置 IsDeleteFilter = true → 全局自动过滤
db.Ado.BeginTran() 手动事务 [UnitOfWork] 特性或 UnitOfWorkFilter
明文连接字符串 IsEncrypt = true + SM2 加密

v8.0.x → v10.0.x

  • API 无破坏性变化。v10.0.x 仅在 net10.0 TFM 上编译,所有公开 API 签名保持一致。
  • Mapster 版本从 7.4.0 升级到 10.0.7,如需自定义 TypeAdapterConfig,请参考 Mapster 10.x 迁移指南。
  • Microsoft.Extensions.DependencyModel 从 8.0.* 升级到 10.0.*,行为无变化。

📄 License

MIT License © RuoVea


🔗 相关资源: NuGet Gallery · 问题反馈 · Gitee 仓库

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (14)

Showing the top 5 NuGet packages that depend on RuoVea.ExSugar:

Package Downloads
RuoVea.OmiApi.Config

系统配置管理

RuoVea.OmiApi.UserRoleMenu

用户角色菜单管理

RuoVea.OmiApi.SystemApp

系统应用管理

RuoVea.OmiApi.UserRole

用户角色管理

RuoVea.OmiApi.User

用户管理模块

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.0.1.2 82 8/15/2026
10.0.1.1 97 8/15/2026
10.0.1 118 8/14/2026
10.0.0.9 819 7/15/2026
10.0.0.8 492 6/24/2026
10.0.0.7 504 5/28/2026
9.0.0.13 139 5/28/2026
8.0.1.2 99 8/15/2026
8.0.1.1 99 8/15/2026
8.0.1 123 8/14/2026
8.0.0.35 1,464 7/15/2026
8.0.0.34 920 6/24/2026
8.0.0.33 959 5/28/2026
8.0.0.32 923 3/23/2026
7.0.0.33 597 5/28/2026
7.0.0.32 884 3/23/2026
6.0.18.33 749 5/28/2026
6.0.18.32 1,089 3/23/2026
5.0.1.20 113 5/28/2026
5.0.1.19 127 3/23/2026
Loading failed