RuoVea.ExSugar 10.0.0.8

dotnet add package RuoVea.ExSugar --version 10.0.0.8
                    
NuGet\Install-Package RuoVea.ExSugar -Version 10.0.0.8
                    
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.0.8" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RuoVea.ExSugar" Version="10.0.0.8" />
                    
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.0.8
                    
#r "nuget: RuoVea.ExSugar, 10.0.0.8"
                    
#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.0.8
                    
#: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.0.8
                    
Install as a Cake Addin
#tool nuget:?package=RuoVea.ExSugar&version=10.0.0.8
                    
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
 │                                               分页扩展     │
 ├─────────────────────────────────────────────────────────┤
 │  国际化: 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 的默认值是 "CreateTime"(疑似 Bug),务必在配置文件中显式覆写为 "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)或加密的配置文件注入。


⚙️ 配置选项详解

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 ⚠️ "CreateTime" BUG: 默认值错误,应为 "TenantId"
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.TenantId 默认值错误 构造函数中 TenantId = "CreateTime" 而非 "TenantId",导致多租户场景下租户字段映射失效 务必在 appsettings.json 中显式覆写 "DataAuditing": { "TenantId": "TenantId" }
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 (13)

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.0.8 419 6/24/2026
10.0.0.7 495 5/28/2026
10.0.0.6 388 3/23/2026
10.0.0.5 303 1/27/2026
9.0.0.13 125 5/28/2026
9.0.0.11 1,132 1/27/2026
9.0.0.10 134 1/26/2026
8.0.0.34 808 6/24/2026
8.0.0.33 950 5/28/2026
8.0.0.32 918 3/23/2026
8.0.0.31 726 1/27/2026
7.0.0.33 585 5/28/2026
7.0.0.32 878 3/23/2026
7.0.0.31 961 1/27/2026
6.0.18.33 684 5/28/2026
6.0.18.32 1,081 3/23/2026
6.0.18.31 1,090 1/27/2026
5.0.1.20 99 5/28/2026
5.0.1.19 117 3/23/2026
5.0.1.18 127 1/27/2026
Loading failed