Aore.SqlBuilder 1.2.3

dotnet add package Aore.SqlBuilder --version 1.2.3
                    
NuGet\Install-Package Aore.SqlBuilder -Version 1.2.3
                    
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="Aore.SqlBuilder" Version="1.2.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Aore.SqlBuilder" Version="1.2.3" />
                    
Directory.Packages.props
<PackageReference Include="Aore.SqlBuilder" />
                    
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 Aore.SqlBuilder --version 1.2.3
                    
#r "nuget: Aore.SqlBuilder, 1.2.3"
                    
#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 Aore.SqlBuilder@1.2.3
                    
#: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=Aore.SqlBuilder&version=1.2.3
                    
Install as a Cake Addin
#tool nuget:?package=Aore.SqlBuilder&version=1.2.3
                    
Install as a Cake Tool

Aore.SqlBuilder

多数据库方言 SQL 构建器核心库:把实体对象与强类型 Lambda 表达式翻译成可执行、全参数化的 SQL。纯 BCL 实现,零 NuGet 依赖,可直接配合 Dapper、手写 DbCommand 或任何 ADO.NET 执行方式使用。

using Aore.SqlBuilder;

var sql = Sql.Select<User>()
    .Where(u => u.Age >= 18)
    .WhereIf(!string.IsNullOrEmpty(keyword), u => u.UserName.Contains(keyword))
    .OrderByDescending(u => u.CreateTime)
    .Page(1, 20)
    .ToSql(SqlDialect.MySql);

生成的 SQL(MySQL,值全部参数绑定):

SELECT t0.* FROM `Users` t0
WHERE ((t0.`Age` >= @p0) AND (t0.`UserName` LIKE @p1))
ORDER BY t0.`CreateTime` DESC LIMIT 20 OFFSET 0

特性一览

  • 链式 API:Sql.Select<T>()...ToSql(dialect) 一行出 SQL,构建器方法全部返回自身支持链式
  • 表达式树翻译:Where / 投影 / 排序 / 分组 / HAVING 全部支持强类型 Lambda,编译期可查错
  • 六种内置方言:SQL Server、MySQL、Oracle、PostgreSQL、SQLite、Firebird,可注册自定义方言(如达梦)
  • 全参数化防注入:所有外部值一律绑定 @p0… 参数;标识符经方言引号转义;LIKE 通配符自动转义
  • 防全表保护:Update / Delete 缺 WHERE 默认拒绝执行
  • SqlResult 不可变产物:可跨线程共享、可重复执行、可日志打印
  • 多目标框架:netstandard2.0 / net462 / net472 / net48 / net8.0 / net9.0 / net10.0

本库只负责"构建与渲染"。要把 SqlResult 直接跑起来(参数转换、执行、分页、自增回读),请配套使用 Aore.SqlBuilder.Extensions(基于 Dapper),或自行读取 SqlResult.Sql + SqlResult.Parameters 交给任意执行层。

安装

dotnet add package Aore.SqlBuilder

快速上手

1. 定义实体(约定优先,特性可选)

[SqlTable("SYS_USER", Schema = "APP")]     // 表名/模式(未标注 = 类名按命名策略)
public class User
{
    [SqlKey(IsIdentity = true)]            // 主键 + 自增(复合主键可多处标注)
    public long Id { get; set; }

    [SqlColumn("USER_NAME")]               // 显式列名(未标注 = 属性名按命名策略)
    public string UserName { get; set; } = null!;

    [SqlIgnoreUpdate]                      // 更新跳过(创建人只写一次)
    public string? CreatedBy { get; set; }

    [SqlIgnoreInsert]                      // 插入跳过(触发器维护)
    public DateTime? RowVersion { get; set; }

    [SqlNotMapped]                         // 不参与任何 SQL
    public string? Temp { get; set; }
}

可用特性:[SqlTable] [SqlColumn] [SqlKey] [SqlIdentity] [SqlIgnoreInsert] [SqlIgnoreUpdate] [SqlNotMapped];同时兼容 DataAnnotations 的 [Table] [Column] [Key] [NotMapped](按特性全名探测)。约定:无显式主键时,Id 或 类型名+Id(int/long/Guid)自动识别为主键,int/long 视为自增。

2. 查询

// 条件 / 动态条件 / 排序 / 分页(页码从 1 起)
var query = Sql.Select<User>()
    .Where(u => u.Age >= 18)
    .WhereIf(minAge.HasValue, u => u.Age >= minAge!.Value)
    .OrderByDescending(u => u.CreateTime)
    .Page(1, 20)
    .ToSql(SqlDialect.MySql);

// 投影:匿名对象自动 AS 别名(开启 SnakeCase 后列名 user_name,别名保证映射)
var proj = Sql.Select<User>()
    .Select(u => new { u.Id, u.UserName })
    .ToSql(SqlDialect.MySql);

// 函数 / 三目 / ?? 混用,函数经 SqlF
var agg = Sql.Select<Order>()
    .Select(o => new
    {
        UserId = o.UserId,
        Cnt    = SqlF.Count(o.Id),
        Total  = SqlF.Sum(o.Amount),
        Level  = o.Amount >= 100 ? "大额" : "小额",
    })
    .GroupBy(o => o.UserId)
    .Having(o => SqlF.Count(o.Id) > 1)
    .ToSql(SqlDialect.MySql);

// JOIN + 跨实体投影(跨实体 lambda 参数须显式标类型)
var joined = Sql.Select<User>()
    .Join<Order>((u, o) => u.Id == o.UserId)          // INNER JOIN;另有 LeftJoin/RightJoin/FullJoin/CrossJoin
    .Where((User u, Order o) => o.Amount > 50)
    .Select((User u, Order o) => new { u.UserName, o.Amount })
    .ToSql(SqlDialect.MySql);

// IN / IN 子查询 / BETWEEN / EXISTS
var adv = Sql.Select<User>()
    .Where(u => ids.Contains(u.Id))                                      // IN (@p0, @p1, …)
    .WhereIn(u => u.Id, Sql.Select<Order>().Select(o => o.UserId))       // IN (SELECT …)
    .WhereBetween(u => u.Age, 18, 30)
    .Where(u => Sql.Exists(Sql.Select<Order>().Where(o => o.UserId == u.Id)))
    .ToSql(SqlDialect.MySql);

3. 插入

// 单实体:INSERT INTO `Users` (`UserName`, `Age`, …) VALUES (@p0, @p1, …)
var insert = Sql.Insert(user).ToSql(SqlDialect.MySql);

// 忽略 null 列(交给数据库默认值)/ 限定插入列 / 显式写自增列
var flex = Sql.Insert(user).IgnoreNullColumns().Columns(u => new { u.UserName, u.Age }).ToSql(SqlDialect.MySql);

// 回读自增主键(MySQL 追加 SELECT LAST_INSERT_ID();PG RETURNING;Oracle RETURNING INTO 输出参数)
var withId = Sql.Insert(user).ReturnId().ToSql(SqlDialect.MySql);

// 批量插入:多行 VALUES,按参数上限(MaxParameters,默认 2000)自动分片
var batch = Sql.InsertRange(userList).IgnoreNullColumns().ToSql(SqlDialect.MySql);
IReadOnlyList<SqlResult> statements = Sql.InsertRange(userList).ToSqlBatch(SqlDialect.MySql);

4. 更新(缺 WHERE 默认拒绝执行)

// 表达式精确修改(推荐):SET `LoginCount` = @p0, `Balance` = (`Balance` - @p1)
var update = Sql.Update<User>()
    .Set(u => u.LoginCount, 1)
    .SetExpr(u => u.Balance, u => u.Balance - 100)
    .Increment(u => u.LoginCount, 1)          // 数据库端自增,并发安全
    .Where(u => u.Id == id)
    .ToSql(SqlDialect.MySql);

// 实体全字段更新(跳过主键/自增/IgnoreUpdate/null)+ 主键定位
var byEntity = Sql.UpdateBy(user).ToSql(SqlDialect.MySql);

// 确需全表更新时显式放行
var all = Sql.Update<User>().Set(u => u.IsActive, true).AllowFullTable().ToSql(SqlDialect.MySql);

5. 删除

var delete = Sql.Delete<User>().Where(u => u.LastLogin < cutoff).ToSql(SqlDialect.MySql);
var byKey  = Sql.Delete<User>().WhereKeyOf(user).ToSql(SqlDialect.MySql);   // 按主键定位
// 同样缺 WHERE 拒绝执行;AllowFullTable() 显式放行

6. Upsert(渲染由方言决定)

var upsert = Sql.Upsert(user)              // 冲突列默认 = 主键
    .OnKey(u => new { u.UserName })        // 或指定唯一索引列
    .IgnoreNullColumns()                   // null 列不插入、冲突时不覆盖
    .UpdateColumns(u => new { u.Age })     // 冲突时更新的列(默认全部非键列)
    .ToSql(SqlDialect.MySql);
// MySQL → ON DUPLICATE KEY UPDATE;PG/SQLite → ON CONFLICT DO UPDATE;
// SQL Server / Oracle / Firebird → MERGE

7. 插入自查询

var source = Sql.Select<User>().Where(u => u.Age >= 26);
var archive = Sql.InsertFrom<Archive, User>(source)
    .Columns(a => new { a.UserName, a.Age })      // 目标列
    .FromColumns(u => new { u.UserName, u.Age })  // 源列(一一对应、顺序一致)
    .ToSql(SqlDialect.MySql);

入口类 Sql 全量方法

方法 用途
Sql.Select<T>() SELECT 构建器
Sql.Insert(entity) / Sql.InsertRange(list) 单实体 / 批量插入(批量自动分片)
Sql.Update<T>() / Sql.UpdateBy(entity) 更新构建器 / 快捷全字段更新 + 主键 WHERE
Sql.Delete<T>() 删除构建器(缺 WHERE 拒绝执行)
Sql.Upsert(entity) 存在则更新 / 不存在则插入
Sql.InsertFrom<T, TSource>(source) INSERT INTO … SELECT
Sql.Raw(sql, params) 原生片段:@name 占位自动换方言前缀并绑定参数;严禁把用户输入直接拼进 sql 文本
Sql.Truncate<T>(dialect) TRUNCATE TABLE(SQLite 自动降级 DELETE FROM)
Sql.Batch(dialect, builders[]) 多语句合并:同一上下文渲染(参数连续编号、别名不冲突),以 ;\n 连接
Sql.ExistsOf(builder, dialect) 生成 SELECT CASE WHEN EXISTS (…) THEN 1 ELSE 0 END 探测语句
Sql.Exists(sub) / Sql.ExistsNot(sub) 仅限在 Where/投影表达式树内使用(直接调用抛 InvalidOperationException)

查询构建器 SelectBuilder 常用 API

分类 方法
投影 Select() 全列 / Select(u => …) 单列或匿名对象 / Select(Sql.Raw(…), "alias") 原生片段 / Select(sub, "alias") 子查询 / 跨实体投影(JOIN 后)
条件 Where / WhereIf / WhereIn / WhereNotIn / WhereBetween / WhereExists / WhereExistsNot / WhereRaw / WhereRawIf
JOIN Join / LeftJoin / RightJoin / FullJoin(MySQL 不支持)/ CrossJoin / JOIN 子查询(别名必填)/ WithNoLock(仅 SQL Server,其他方言忽略)
分组 GroupBy(匿名对象 = 多列)/ Having / HavingRaw / HavingBetween / HavingNull / HavingNotNull / HavingTrue / HavingFalse
排序分页 OrderBy / OrderByDescending / OrderByRaw / OrderByRandom / Page(页码, 页大小) / Limit / Offset / Distinct
CTE With("名称", 子查询) / WithRaw / WithValues(Ad-hoc VALUES 表)/ Table("名称") 引用 CTE
集合操作 Union / UnionAll / Except(All) / Intersect(All) / UnionRaw,之后再 OrderBy/Page 作用于合并结果
杂项 Table("表名", "模式") 覆盖表名(分表场景)
输出 ToSql(dialect) → SqlResult;ToCountSql(dialect) → 自动剥离排序分页的 Count 语句

SqlF · SQL 函数(仅限表达式树内使用)

SqlF.Count() SqlF.Count(x) SqlF.Sum SqlF.Avg SqlF.Min SqlF.Max SqlF.Length SqlF.Upper SqlF.Lower SqlF.Trim SqlF.SubString(0 基自动换 1 基)SqlF.Replace SqlF.Concat SqlF.Coalesce SqlF.NullIf SqlF.Abs SqlF.Round SqlF.Now —— 具体函数名由方言翻译(如 Now() 在 SQL Server 渲染 GETDATE()、Oracle 渲染 SYSDATE)。

表达式翻译支持(节选)

C# 写法 SQL 形态
u.Age > 18、u.Name == "x" (t0.Age > @p0)、(t0.Name = @p0)
u.CreatedAt == null / != null IS NULL / IS NOT NULL
u.IsActive(bool 列) (t0.IsActive = 1)(PG:TRUE)
!cond / a && b / a \|\| b NOT (…) / (… AND …) / (… OR …)
u.Nick ?? u.Name COALESCE(…)
cond ? x : y CASE WHEN … THEN … ELSE … END(嵌套自动扁平化)
s.Contains("x") / StartsWith / EndsWith LIKE '%x%' / 'x%' / '%x'(通配符转义)
s.ToUpper() / ToLower() / Trim() / Replace() / Substring() / s.Length UPPER / LOWER / TRIM / REPLACE / SUBSTRING(0 基→1 基)/ LEN·CHAR_LENGTH·LENGTH
string.IsNullOrEmpty(s) (s IS NULL OR s = '')
ids.Contains(u.Id) IN (@p…);空集合 → (1 = 0) 防全表
枚举比较 底层数值绑定
闭包变量 / 静态属性 / 本地方法结果 客户端求值后绑定参数

不支持的表达式(自由表达式、未映射属性)构建期抛 SqlBuilderException 并给出修复指引;可改用 WhereRaw 或先在客户端求值。

SqlResult · 构建产物

ToSql() 返回不可变的 SqlResult,可跨线程共享、重复执行:

成员 说明
Sql SQL 文本(含方言引号与参数前缀);ToString() 即返回它,调试打印友好
Parameters IReadOnlyList<SqlParameter>(Name 不含前缀 / Value / IsOutput)
Dialect 生成时所用方言
HasOutputParameters 是否含输出参数(Oracle RETURNING INTO)
ToDictionary() 参数字典(不含输出参数),可直接传 Dapper

全局配置 SqlOptions

// 进程级默认:仅启动期调用一次(Default 是只读快照,直接修改会抛异常)
SqlOptions.SetDefault(new SqlOptions
{
    NamingPolicy = SqlNamingPolicy.SnakeCase,      // AsIs(默认)/SnakeCase/CamelCase/Lowercase/Uppercase
    IdentifierQuoting = SqlIdentifierQuoting.All,  // 标识符引号开关:None/Table/Column/All(默认)
    EscapeLikePattern = true,                      // LIKE 通配符转义(默认开)
    MaxParameters = 2000,                          // 批量插入分片阈值(SQL Server 上限 2100 留余量)
    ParameterBaseName = "p",                       // 参数名前缀:p0、p1…
    UseLegacyOraclePaging = false,                 // true = Oracle 11g ROWNUM 分页
});

// 局部覆盖(不影响全局)
var sql = Sql.Select<User>().ToSql(SqlDialect.PostgreSql,
    new SqlOptions { NamingPolicy = SqlNamingPolicy.SnakeCase });

IdentifierQuoting 决定表名/列名渲染时是否加方言引号(引号字符始终由方言决定):Oracle 库建表未加引号时报 ORA-00942/ORA-00904,可设 None 让大小写折叠交还数据库。注意:投影别名、子查询/CTE 名始终加引号;开关不检测保留字(Order/User 等 Table/Class 名裸出会语法错误,须保持 All);裸出前做标识符形状校验。

方言

// 内置实例:SqlServer / MySql / Oracle / PostgreSql / Sqlite / Firebird
var sql = Sql.Select<User>().ToSql(SqlDialect.SqlServer);

// 按名称获取(大小写不敏感,支持别名 mssql / mariadb / postgres / pg / sqlite3 / fb)
var dialect = SqlDialect.Get("pg");

// 自定义方言:继承 DialectBase,最少覆写 Name / ParameterPrefix / QuoteIdentifier / GetFunctionName
public class DmDialect : DialectBase
{
    public override string Name => "DM";
    public override string ParameterPrefix => "?";
    public override string QuoteIdentifier(string name) => "\"" + name.Replace("\"", "\"\"") + "\"";
    public override string GetFunctionName(SqlFunction function) => DefaultFunctionName(function);
}

SqlDialect.Register(new DmDialect());
var dmSql = Sql.Select<User>().ToSql(SqlDialect.Get("DM"));

架构约束:构建器层不写任何方言特判,所有数据库差异收敛在 ISqlDialect / DialectBase —— 分层为 构建意图(Builders)→ 渲染(Dialects)→ 执行(Extensions)。

异常与安全

  • 构建期错误一律抛 Aore.SqlBuilder.Exceptions.SqlBuilderException,消息可直接指导修复(缺 WHERE、未映射属性、空 IN 集合含 null、批量 ReturnId 不支持等)
  • SqlF.* / Sql.Exists 脱离表达式树直接调用抛 InvalidOperationException
  • 安全红线:所有外部值一律参数绑定(@p0…),标识符经方言转义;Sql.Raw 的值必须走参数绑定,严禁字符串拼接用户输入进 SQL

NativeAOT 边界:依赖表达式编译与反射元数据发现,不保证 NativeAOT;裁剪发布需自行保留实体类型。

相关包

  • Aore.SqlBuilder.Extensions:Dapper 执行层扩展(参数转换、同步/异步执行、分页自动 Count、自增回读、事务、批量)
  • 更多文档见源码仓库 docs/ 目录(03-查询构建、04-增删改与Upsert、05-多数据库方言、07-实体映射与特性、12-方法使用手册等)
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 is compatible.  net463 was computed.  net47 was computed.  net471 was computed.  net472 is compatible.  net48 is compatible.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.6.2

  • .NETFramework 4.7.2

    • No dependencies.
  • .NETFramework 4.8

    • No dependencies.
  • .NETStandard 2.0

    • No dependencies.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Aore.SqlBuilder:

Package Downloads
Aore.SqlBuilder.Extensions

Aore.SqlBuilder 的 Dapper 执行扩展:参数转换、同步/异步执行、分页查询(自动 Count)、通用 CRUD 执行器。

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.3 49 9/24/2026