Javxp.DapperHelper 1.0.1

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

Javxp.DapperHelper

项目概述

Javxp.DapperHelper 是一个基于 Dapper 的高性能存储过程驱动数据访问引擎,面向企业级 .NET 应用。它封装了 Dapper 的底层操作,以存储过程为唯一调用入口,提供类型安全、极简调用的数据访问体验。

核心能力:

  • 全异步 API —— 所有数据库操作均为异步方法,支持 CancellationToken 取消
  • 多结果集自动聚合 —— QueryMultipleAsync<TResult> 通过 Expression Tree 编译缓存,实现聚合 DTO 的零反射自动映射
  • 流式查询 —— QueryStreamAsync 基于 IAsyncEnumerable<T> 逐行产出,百万级数据内存占用极低
  • 分页查询 —— 自动绑定 OUTPUT 参数,开箱即用的分页支持
  • SQL Server 批量插入 —— BulkCopyAsync 封装 SqlBulkCopy,万级/十万级数据秒级写入
  • TVP 参数构建 —— ToDataTable 扩展方法一键构建 Table-Valued Parameter
  • 聚合关联 —— AggregateHelper 将父子集合按外键快速关联回填
  • 依赖注入集成 —— 一行代码完成服务注册

运行环境

项目 说明
开发语言 C# 13
目标框架 .NET 10.0
核心依赖 Dapper 2.1.72
数据库驱动 Microsoft.Data.SqlClient 7.0.1
DI 抽象 Microsoft.Extensions.DependencyInjection.Abstractions 10.0.0
数据库支持 SQL Server(完全支持),架构预留 MySQL / PostgreSQL 扩展
编译器依赖 Microsoft.CodeAnalysis.CSharp 5.3.0(源生成器支持)

集成示例

1. 依赖注入注册

using Javxp.DapperHelper;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

// 一行代码注册所有服务
services.JavxpDapperHelper(DBProvider.SQLServer, "Server=.;Database=MyDb;Trusted_Connection=true;");

var provider = services.BuildServiceProvider();
var db = provider.GetRequiredService<IDBService>();

内部注册:IDbConnectionFactory(Singleton)+ IDBService(Scoped),保证同一 Scope 内共享连接。


2. 单条查询(QuerySingleAsync)

// DTO 定义
public class OrderDto
{
    public long OrderId { get; set; }
    public string OrderNo { get; set; } = string.Empty;
    public string CustomerName { get; set; } = string.Empty;
    public decimal TotalAmount { get; set; }
    public DateTime CreatedAt { get; set; }
}

// 查询单条记录(无匹配返回 null)
OrderDto? order = await db.QuerySingleAsync<OrderDto>(
    "usp_GetOrderById",
    new { OrderId = 1001 });

// 带事务查询
OrderDto? orderInTx = await db.QuerySingleAsync<OrderDto>(
    "usp_GetOrderById",
    new { OrderId = 1001 },
    transaction: currentTransaction);

3. 列表查询(QueryListAsync)

// 查询多条记录,返回 IReadOnlyList<T>
IReadOnlyList<OrderDto> orders = await db.QueryListAsync<OrderDto>(
    "usp_GetOrdersByCustomer",
    new { CustomerId = 50 });

// 无参数查询全部
IReadOnlyList<OrderDto> allOrders = await db.QueryListAsync<OrderDto>(
    "usp_GetAllOrders");

4. 多结果集自动聚合(QueryMultipleAsync<TResult>)

存储过程返回多个结果集时,QueryMultipleAsync<TResult> 自动按属性声明顺序将结果集映射到聚合 DTO,无需手动逐个读取。

// 聚合 DTO —— 属性声明顺序必须与存储过程返回的结果集顺序一致
public class OrderAggregate
{
    // 第1个结果集 → 单个订单(单对象属性自动取第一条记录)
    public OrderDto? Order { get; set; }

    // 第2个结果集 → 订单明细列表(集合属性绑定整个结果集)
    public IReadOnlyList<OrderItemDto> Items { get; set; } = [];

    // 第3个结果集 → 操作日志
    public IReadOnlyList<OrderLogDto> Logs { get; set; } = [];
}

public class OrderItemDto
{
    public long ItemId { get; set; }
    public string ProductName { get; set; } = string.Empty;
    public int Quantity { get; set; }
    public decimal UnitPrice { get; set; }
}

public class OrderLogDto
{
    public long LogId { get; set; }
    public string Action { get; set; } = string.Empty;
    public DateTime OperatedAt { get; set; }
}

// 一次调用,自动聚合所有结果集
OrderAggregate result = await db.QueryMultipleAsync<OrderAggregate>(
    "usp_GetOrderDetail",
    new { OrderId = 1001 });

// 直接使用:result.Order、result.Items、result.Logs 均已填充

映射规则:

  • 集合属性(IReadOnlyList<T>、List<T>、IEnumerable<T> 等)→ 绑定整个结果集
  • 单对象属性(class 类型,非 string)→ 自动取结果集第一条记录(FirstOrDefault)
  • 属性声明顺序严格对应存储过程返回的结果集顺序

5. 多结果集手动读取(QueryMultipleAsync)

需要灵活控制读取逻辑时,使用非泛型 QueryMultipleAsync 获取 IDataSet 手动按序读取。

// 必须 await using 确保连接释放
await using var dataSet = await db.QueryMultipleAsync(
    "usp_GetOrderDetail",
    new { OrderId = 1001 });

// 按结果集返回顺序依次读取
var order = await dataSet.ReadAsync<OrderDto>();
var items = await dataSet.ReadAsync<OrderItemDto>();

// 也可使用自定义映射函数读取
var customItems = await dataSet.ReadAsync<OrderItemDto>(
    reader => reader.ReadAsync<OrderItemDto>());

注意:ReadAsync 调用顺序必须与数据库返回的结果集顺序一致,否则映射错乱。


6. 流式查询(QueryStreamAsync)

适合百万级大数据量场景,使用 IAsyncEnumerable<T> 逐行产出,内存占用极低。

// 流式遍历大结果集
await foreach (var order in db.QueryStreamAsync<OrderDto>(
    "usp_ExportOrders",
    new { Year = 2025 },
    cancellationToken: ct))
{
    // 逐行处理,不会一次性加载所有数据到内存
    await ExportToFile(order);
}

实现原理:基于 DbDataReader + Dapper GetRowParser<T> 逐行读取,不缓存全部结果。


7. 分页查询(QueryToPageAsync)

// 构建分页请求
var page = new PageRequest(PageIndex: 1, PageSize: 20);

// 执行分页查询
PageResponse<OrderDto> result = await db.QueryToPageAsync<OrderDto>(
    "usp_GetOrdersByPage",
    page,
    new { Status = "Completed" });

// 使用分页结果
Console.WriteLine($"总记录数: {result.TotalCount}");
Console.WriteLine($"总页数: {result.TotalPages}");
Console.WriteLine($"是否有下一页: {result.HasNextPage}");
Console.WriteLine($"是否有上一页: {result.HasPreviousPage}");
foreach (var order in result.Items)
{
    Console.WriteLine($"{order.OrderNo} - {order.TotalAmount:C}");
}

存储过程参数约定(必须按此约定定义 OUTPUT 参数):

参数名 方向 说明
@PageSize IN/OUT 每页记录数
@PageIndex IN/OUT 页码(从 1 开始)
@PageTotal OUT 总页数
@RecordTotal OUT 总记录数

8. 执行存储过程(ExecuteAsync)

// 执行写入操作,返回受影响行数
int affected = await db.ExecuteAsync(
    "usp_UpdateOrderStatus",
    new { OrderId = 1001, Status = "Shipped" });

// 带事务执行
int affectedInTx = await db.ExecuteAsync(
    "usp_CancelOrder",
    new { OrderId = 1001 },
    transaction: currentTransaction);

// 执行标量查询,返回单个值
long newId = await db.ExecuteScalarAsync<long>(
    "usp_CreateOrder",
    new { CustomerId = 50, TotalAmount = 199.99m });

9. 批量插入(BulkCopyAsync)

基于 SqlBulkCopy 的高性能批量写入,适合万级/十万级数据导入。

public class OrderItemEntity
{
    public long OrderId { get; set; }
    public string ProductCode { get; set; } = string.Empty;
    public string ProductName { get; set; } = string.Empty;
    public int Quantity { get; set; }
    public decimal UnitPrice { get; set; }
}

// 构建批量数据
var items = new List<OrderItemEntity>
{
    new() { OrderId = 1001, ProductCode = "P001", ProductName = "商品A", Quantity = 2, UnitPrice = 99.9m },
    new() { OrderId = 1001, ProductCode = "P002", ProductName = "商品B", Quantity = 5, UnitPrice = 49.5m },
    // ... 更多数据
};

// 执行批量插入(默认每批 5000 行)
bool success = await db.BulkCopyAsync(
    "OrderItems",       // 目标表名
    items.AsReadOnly(), // 数据集合
    batchSize: 5000);   // 每批写入行数

// 自定义批次大小
bool success2 = await db.BulkCopyAsync(
    "OrderItems",
    items.AsReadOnly(),
    batchSize: 10000,
    cancellationToken: ct);

注意:BulkCopyAsync 仅支持 SQL Server。数据实体的属性名必须与目标表列名一致,自动按名称映射。


10. TVP 参数构建(ToDataTable)

将集合转换为 DataTable,用于 SQL Server Table-Valued Parameter(表值参数)。

简单值类型集合 → 单列 DataTable:

// int[] 转单列 DataTable
int[] orderIds = [1001, 1002, 1003];
DataTable idTable = orderIds.ToDataTable("OrderId");

// string[] 转单列 DataTable
string[] productCodes = ["P001", "P002", "P003"];
DataTable codeTable = productCodes.ToDataTable("ProductCode");

// List<long> 转单列 DataTable
var ids = new List<long> { 1001, 1002, 1003 };
DataTable longTable = ids.ToDataTable("Id");

// 作为 TVP 参数传递给存储过程
var orders = await db.QueryListAsync<OrderDto>(
    "usp_GetOrdersByIds",
    new { Ids = idTable.AsTableValuedParameter("dbo.IntTableType") });

复杂对象集合 → 多列 DataTable:

public class OrderItemParam
{
    public long OrderId { get; set; }
    public string ProductCode { get; set; } = string.Empty;
    public int Quantity { get; set; }
    public decimal UnitPrice { get; set; }
}

var itemParams = new List<OrderItemParam>
{
    new() { OrderId = 1001, ProductCode = "P001", Quantity = 2, UnitPrice = 99.9m },
    new() { OrderId = 1001, ProductCode = "P002", Quantity = 5, UnitPrice = 49.5m },
};

// 无参 ToDataTable() 自动根据属性生成多列
DataTable tvpTable = itemParams.ToDataTable();

// 作为 TVP 参数传递
int affected = await db.ExecuteAsync(
    "usp_BatchInsertOrderItems",
    new { Items = tvpTable.AsTableValuedParameter("dbo.OrderItemType") });

内部使用 DataTableBuilder 的 Expression Tree 缓存机制,避免反复反射,大批量转换性能优异。


11. 聚合关联(AggregateHelper)

将独立查询的父子集合按外键快速关联回填,适用于 QueryMultipleAsync<TResult> 无法覆盖的灵活场景。

单子集合关联:

using Javxp.DapperHelper.Core;

public class OrderWithItems
{
    public long OrderId { get; set; }
    public string OrderNo { get; set; } = string.Empty;
    public IReadOnlyList<OrderItemDto> Items { get; set; } = [];
}

// 独立查询父集合和子集合
var orders = await db.QueryListAsync<OrderWithItems>("usp_GetOrders");
var items = await db.QueryListAsync<OrderItemDto>("usp_GetOrderItems");

// 按外键关联回填
AggregateHelper.AggregatePooled<OrderWithItems, OrderItemDto, long>(
    parents: orders,
    children: items,
    parentKeySelector: o => o.OrderId,       // 父表外键
    childKeySelector: i => i.OrderId,        // 子表外键
    assignExpr: o => o.Items);               // 赋值目标属性

// 现在 orders 中每个 OrderWithItems.Items 已填充对应的子记录

多子集合关联:

public class OrderFullView
{
    public long OrderId { get; set; }
    public string OrderNo { get; set; } = string.Empty;
    public IReadOnlyList<OrderItemDto> Items { get; set; } = [];
    public IReadOnlyList<OrderLogDto> Logs { get; set; } = [];
}

var orders = await db.QueryListAsync<OrderFullView>("usp_GetOrders");
var items = await db.QueryListAsync<OrderItemDto>("usp_GetOrderItems");
var logs = await db.QueryListAsync<OrderLogDto>("usp_GetOrderLogs");

// 一次调用关联多个子集合
AggregateHelper.AggregateMultiPooled<OrderFullView, long>(
    parents: orders,
    builder: builder =>
    {
        builder.Add(items, o => o.OrderId, i => i.OrderId, o => o.Items);
        builder.Add(logs, o => o.OrderId, l => l.OrderId, o => o.Logs);
    });

// 大数据量时可启用并行(父集合 > 64 条时自动并行)
AggregateHelper.AggregateMultiPooled<OrderFullView, long>(
    parents: orders,
    builder: builder =>
    {
        builder.Add(items, o => o.OrderId, i => i.OrderId, o => o.Items);
        builder.Add(logs, o => o.OrderId, l => l.OrderId, o => o.Logs);
    },
    parallel: true);

性能特性:

  • 内部使用 Dictionary<TKey, List<TChild>> 构建索引,时间复杂度 O(N+M)
  • Setter 委托通过 Expression Tree 编译并缓存,热路径零反射
  • parallel: true 时,父集合超过 64 条记录自动使用 Parallel.ForEach 并行回填

模块职责:

模块 职责
Abstractions 定义业务层依赖的接口和模型,业务层零耦合实现细节
Core 实现数据库操作核心逻辑,管理连接生命周期和事务复用
Mapping 自动聚合映射引擎,首次调用编译 Expression Tree,后续 O(1) 命中缓存
Extensions 提供开发体验增强:DI 注册、批量插入、TVP 构建

核心设计原理

连接管理策略

DBService 根据是否存在事务自动选择连接获取方式:

  • 无事务:通过 IDbConnectionFactory.CreateOpenConnectionAsync() 创建新连接,操作完成后自动释放(await using)
  • 有事务:复用 DbTransaction.Connection,保证同一事务内所有操作共享连接,支持原子提交/回滚
  • 多结果集场景:QueryMultipleAsync 始终创建独立连接,由 DapperDataSet 实现 IAsyncDisposable 统一管理 GridReader 和连接的释放顺序

Expression Tree 编译缓存

项目在三个核心位置使用 Expression Tree 编译缓存,消除热路径反射开销:

组件 编译内容 缓存策略
MappingPlan 聚合 DTO 属性的 Setter 委托 + Reader 委托 ConcurrentDictionary<Type, MappingStep[]>,按 TResult 类型缓存
DataTableBuilder 数据实体的属性 Getter 委托 ConcurrentDictionary<Type, PropertyAccessor[]>,按数据类型缓存
AggregateHelper 父对象属性的 Setter 委托 ConcurrentDictionary<string, object>,按类型全名 + 表达式字符串缓存

所有编译均为首次调用触发,后续 O(1) 命中缓存,运行时循环体内仅调用预编译委托,热路径零反射。

多结果集映射机制

QueryMultipleAsync<TResult> 的自动聚合映射流程:

  1. 元数据构建(仅首次):MappingPlan.GetOrCreateSteps<TResult>() 扫描 TResult 所有可写属性,区分为集合属性和单对象属性,为每个属性编译 Setter 和 Reader 委托,缓存为 MappingStep[]
  2. 数据读取:调用非泛型 QueryMultipleAsync 获取 IDataSet
  3. 按序映射:遍历缓存的 MappingStep[],按属性声明顺序依次调用 Reader 从 IDataSet 读取对应结果集
  4. 类型自适应赋值:集合属性直接赋值整个结果集;单对象属性自动取 FirstOrDefault

这种设计将所有反射操作(GetProperty、MakeGenericMethod、Invoke)集中在构建阶段一次性完成,运行时仅执行编译后的委托调用。

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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 127 6/8/2026
1.0.0 107 6/8/2026