Liwendong.Frameworks.Data.DapperExtend
1.5.1
dotnet add package Liwendong.Frameworks.Data.DapperExtend --version 1.5.1
NuGet\Install-Package Liwendong.Frameworks.Data.DapperExtend -Version 1.5.1
<PackageReference Include="Liwendong.Frameworks.Data.DapperExtend" Version="1.5.1" />
<PackageVersion Include="Liwendong.Frameworks.Data.DapperExtend" Version="1.5.1" />
<PackageReference Include="Liwendong.Frameworks.Data.DapperExtend" />
paket add Liwendong.Frameworks.Data.DapperExtend --version 1.5.1
#r "nuget: Liwendong.Frameworks.Data.DapperExtend, 1.5.1"
#:package Liwendong.Frameworks.Data.DapperExtend@1.5.1
#addin nuget:?package=Liwendong.Frameworks.Data.DapperExtend&version=1.5.1
#tool nuget:?package=Liwendong.Frameworks.Data.DapperExtend&version=1.5.1
Liwendong.Frameworks.Data.DapperExtend
Dapper 扩展:基于实体特性自动生成增删改查 SQL,并提供表达式条件、分页、事务与异步能力。
目标框架:net8.0;netcoreapp3.1;net451;net46;net47;net48
安装
dotnet add package Liwendong.Frameworks.Data.DapperExtend
快速开始
1. 定义实体
[DapperTable("Sys_User")] // 不标注时默认使用类名,支持 "dbo.Users" 这种带架构的写法
public class User : IDapperEntityAutoRegister
{
[DapperColumn(IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
[DapperColumn("user_name")] // 数据库栏目名与属性名不一致时使用
public string UserName { get; set; }
public int Age { get; set; }
[DapperColumn(IsOnlyIgnoreUpdate = true)]
public DateTime CreateTime { get; set; }
[DapperColumn(IsIgnore = true)] // 不参与任何 SQL
public string ExtendData { get; set; }
}
DapperColumnAttribute 可用属性:
| 属性 | 说明 |
|---|---|
ColumnName |
数据库栏目名 |
IsPrimaryKey |
主键(用于 WHERE 条件) |
IsIdentity |
自增长列(不参与插入与更新) |
IsIgnore |
完全忽略 |
IsOnlyIgnoreInsert |
仅忽略插入 |
IsOnlyIgnoreUpdate |
仅忽略更新 |
2. 初始化
// 默认 SQL Server
var handler = new DapperHandler(connectionString);
注意(仅支持 SQL Server):内置增删改查/分页 SQL 模板均为 T-SQL 专属语法(
SELECT TOP 1、SCOPE_IDENTITY()、ROW_NUMBER()等), 只支持 SQL Server。构造函数虽可传入自定义连接工厂,但并不能让这些 CRUD 模板工作在 MySQL/SQLite/PostgreSQL 上, 仅适用于自行传入 SQL 的Execute/GetList等方法。
3. 增删改查
// 新增
handler.Insert(user);
long newId = handler.InsertReturnId(user); // 返回自增长主键
// 按主键查询
var u1 = handler.GetEntityInfo(new User { Id = 1 });
var u2 = handler.GetEntityInfo<User>(new { Id = 1 });
// 按条件查询(全部参数化,无注入风险)
var u3 = handler.GetEntityInfo<User>(x => x.UserName == "张三" && x.Age > 18);
// 列表
var list = handler.GetList<User>(x => x.Age >= 18);
var all = handler.GetAllList<User>();
// 更新
handler.Update(user); // 全字段更新(未赋值字段会被写成 NULL)
handler.UpdateIgnoreNull(user); // 只更新非 null 字段,推荐
handler.Update(user, x => new { x.Age }); // 只更新指定列
handler.Update(user, x => new { x.Age }); // 只更新指定列
handler.UpdateWhere(user, x => x.Age > 18); // 按条件更新
// 删除
handler.Delete(user); // 按主键
handler.DeleteByKey<User>(new { Id = 1 });
handler.DeleteWhere<User>(x => x.Age < 18); // 按条件删除
// 统计
int count = handler.Count<User>(x => x.Age >= 18);
bool exists = handler.Exists<User>(x => x.UserName == "张三");
分页
var page = handler.GetPageList<User>(1, 20, "Id DESC", x => x.Age >= 18);
// page.Items / page.TotalCount / page.TotalPages / page.HasNext
排序字段 orderBy 只接受「字段名 + ASC/DESC」,含其他字符会直接抛异常,避免拼接注入。
表达式条件支持范围
| 写法 | 生成 SQL |
|---|---|
x => x.Age == 18 |
[Age]=@wp0 |
x => x.UserName != null |
[UserName] IS NOT NULL |
x => (x.Age == 1 \|\| x.Age == 2) && x.Id > 0 |
(([Age]=@wp0 OR [Age]=@wp1) AND [Id]>@wp2) |
x => !x.IsDel |
NOT ([IsDel]=@wp0) |
x => x.UserName.Contains("张") |
[UserName] LIKE @wp0 ESCAPE '\' |
x => x.UserName.StartsWith("张") |
[UserName] LIKE @wp0 ESCAPE '\' |
x => ids.Contains(x.Id) |
[Id] IN (@wp0,@wp1,...) |
- 所有值一律走 Dapper 参数化,不做字符串拼接,不存在 SQL 注入。
AND/OR自动补括号,不会出现优先级导致的语义反转。- 空集合的
Contains会生成(1=0),不会产生非法 SQL。 - 不支持的表达式会抛
EasyErrorException,不会静默生成畸形 SQL。 x => !x.IsDel会生成([IsDel]=0),不依赖数据库对 bit 的隐式布尔转换。
提示:
UpdateIgnoreNull只能跳过值为null的字段。bool、int、DateTime等值类型永远不为 null, 若希望它们也能被跳过,请把属性声明为bool?、int?、DateTime?。
事务
// 方式一:SQL 集合
var result = handler.ExecuteTransaction(new List<Tuple<string, object>>
{
new Tuple<string, object>("UPDATE Sys_User SET Age=@Age WHERE Id=@Id", new { Age = 20, Id = 1 }),
new Tuple<string, object>("DELETE FROM Sys_Log WHERE UserId=@UserId", new { UserId = 1 })
});
if (result.Item1 == false) { /* result.Item2 为错误信息 */ }
// 方式二:回调(支持事务内查询,异常向外抛出)
handler.ExecuteTransaction((db, trans) =>
{
db.Execute("UPDATE ...", new { Id = 1 }, trans);
db.Execute("DELETE ...", new { Id = 1 }, trans);
});
// 方式三:回调带回返回值
var id = handler.ExecuteTransaction((db, trans) =>
db.ExecuteScalar<long>("INSERT ...; SELECT SCOPE_IDENTITY();", null, trans));
异步
所有主要方法都有对应的 Async 版本,支持 CancellationToken:
var user = await handler.GetEntityInfoAsync<User>(x => x.Id == 1, cancellationToken);
var list = await handler.GetListAsync<User>(x => x.Age >= 18);
int rows = await handler.ExecuteAsync("UPDATE Sys_User SET Age=@Age", new { Age = 20 });
long newId = await handler.InsertReturnIdAsync(user);
存储过程
var p = new DynamicParameters();
p.Add("UserId", 1);
p.Add("Total", dbType: DbType.Int32, direction: ParameterDirection.Output);
var list = handler.QueryStoredProcedure<User>("sp_GetUsers", p);
int total = p.Get<int>("Total"); // 读取输出参数
栏目名与属性名不一致时的映射
查询回来的数据要映射回实体,需要提前注册 TypeMap:
// 单个实体
DapperColumnMapper.SetMapper<User>();
// 批量注册所有实现 IDapperEntityAutoRegister 的实体
DapperColumnMapper.AllSetMapper();
// .NET Framework 项目 dll 不在根目录时
DapperColumnMapper.AllSetMapper(searchAllDrictory: true, appendDirectory: "bin");
建议在应用程序启动时执行一次即可。
版本历史
1.5.1 (2026-10-17)
- 修复:更新列默认排除主键,主键不再被写进
SET子句(UpdateColumns/UpdateSetSQL) - 修复:
BuildUpdateIgnoreNull主键值为 null 时 WHERE 引用缺失参数报错,改为直接抛EasyErrorException;补充"值类型属性永远非 null"的说明 - 修复:
string.Contains(s, StringComparison)两参重载被误判为 IN 查询,先判定调用对象为 string 走 LIKE/Contains 字符串语义 - 修复:嵌套成员
x.Child.Name被静默映射成实体同名栏目生成错误条件,改为明确抛"不支持的表达式" - 修复:分页
(pageIndex-1)*pageSizeint 溢出,改 long 计算;GetPageList统计总数复用同一 where 构建结果,避免与分页条件不一致 - 修复:列名映射比较改
StringComparison.OrdinalIgnoreCase;TrimQuote按首尾成对剥括号,保留]]转义不再破坏含]的标识符 - 修复:无可插入/更新栏目的只读实体在
Build阶段抛错导致无法查询,改为仅在执行 Insert/Update 时报错 - 修复:
using System.Data.SqlClient与Microsoft.Data.SqlClient对齐条件编译,避免误导 - 修复:SQL 模板为 T-SQL 专属但文档声称支持 MySQL 等,修正 XML/README 文档为"仅 SQL Server"
- 优化:删除
DapperColumnAttribute注释掉的重复构造函数死代码 - 优化:
ExecuteTransaction(List<Tuple>)吞异常行为保留,XML 注释明确"检查 Item1"并推荐回调式重载;InsertBatch标注逐条执行(N+1),大批量请用 SqlBulkCopy
1.5.0 (2026-10-15)
- 新增:
net8.0目标框架(Dapper 2.1.66 + Microsoft.Data.SqlClient 5.2.3) - 新增:
net47目标框架(Dapper 2.1.66)
1.4.0 (2026-10-10)
- 修复:
Delete两个重载因SQL_DELETE占位符写成{2}却只传 2 个参数,必然抛FormatException,删除功能完全不可用 - 修复:Where 条件由字符串拼接改为 Dapper 参数化,消除 SQL 注入风险
- 修复:
AND/OR组合未加括号,导致(a || b) && c语义反转、误删误查数据 - 修复:一元
Not表达式被丢弃,x => !x.IsDel生成非法 SQL - 修复:
bool/Guid/DateTime/闭包字符串等值生成非法 SQL;null值不再触发空引用异常 - 修复:
Contains/StartsWith/IN等方法调用静默返回空串,拼出畸形 SQL,现显式抛异常 - 修复:
DapperColumnAttribute/DapperTableAttribute的AllowMultiple = true与单取特性矛盾,重复标注会抛AmbiguousMatchException - 修复:
ExecuteTransaction中Stopwatch死代码与重复的连接关闭;异常信息不再返回完整堆栈 - 优化:引入
DapperEntityMeta元数据缓存,消除每次 CRUD 的重复反射 - 优化:表名统一转义,支持
dbo.Users带架构写法 - 优化:
GetEntityInfo两个重载的重复代码合并 - 新增:
GetList(表达式)、GetAllList、Count、Exists、GetPageList分页 - 新增:
InsertReturnId、InsertBatch、UpdateIgnoreNull、UpdateWhere、DeleteWhere、DeleteByKey - 新增:
ExecuteScalar、QueryStoredProcedure、QueryFirstStoredProcedure - 新增:全链路
Async方法与CancellationToken支持 - 新增:
ExecuteTransaction支持IsolationLevel与回调式事务 - 新增:连接工厂注入(注意:内置 SQL 模板为 T-SQL 专属,仅支持 SQL Server,连接工厂只适用于自行传入 SQL 的方法)
- 新增:
net48目标框架(使用 Dapper 2.1.66) - 新增:
README.md与PackageTags包元数据 - 新增:每个公开 API 的完整 XML 注释(暂未生成 XML 文档文件,因其会连带开启
Liwendong.Utility的文档生成并引入大量既有注释告警,建议单独处理)
1.3.0
- 历史版本
| Product | Versions 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 was computed. 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 was computed. 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 | netcoreapp3.1 is compatible. |
| .NET Framework | net451 is compatible. net452 was computed. net46 is compatible. net461 was computed. net462 was computed. net463 was computed. net47 is compatible. net471 was computed. net472 was computed. net48 is compatible. net481 was computed. |
-
.NETCoreApp 3.1
- Dapper (>= 2.1.66)
- Liwendong.Utility (>= 3.3.1)
- Microsoft.Data.SqlClient (>= 5.2.3)
-
.NETFramework 4.5.1
- Dapper (>= 1.60.6)
- Liwendong.Utility (>= 3.3.1)
-
.NETFramework 4.6
- Dapper (>= 1.60.6)
- Liwendong.Utility (>= 3.3.1)
-
.NETFramework 4.7
- Dapper (>= 2.1.66)
- Liwendong.Utility (>= 3.3.1)
-
.NETFramework 4.8
- Dapper (>= 2.1.66)
- Liwendong.Utility (>= 3.3.1)
-
net8.0
- Dapper (>= 2.1.66)
- Liwendong.Utility (>= 3.3.1)
- Microsoft.Data.SqlClient (>= 5.2.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.