PalORM.PostgreSql 5.6.0

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

<div align="center">

PalORM

面向 Native AOT 的 .NET 11 微 ORM

.NET AOT Version License

</div>

  • .NET 生态唯一完整支持全链路 Native AOT 的 ORM
  • 编译时生成一切——Roslyn 在编译期产出 SQL 构造、参数绑定、对象映射、表结构迁移,运行时零反射、零 IL Emit。Native AOT 全链路验证,原生二进制部署
  • 支持 PostgreSQL / MySQL / SQLite。内置多租户隔离、乐观锁、软删除、审计拦截器、咨询锁、编译时诊断——企业级特性开箱即用,无需样板代码;SQLite 经 SQLite3MC 驱动支持 AES-256 静态加密(连接字符串 Password= 启用,属驱动层能力,见下表)

目录


环境要求

硬件要求

维度 JIT 运行时 Native AOT 编译 说明
CPU 架构 x64 ✓ / ARM64 ✓ x64 ✓(ARM64 未实测) AOT 需指定 RID(-r win-x64 / linux-x64)
内存 256 MB 4 GB(ILC 编译器) AOT 编译器内存消耗高;运行时仅需 ~100 MB
磁盘 50 MB(NuGet 包 + 依赖) 100 MB(含发布产物) PG AOT 产物 61MB / MySQL 47MB / SQLite 26MB
存储 任意 SSD 推荐 ILC 编译大量临时文件,HDD 编译时间 5-10x

软件要求

组件 版本 说明
.NET SDK 11.0.100-preview.6+ global.json 锁定 rollForward: latestMinor
C# 15.0(latest) LangVersion: latest
操作系统 Windows 10+ / Linux / macOS x64 / ARM64
IDE Visual Studio 2026 / Rider / VS Code 需支持 Roslyn 5.6+ 源生成器

数据库兼容性

数据库 版本 驱动 加密
PostgreSQL 14+(推荐 18) Npgsql 10.0.3 SSL/TLS
MySQL 8.0+(推荐 8.4 LTS) MySqlConnector 2.6.2 SSL/TLS
SQLite 3.47+(via SQLite3MC 2.4.0) Microsoft.Data.Sqlite.Core 11.0-p7 ✓ AES-256(驱动层,Password=)

为什么选择这些版本

  • Npgsql 10.0.3:原生支持 PG date/time → DateOnly/TimeOnly、NpgsqlSlimDataSourceBuilder(AOT 友好)、Binary COPY 批量写入
  • MySqlConnector 2.6.2:含安全修复 GHSA-473q(zero-config TLS MitM)、MySqlBulkCopy(LOAD DATA LOCAL INFILE)、VECTOR 类型准备
  • SQLite3MC 2.4.0:内嵌 SQLite + AES-256 加密(连接字符串 Password= 启用静态加密——驱动层透传,PalORM 不解析该参数),PCLRaw 跨平台原生二进制加载

安装


<PackageReference Include="PalORM.PostgreSql" Version="5.6.0" />

<PackageReference Include="PalORM.MySql" Version="5.6.0" />

<PackageReference Include="PalORM.Sqlite" Version="5.6.0" />

每个 Provider 包含 PalORM.Core(运行时)和 PalORM.SourceGen(编译时源生成器)。


快速开始

定义实体

using PalORM;

[Table("users")]
public partial class User
{
    [Key] public long Id { get; set; }
    [Column("email")] public string Email { get; set; } = "";
    [Column("created_at")] public DateTime CreatedAt { get; set; }
    [Column("metadata")]
    [OwnedJson(typeof(UserJsonContext))]
    public UserMetadata? Metadata { get; set; }
}

创建会话

using var db = await DataSession<PostgreSqlProvider>.CreateAsync(new DbOptions
{
    ConnectionString = "Host=localhost;Username=user;Password=xxx;Database=mydb"
});

可选:启动期预热连接池(v5.6.0),让首批突发查询命中暖连接而非各付一次建连(远程建连实测 ~8.5 ms/条):

// 打开 N 条连接随即归还池;SQLite 无池直接返回。建议配合 MinPoolSize 保持暖态
await DataSession<PostgreSqlProvider>.PreWarmAsync(options, count: 10);

CRUD

// 插入
var user = await db.InsertAsync(new User { Email = "alice@example.com", CreatedAt = DateTime.UtcNow });

// 查询
var alice = await db.GetAsync<User>(user.Id);
var all = await db.From<User>().Where($"email LIKE {"%@example.com%"}").ToListAsync();

// 更新
alice.Email = "new@example.com";
await db.UpdateAsync(alice);

// 删除
await db.DeleteAsync<User>(alice.Id);

批量操作

// 批量插入(PG: Binary COPY / MySQL: BulkCopy / SQLite: 多值 INSERT)
await db.BulkInsertAsync(users);

// 批量更新(逐条 + 乐观锁)
await db.BulkUpdateAsync(users);

// 批量更新(v5.0 单语句批量,PG: FROM VALUES / MySQL: CASE WHEN / SQLite: 自动回退逐条)
await db.BulkUpdateBatchAsync(users);

// 批量删除(IN 子句单语句)
await db.BulkDeleteAsync<User>(keyList);

// 批量 UPSERT
await db.BulkMergeAsync(users);

配置系统

预设配置

// 开发环境
var dev = DbOptions.Development(connectionString);

// 生产环境(含连接池配置)
var prod = DbOptions.Production(connectionString, readConnectionString);

// 测试环境(零重试 + 短超时)
var test = DbOptions.Testing(connectionString);

// 环境变量加载(Docker/K8s 友好)
var env = DbOptions.FromEnvironment("PALORM_CONNECTION");

全部配置项

属性 类型 默认值 说明 v5.0
ConnectionString string (required) — 主库连接串(必需)。支持 $ENV:VAR_NAME 环境变量引用
ReadConnectionString string? null 只读副本连接串。配置后 ForRead() 自动路由到副本
ConnectionTimeout TimeSpan 15s 连接建立超时(含重试)。超时后抛 TimeoutException
CommandTimeout TimeSpan 30s 每条 SQL 命令的执行超时。亚秒值向上取整为 1 秒(避免塌缩为 0=无限等待)
MaxRetries int 3 瞬时故障(连接失败/超时/死锁)最大重试次数。0=禁用重试。v5.4 起覆盖只读查询内置管线;写入路径不自动重试
RetryBackoff Func<int, TimeSpan>? 指数退避 自定义重试间隔(参数=重试次数)。返回负值抛异常
MaxPoolSize int 100 连接池最大连接数。SQLite 忽略此项(嵌入式库无服务端池可调)

SQLite 并发写扩展性(2026-09-21 实测):80/20 读写混合负载下,SQLite 从 1 线程到 8 线程吞吐 下降 69%(75,720 → 23,304 ops/s)。已用裸 ADO.NET 同负载对照证实根因是 SQLite WAL 的单写者模型(裸 ADO.NET 同样 −69%),不是 PalORM 引入的锁。同期 PostgreSQL 为 +7.2×、MySQL +4.6×。选型建议:高并发写场景用 PG/MySQL; SQLite 适合读密集或低并发写的嵌入式场景。详见 docs/架构设计.md 的方言并发扩展性段。 | MinPoolSize | int | 0 | 连接池空闲保留下限(v5.6.0 新增)。0 = 不覆盖驱动默认;正数透传(PG MinPoolSize / MySQL MinimumPoolSize),空闲超时修剪时池内至少保留这么多条连接——消除「稀疏流量 + 空闲修剪清池 → 突发查询重建物理连接」的延迟尖峰(远程建连实测 ~8.5 ms/条)。启动期一次性预热用 DataSession.PreWarmAsync,与本参数正交。SQLite 忽略此项 | | | PoolIdleTimeoutSeconds | int | 0(v5.6.0 起;5.5.1 及更早为 30) | 连接池空闲超时(秒)。0 = 不覆盖驱动默认值(Npgsql 300 秒 / MySqlConnector 180 秒,原样保留);正数才覆盖。v5.6 起默认由 30 改为 0——被覆盖时的代价是间隔超过该值后的首个查询必须重建物理连接:实测跨网段 SELECT 1 池内 0.300 ms vs 新建连接 13.523 ms(多付 13.2 ms)。若你的部署受服务端 max_connections 挤压、希望更快释放空闲连接,显式设一个较小值即可 | | | PoolLifetimeMinutes | int | 60 | 连接最大生命周期(分钟)。到期后强制重建,避免长期持有陈旧连接 | | | PoolExplicitlyConfigured | bool | false | WithPool() 设置后为 true。标记「池参数由调用方显式给出」 | | | CircuitBreakerThreshold | int | 5 | 断路器:连续失败次数阈值。0=禁用熔断 | | | CircuitBreakerResetAfter | TimeSpan | 30s | 熔断后恢复等待时间。超时后进入半开状态(允许一次试探请求) | | | NamingConvention | enum | None | 命名策略(None=原样 / SnakeCase / LowerCase)。仅影响自定义 SQL 中的标识符归一化,不影响源生成器列映射 | | | Interceptors | IReadOnlyList<IQueryInterceptor>? | null | 查询拦截器列表。按 Priority 升序执行(数值小先执行)。AuditInterceptor 默认 Priority=200 | | | ValidateQueryColumnOrder | bool | true | QueryAsync 首行列名与实体声明序比对,不匹配抛异常。使用列别名/表达式列的查询需关闭 | | | QueryCache | IQueryCache? | 默认 1024 条 | 查询缓存实现。注入独立实例可实现会话/租户级隔离。实现需线程安全 | | | SessionSetupSql | string? | null | 主连接首次激活后执行的 SQL(SET TIME ZONE / search_path / statement_timeout)。多条用分号分隔 | v5.0 | | ReadSessionSetupSql | string? | null | 读副本连接首次激活后执行的 SQL。语义同 SessionSetupSql,作用于 ForRead 路由的只读副本。v5.6:读连接按会话复用,故每个会话只执行一次 | v5.0 | | LoggerFactory | ILoggerFactory? | null | 日志工厂。设置后 DataSession 创建 ILogger。日志级别过滤在 LoggerFactory 配置 | |

v5.0 连接串自动调优

PalORM v5.0 在 CreateConnection 时自动调优(仅当用户未显式设置时覆盖默认值):

PostgreSQL(6 项):

参数 默认 → 调优值 收益
MaxAutoPrepare 0 → 100 查询延迟 -30~50%(自动预编译)
AutoPrepareMinUsages 5 → 2 第 2 次执行起 Prepare
NoResetOnClose false → true 归还连接跳过 DISCARD ALL,+30% localhost 吞吐
ReadBufferSize 8192 → 16384 大结果集吞吐
WriteBufferSize 8192 → 16384 大值写入吞吐
Enlist true → false 跳过 TransactionScope 检查

MySQL(5 项):

参数 默认 → 调优值 收益
AutoEnlist true → false 跳过 TransactionScope
ConnectionReset true → false 跳过 COM_RESET_CONNECTION
CancellationTimeout 2 → 5 防连接泄漏
AllowLoadLocalInfile false → true MySqlBulkCopy 前提
ServerRedirectionMode Disabled → Preferred Azure MySQL 直连

SQLite PRAGMA(5 项):

PRAGMA 默认 → 调优值 收益
synchronous FULL → NORMAL WAL 下安全,减少 fsync
cache_size 2MB → 64MB 读密集型提升
temp_store DEFAULT → MEMORY 临时表走内存
wal_autocheckpoint 1000 → 1000 显式固定防漂移
mmap_size 0 → 256MB 文件库 I/O 加速(:memory: 跳过)

与主流 ORM 特性对比

版本基准:PalORM 5.0.0(.NET 11)/ Dapper 2.1.79(2025)/ EF Core 10.0.10(2025-11 LTS)/ RepoDb 1.15.1(2025)。单元格依据见下方"对比依据"小节。

特性 PalORM 5.0 Dapper 2.1.79 EF Core 10.0.10 RepoDb 1.15.1
Native AOT 全链路 ✓ 源生成验证 △ Dapper.Aot 可选(实验性拦截器) ❌ 实验性,生产不推荐 ❌ 反射 + IL Emit
编译时类型诊断 ✓ 39 条诊断规则(36 分析器 + 3 生成器;P0 防崩溃 + P1 防静默错误 + 调用级 API 误用 + 生成器兜底) ❌ 运行时失败 △ 迁移检查(设计时) ❌ 运行时失败
编译时 SQL 预构建 ✓ Roslyn 源生成 ❌ 运行时拼接 △ 预编译查询(实验性) ❌ 运行时表达式树
运行时反射 零 △ 首次反射 + IL Emit 缓存 △ 表达式树编译 ❌ 反射 + IL Emit
三方言批量策略 ✓ COPY / BulkCopy / 多值 ❌ 无(手写多值 SQL) △ Provider 各异 △ BulkInsert 仅 SQL Server
单语句多行 UPDATE ✓ FROM VALUES / CASE WHEN ❌ ❌ ExecuteUpdate 仅按 WHERE 单值 ❌
乐观锁 ✓ [ConcurrencyCheck] 自动 ❌ 手写 ✓ RowVersion 自动 ❌ 手写
软删除 ✓ [SoftDelete] 自动过滤 ❌ ✓ 全局查询过滤器 ❌
多租户列隔离 ✓ [TenantAware] 编译时 ❌ △ 需手动实现 ❌
OwnedJson 编译时安全 ✓ [OwnedJson] + 源生成 ❌ 手写 STJ ✓ Owned Types(运行时) ❌
审计拦截器 ✓ AuditInterceptor(v5.0) ❌ ✓ Interceptors ❌
咨询锁 ✓ pg_advisory_xact_lock(v5.0) ❌ ❌ ❌
会话级 SET ✓ SessionSetupSql(v5.0) ❌ ❌ ❌
SQL 文件嵌入 ✓ [SqlFile] 编译时校验 ❌ ❌ ❌
断路器 + 重试 ✓ 内置 ❌ 需 Polly △ 类似(执行策略) ❌
CTE / 窗口函数 ✓ 链式 API △ 原生 SQL 字符串 △ LINQ 翻译(部分) △ 原生 SQL
多结果集 ✓ GridReader ✓ QueryMultiple ❌ △ ExecuteQueryMultiple
Keyset 分页 ✓ ToPageAsync ❌ ❌ ❌
Scaffold 工具 ✓ 三 Provider(v5.0) ❌ ✓ dotnet ef dbContext scaffold ❌
连接串自动调优 ✓ PG 6 / MySQL 5 / SQLite 5 ❌ ❌ ❌
BulkInsert 内存效率 ✓ Dapper 的 ~40% 基线 最高(ChangeTracker) 中等(packed)
核心包 NuGet 依赖 零 零 高(多包拆分) 中等
目标框架 net11.0(单目标) 多目标(netstandard2.0+) 多目标(net8+) 多目标(netstandard2.0+)
许可证 AGPL-3.0-only Apache-2.0 MIT Apache-2.0

PalORM 的核心差异:编译时生成 + 全链路 AOT 兼容 + 三方言批量策略。Dapper 快但运行时反射;EF Core 功能完整但运行时重、AOT 仍实验性;RepoDb 与 PalORM 同为微 ORM 但无源生成,且批量仅 SQL Server。

对比依据

  • Dapper 2.1.79:Dapper.AOT(独立包,aot.dapperlib.dev)通过 Roslyn interceptors 生成 AOT 拦截器,但 interceptors 是 C# 实验性特性,非默认启用。
  • EF Core 10.0.10:EF Core 10 为 LTS(learn.microsoft.com)。ExecuteUpdateAsync/ExecuteDeleteAsync 仅支持"按 WHERE 单值更新",无法在单 SQL 内对每行设置不同值。AOT 仍为实验性(issue #35945,CS9137 错误未解决)。Scaffold:dotnet ef dbContext scaffold 完整支持。
  • RepoDb 1.15.1:BulkOperation 仅 SQL Server(repodb.net/operation/bulkinsert:"It is only supporting the SQL Server RDBMS."),其他方言走 InsertAll(packed statements,非真正二进制 bulk)。ExecuteQueryMultiple 提供多结果集。

特性总览

编译时源生成

Roslyn IIncrementalGenerator 为每个 [Table] 实体生成 RowFactory(物化委托)、CommandFactory(参数绑定)、Migration(三方言 DDL)。FormattableString 路径的值只进 @pN 占位(编译期参数化,默认防注入);显式逃生门——Raw()、ExecuteNonQuery(string)、SessionSetupSql——由调用方负责内容(Raw 拒绝控制字符防线,其余不验证)。WithComparer 优化增量缓存命中率。Native AOT 全链路零 IL。

注解

注解 说明
[Table("name")] 表名,标识 ORM 实体
[Column("name")] 列名
[Key] 主键(支持 AutoIncrement = false)
[ForeignKey] 外键引用(支持 OnDelete 级联策略)
[ConcurrencyCheck] 乐观锁版本检查
[SoftDelete] 软删除自动过滤
[TenantAware] 多租户自动隔离(WithTenant(id) 单库列过滤)
[OwnedJson(typeof(Ctx))] 编译时安全 JSON 序列化
[Index(name, cols, Unique = true)] 复合索引
[Unique] 唯一约束
[Computed("SQL")] 计算列
[IgnoreOnInsert] 插入跳过
[Converter(typeof(T))] 自定义值转换器
[SqlFile("path.sql")] 编译时嵌入 SQL 文件
[SqlTemplate("name")] FormattableString 常量
[SensitiveData] 敏感字段标记
[NotMapped] 排除映射
[Schema("name")] 数据库 schema

查询

From<T>() 返回 struct QueryBuilder<T>,链式 .Where() / .OrderBy() / .Take() / .Skip() / .Select() / .GroupBy() / .Having() / .Include() / .ThenInclude()。支持 InnerJoin / LeftJoin / RightJoin、WhereIn / WhereNotIn(自动分批)、CTE、窗口函数、悲观锁(ForUpdate / ForShare)、SQL 预览(AsDryRun)、查询缓存、Keyset 分页。

写入与批量

InsertAsync / UpdateAsync / DeleteAsync / SaveAsync(UPSERT)。批量:

方法 PG MySQL SQLite
BulkInsertAsync Binary COPY BulkCopy(local_infile)或 多值 INSERT 多值 INSERT
BulkUpdateAsync 逐条 + 乐观锁 逐条 + 乐观锁 逐条 + 乐观锁
BulkUpdateBatchAsync FROM VALUES(v5.0) CASE WHEN(v5.0) 自动回退逐条
BulkDeleteAsync IN 单语句 IN 单语句 IN 单语句
BulkMergeAsync 逐条 UPSERT 逐条 UPSERT 逐条 UPSERT

事务与弹性

函数式事务 WithTransaction(callback) 自动 commit/rollback,支持保存点。

弹性策略(WithRetry 指数退避 + WithCircuitBreaker 熔断)自 v5.4 起自动覆盖只读查询内置管线:

路径 弹性覆盖 说明
From<T>() SELECT 族(ToList/First/Single)、GetAsync/GetAllAsync、聚合五兄弟 ✓ 自动 瞬时故障按配置重试并计入熔断;每次重试重建连接
连接建立 ✓ 自动 CreateAsync 自有重试循环(v5.0 起既有行为)
写入路径(Insert/Update/Delete/Save/Bulk/StoredProc) ✗ 直连 非幂等写自动重试有重复执行风险;显式需求用 ExecuteWithResilience 包裹
事务内查询 / ToPageAsync / 原始 SQL 家族 ✗ 直连 事务内重试以次生异常掩盖根因;原始 SQL 保持直连语义

默认 DbOptions(MaxRetries=3 / CircuitBreakerThreshold=5)即生效;Testing 预设零重试零熔断,测试确定性不受影响。

每查询常数开销(v5.6 实测):只读查询走弹性策略的代价 ≈272 B 分配/查询,与结果行数无关(单行查询 +8%,千行查询 +0.2%)。构成:超时 CTS + 定时器 ≈168 B(CommandTimeout 语义本身)、调用点委托 56 B、执行器机械 ≈48 B。其中只有后两项(104 B)原则上可剥,需把只读内核从 async 局部函数改成 struct 内核 + 泛型约束,而实测耗时无变化,故未做。想一点开销都不出就用直通配置(MaxRetries=0 + CircuitBreakerThreshold=0)——代价是同时失去超时包装:慢命令抛驱动自身异常,不再是带 PalORM.InfrastructureTimeout 标记的 TimeoutException。

横切关注点

功能 说明
[SoftDelete] 软删除自动 WHERE 过滤
[TenantAware] 多租户 WithTenant(id) 单库列隔离。查询结果缓存(WithCache)经 ADR-L 结构性隔离:多租户会话的实际缓存 key 由框架自动加租户前缀(__t:{tenantId}:),IgnoreFilters() 全量查询走独立 __all__: 命名空间——跨租户命中不可能;需要按租户控制缓存容量/TTL 时,经 DbOptions.QueryCache 为每租户注入独立实例(推荐路径)
[ConcurrencyCheck] 乐观锁 version 字段自动检查
AuditInterceptor(v5.0) 查询审计拦截器(⚠️ 覆盖面:实体 SELECT 管线、QueryBuilder UPDATE 与 ExecuteAsync(原始 DDL/DML,v5.6.0 接入)的 OnBefore/OnAfter/OnError——InsertAsync/DeleteAsync/SaveAsync/Bulk 家族/存储过程/迁移不产生审计记录,完整写入审计请用数据库层审计或 OpenTelemetry。logParameters:true 时 Set() 写入 [SensitiveData] 列的参数值自动掩码——经 QueryContext 传递)
IQueryInterceptor 三阶段查询拦截器接口
SessionSetupSql(v5.0) 连接首次激活后执行 SET 语句(SET TIME ZONE / search_path)
ForRead 读写分离(只读副本路由)

PostgreSQL 专有

功能 说明
PgNotificationListener 异步通知监听(自动重连 + 半开探针)
WhereJson JSONB 路径查询
Binary COPY BulkInsertAsync 内部使用
AcquireXactLockAsync(v5.0) 事务级咨询锁 pg_advisory_xact_lock。必须在事务内调用——事务外获取会立即释放,库内显式失败而非静默无效
TryAcquireXactLockAsync(v5.0) 非阻塞咨询锁

编译时诊断(PALORM001-046)

39 条诊断规则(36 条分析器规则 + 3 条生成器:PALORM041/045/046),按价值分层:

  • P0 防运行时崩溃(PALORM001-027 + 031-033 + 042-043):缺 [Key]、N+1 检测、软删/租户列校验、OwnedJson 上下文验证、无插入/更新列、[Timestamp] 非时间类型、[NotMapped] 冲突、BulkUpdateBatchAsync 对并发实体调用等——把运行时 throw 提前到编译期;v5.4 新增 [Timestamp]+[Computed] 冲突(042)与 SQL 标识符含控制字符/空串(043)——两者此前以生成器异常或静默跳过呈现
  • P1 防静默错误(PALORM034-037 + 040 + 044 + 045):[Key] 非默认初值让 SaveAsync 永远走 Update、[ConcurrencyCheck]+[IgnoreOnInsert] 让乐观锁基线为 0、#nullable disable 下 NULL 读取崩溃、[Required]+可空矛盾、[TenantAware] 租户列可空绕过隔离、[Computed] 表达式括号不平衡致实体被静默跳过(044)、生成器兜底提示(045——分析器规则被 .editorconfig/ruleset 抑制时,实体静默跳过场景的唯一编译期线索)——防止不 throw 但数据错/丢失/安全绕过

性能基准报告

测试环境:AMD Ryzen 9 8945HX (32 logical) · Windows 10 22H2 · .NET 11 RC1(SDK 11.0.100-rc.1.26425.128)· BenchmarkDotNet fork (net11) · SQLite 共享内存 10K 行 · PG 18.4 / MySQL 8.4.10 远程 数据批次:BDN 对照表 = 2026-09-24 全量跑测 gate-set(launch 1 / warmup 3 / iteration 5,均值);批量表 = 同日 PerfHub 完整批(history-20260924-231214.json,中位数 + AllocatedBytesPerOp 精确计数)。BDN gate-set 未开 MemoryDiagnoser,单表分配列以 PerfHub 批次 JSON 为准。下方 GC 装箱 / PostgreSQL / MySQL / AOT 体积 / SQL 构建各表为对应专项测量的原始批次结果,未随本批重跑。

SQLite CRUD(4 ORM 对照)

全表查询 10,000 行
方法 Mean vs ADO.NET
ADO.NET(基线) 4.28 ms 1.00x
Dapper 3.69 ms 0.86x
PalORM 4.85 ms 1.13x
RepoDb 3.53 ms 0.82x
单行插入
方法 Mean vs ADO.NET
ADO.NET 25.01 μs 1.00x
Dapper 26.88 μs 1.07x
PalORM 32.51 μs 1.30x
RepoDb 27.12 μs 1.08x
主键查询
方法 Mean vs ADO.NET
ADO.NET 22.96 μs 1.00x
Dapper 25.28 μs 1.10x
PalORM 27.66 μs 1.20x
RepoDb 27.43 μs 1.19x

批量操作(PerfHub · SQLite · 20,000 行 · 三臂对等口径)

方法 Mean Allocated vs Dapper
ADO.NET 地板 160.4 ms 14.9 MB 0.15x
Dapper 多值 INSERT 1,105.4 ms 79.5 MB 1.0x
PalORM BulkInsert 161.0 ms 14.8 MB 0.15x(快 6.9×,分配 19%)

三臂契约下 PalORM 与手写 ADO.NET 地板逐项持平(P/ADO 0.98–1.00),Dapper 多值 INSERT 在 20,000 行档因巨型 SQL 字符串构造慢 6.9×、分配 5.3×。跨批比值见 PerfHub 报告(bench/perfhub/report.html)。

GC 装箱分析(v5.0 新增)

操作(10K 行) Mean Allocated bytes/row 装箱占比
Insert(逐条) 103.7 ms 25,930 KB 2,654 B ~5%
BulkInsert 62.3 ms 5,099 KB 522 B ~24.5%
BulkUpdate(逐条) 28.8 ms 17,973 KB 1,839 B ~7%
BulkUpdateBatch(回退) 28.3 ms 17,973 KB 1,839 B ~7%
Query(对照组) 0.089 ms 5.41 KB 0.55 B 0%

BulkInsert 装箱 ~24.5%(每行 4 值类型列 × ~32B/装箱)。PG COPY / MySQL BulkCopy 路径不走 DbParameter.Value,已无装箱。

PostgreSQL(远程 PG 18.4)

操作 Mean Allocated
QueryAll 10K 15.04 ms 1,140 KB
BulkInsert 10K(COPY) 43.06 ms 9,797 KB
BulkUpdateBatch 1K(FROM VALUES) 4.85 ms 2,777 KB
GetByKey 501.9 μs 13.64 KB

MySQL(远程 MySQL 8.4.10)

操作 Mean vs ADO.NET Allocated
QueryAll 10K 94.79 ms 0.85x(快 15%) 1,937 KB
BulkInsert 10K 49.41 ms — 4,741 KB
BulkUpdateBatch 1K 12.43 ms — 2,405 KB
GetByKey 518.1 μs 0.42x(快 58%) 12.02 KB
Insert 1,597 μs 0.86x(快 14%) 12.45 KB

MySQL 单行操作比原生 ADO.NET 快 14~58%——v5.0 连接串调优(AutoEnlist=false / ConnectionReset=false)的收益在远程场景放大。

SQL 构建(纳秒级)

方法 Mean Allocated
StringBuilder(基线) 61.07 ns 1,496 B
PalORM Simple 129.01 ns 544 B(-64%)
PalORM Complex 161.01 ns 696 B(-53%)

跨方言 BulkUpdateBatch 对照(1K 行)

方言 SQL 策略 Mean 速度比
SQLite CASE WHEN → 回退逐条 28.3 ms 1.0x
PostgreSQL UPDATE FROM VALUES 4.85 ms 5.8x 快
MySQL CASE WHEN 12.43 ms 2.3x

Native AOT 发布体积

方言 exe 大小 发布目录
SQLite 4.5 MB 26 MB
PostgreSQL 11.6 MB 61 MB
MySQL 9.5 MB 47 MB

查询构建的性能提示:表达式树提到静态字段

OrderBy / ThenBy / Select / GroupBy / WhereIn / WhereNotIn / Set / Include / ThenInclude 接收 Expression<Func<T, ...>>。C# 在调用点构造表达式树,库拿到时成本已付, 无法在库内缓存——每次调用都要重建,实测每棵树 512 字节加 0.5~1.6 µs。 把 lambda 提到静态字段即可完全消除这笔开销:

// ❌ 每次调用都重建表达式树
await db.From<Order>().OrderBy(o => o.CreatedAt).ToListAsync();

// ✅ 表达式树只构造一次,之后复用
private static readonly Expression<Func<Order, DateTime>> ByCreatedAt = o => o.CreatedAt;
await db.From<Order>().OrderBy(ByCreatedAt).ToListAsync();

收益随查询规模变化(SQLite 实测):

场景 分配降幅 时间降幅
UPDATE + Set(...) −18.0% −18.3%
单行查询 + OrderBy(...) −12.4% −7.2%
WhereIn(500) −0.72% −1.7%
10K 行查询 被结果集摊薄到可忽略 同

什么时候值得改:查询次数多、单次行数少,且热路径上用到上述构建器方法。 批量与报表型负载不必改。Where / OrWhere / Having 接收 FormattableString, 本来就不构造表达式树,无需处理。


使用示例

事务

await db.WithTransaction(async ct =>
{
    await db.InsertAsync(order, ct);
    await db.BulkInsertAsync(order.Items, ct);
    await db.ExecuteAsync($"UPDATE inventory SET stock = stock - {order.Items.Count} WHERE product_id = {productId}", ct);
});

弹性重试(v5.4 只读管线)

// 配置一次,只读查询自动获得重试 + 熔断
await using var db = await DataSession<PostgreSqlProvider>.CreateAsync(
    DbOptions.Production(connectionString)
    .WithRetry(maxRetries: 3)
    .WithCircuitBreaker(failureThreshold: 5, resetAfter: TimeSpan.FromSeconds(30)));

// SELECT 瞬时故障(死锁/超时/连接闪断)自动重试,无需样板代码
List<Order> recent = await db.From<Order>()
    .Where($"created_at > {since}")
    .OrderBy(o => o.CreatedAt)
    .Take(100)
    .ToListAsync(ct);

// 非幂等写入不自动重试——显式声明弹性意图
long affected = await db.ExecuteWithResilience(
    token => db.From<Order>()
        .Set(o => o.Status, OrderStatus.Paid)
        .Where($"id = {orderId}")
        .ExecuteNonQueryAsync(token), ct);

多结果集(GridReader)

using var grid = await db.QueryMultipleAsync($"SELECT * FROM users WHERE id = {userId}; SELECT * FROM orders WHERE user_id = {userId}");
var user = await grid.ReadFirstAsync<User>();
var orders = await grid.ReadAsync<Order>().ToListAsync();

Keyset 游标分页

var page = await db.From<Order>()
    .Where($"created_at < {cursor}")
    .OrderBy(o => o.CreatedAt, descending: true)
    .Take(20)
    .ToPageAsync();

AuditInterceptor(v5.0)

var db = await DataSession<PostgreSqlProvider>.CreateAsync(new DbOptions
{
    ConnectionString = connectionString,
    Interceptors = [new AuditInterceptor(loggerFactory.CreateLogger("Audit"))]
});

SessionSetupSql(v5.0)

var db = await DataSession<PostgreSqlProvider>.CreateAsync(new DbOptions
{
    ConnectionString = connectionString,
    SessionSetupSql = "SET TIME ZONE 'UTC'; SET search_path TO 'app, public'"
});

AdvisoryXactLock(v5.0 PG 专有)

await db.WithTransaction(async ct =>
{
    await db.AcquireXactLockAsync(resourceKey, ct);  // 阻塞获取
    // 临界区操作...
});
// 事务结束自动释放锁

原生 SQL

var count = await db.ScalarAsync<long>($"SELECT COUNT(*) FROM users WHERE email LIKE {"%@example.com%"}");

编译时 SQL 文件

[SqlFile("Reports/MonthlySales.sql")]
public static partial MonthlySalesReport[] GetMonthlySales();

Native AOT

PalORM 是全链路 Native AOT 兼容的微 ORM:

dotnet publish -c Release -r win-x64 /p:PublishAot=true

AOT 安全保证:

组件 AOT 状态
RowFactory(物化委托) ✓ 编译时生成
CommandFactory(参数绑定) ✓ 编译时生成
Migration DDL ✓ 编译时生成
QueryBuilder(值类型 struct) ✓ 零虚调用
OwnedJson(JsonSerializerContext) ✓ 源生成
注解诊断(PALORM001-046) ✓ 编译时
AuditInterceptor ✓ 零反射
BulkUpdateBatchAsync ✓ StringBuilder + 参数绑定

已验证发布:SQLite / PostgreSQL / MySQL 三方言 Native AOT 发布全部成功(win-x64),运行输出 PalORM AOT verification PASSED。


Scaffold 工具

dotnet run --project tools/PalORM.Scaffold -- <connection-string> --dialect sqlite|pg|mysql [--namespace NS] [--output DIR]

三 Provider schema → C# 实体反向工程。40+ 类型映射(含 uuid → Guid、jsonb → string、bytea → byte[]、date → DateOnly、time → TimeOnly)。


架构设计

PalORM.Core           运行时核心(DataSession / QueryBuilder / Resilience / IQueryInterceptor)
PalORM.PostgreSql     Npgsql 适配 + JSONB / NOTIFY / Binary COPY / AdvisoryXactLock
PalORM.MySql          MySqlConnector 适配 + MySqlBulkCopy
PalORM.Sqlite         MDS + SQLite3MC 适配 + PRAGMA 调优
PalORM.SourceGen      Roslyn IIncrementalGenerator(netstandard2.0 编译器插件)
PalORM.Testing        测试辅助(TestEnvironment / TestDb)

零运行时依赖:PalORM.Core 不引用任何第三方 NuGet 包(仅 BCL + ADO.NET 抽象 + 共享框架日志抽象)。

跨程序集注册契约:每个模型程序集的生成物通过 ModuleInitializer 在该模块首次被触达(任一成员被调用、类型被实例化、静态字段被访问)时向 PalORM_Runtime 注册。引用了库程序集但从未触达其中任何类型时,该程序集的实体不会注册,运行期表现为 "not registered"——跨程序集消费方请确保实体类型被真实引用后再使用会话。


许可证

AGPL-3.0-only

Product Compatible and additional computed target framework versions.
.NET net11.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on PalORM.PostgreSql:

Package Downloads
PalDDD.PalORM.PostgreSql

Pal.DDD PalORM PostgreSQL 适配包(AOT + 源生成 + RETURNING/COPY)

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
5.6.0 0 9/24/2026
5.5.1 279 9/11/2026
5.5.0 87 9/11/2026
5.4.0 123 9/2/2026
5.3.0 133 8/21/2026
5.2.0 181 8/16/2026
5.1.0 141 7/30/2026
5.0.0 127 7/27/2026