SmartDapper 2.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package SmartDapper --version 2.0.0
                    
NuGet\Install-Package SmartDapper -Version 2.0.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="SmartDapper" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SmartDapper" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="SmartDapper" />
                    
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 SmartDapper --version 2.0.0
                    
#r "nuget: SmartDapper, 2.0.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 SmartDapper@2.0.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=SmartDapper&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=SmartDapper&version=2.0.0
                    
Install as a Cake Tool

SmartDapper

SmartDapper 是一个基于 Dapper 的轻量级扩展库,提供 表达式树转 SQL通用 CRUD分页查询多数据库适配(SQL Server / MySQL / SQLite)。默认参数化执行,以降低 SQL 注入风险。

安装

dotnet add package SmartDapper

快速开始(SQL 生成 + Dapper 执行)

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Dapper;
using Microsoft.Data.SqlClient;
using SmartDapper.SqlGenerator;

[Table("Users")]
public class User
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public string UserName { get; set; } = string.Empty;
    public bool IsActive { get; set; }
}

var generator = SqlGeneratorFactory.CreateSqlServer<User>();
var (sql, parameters) = generator.GetSelectAll(u => u.IsActive);

using var conn = new SqlConnection("Server=.;Database=MyDb;Trusted_Connection=True;TrustServerCertificate=True;");
var users = conn.Query<User>(sql, parameters).ToList();

使用扩展方法(IDbConnection

using Microsoft.Data.SqlClient;
using SmartDapper.Extensions;

using var conn = new SqlConnection("Server=.;Database=MyDb;Trusted_Connection=True;TrustServerCertificate=True;");

var list = await conn.GetAllListAsync<User>(u => u.IsActive);
var (items, total) = await conn.GetPagedListAsync<User>(skip: 0, take: 20);

表达式树支持范围(WHERE / IN / LIKE / UPDATE SET)

SmartDapper 的“表达式树转 SQL”主要用于 Expression<Func<T, bool>>(WHERE 谓词),以及更新场景的 Expression<Func<T, object>>(SET 字段选择/赋值)。

WHERE(Expression<Func<T, bool>>)支持项
场景 示例表达式(C#) SQL 形态(概念) 备注
等值 / 不等 x => x.Id == 1 / x => x.Id != 1 Id = @Id / Id <> @Id 参数名默认按字段名生成
比较 x => x.Age > 18 / >= / < / <= Age > @Age 仅支持比较运算符集合
逻辑组合 x => x.IsActive && x.Age >= 18 (... ) AND (... ) 支持 && / ||
NULL 判断 x => x.Name == null / != null Name IS NULL / IS NOT NULL 仅对 == null / != null 特判
bool 直接使用 x => x.IsActive IsActive = @IsActive(true) 避免生成裸列名导致 SQL 不可执行
bool 取反 x => !x.IsDeleted IsDeleted = 0 仅对实体 bool 字段取反做优化
字符串 Like Contains/StartsWith/EndsWith LIKE @p 仅支持 x => x.Column.Contains(value) 这类“实体字段在左侧”形态
IN(集合包含) x => ids.Contains(x.Id) Id IN (@Id_0, @Id_1, ...) 仅支持 list.Contains(x.Property);空集合会生成 1 = 0
All(全满足) x => ages.All(a => x.Age >= a) 展开为 AND All(empty) 会生成 1 = 1(见下方安全限制)
UPDATE 的 SET(Expression<Func<T, object>>)支持写法

适用于:conn.UpdateSet<T>().Set(...) / conn.Update(updateFields, predicate) 等。

写法 示例 建议
对象初始化器(推荐) x => new User { Name = name, IsActive = true } 表达力最强,支持常量/闭包变量
单字段 x => x.Name 支持,但通常仍需搭配明确赋值(更推荐对象初始化器)
匿名 new x => new { x.Name, x.IsActive } 可用,但更像“字段选择”,不如对象初始化器语义清晰
典型不支持/慎用
类型 示例 说明
算术运算 x => x.Age + 1 > 18 不支持(WHERE 二元运算仅处理比较与 AND/OR)
未知方法调用 x => x.Name.ToLower() == "a" 不支持(仅支持 string 的 Contains/StartsWith/EndsWith)
反向 Contains x => x.Tags.Contains("a") 不支持(仅支持 list.Contains(x.Prop)
安全限制(重要)
  • UPDATE/DELETE 禁止恒真条件:例如 x => true、或 All(empty) 导致的 1 = 1,会被拦截(防止误全表操作)。
  • Fluent 查询限制
    • Paging(skip, take) 不能与自定义 OrderBy(...) 组合使用(分页 SQL 已内置排序规则)。
    • 投影查询(Select(projection)不支持 Paging(...),也不支持SelectColumns(...) 混用(SelectColumns 已标记 Obsolete,仅为兼容保留)。

链式扩展(Fluent Builder)

SmartDapper 提供了一组面向 IDbConnection链式构建器,用于以“命令式链式 API”组织查询/插入/更新/删除,并在内部复用 SmartDapper 的 SQL 生成能力。

命名约定:QuerySet<T>() / InsertSet<T>() / UpdateSet<T>() / DeleteSet<T>()。构建器对象非线程安全,建议按一次请求/一次操作创建并使用一次。

线程安全说明(重要)
  • 为什么“线程不安全”:构建器内部会保存可变状态(例如:Where 谓词、Select 列、OrderBy 排序、分页、事务、超时等)。如果多个线程/任务共享同一个构建器实例并并发修改,会互相覆盖这些状态,导致生成的 SQL/参数混乱。
  • 推荐用法:每次操作都从 conn.QuerySet<T>() / InsertSet<T>() / ... 新建一个构建器,用完即弃;不要把构建器缓存为静态/单例,也不要在并发场景共享同一个实例。
查询(Select)
using SmartDapper.Extensions;

public sealed class UserBriefDto
{
    public int Id { get; set; }
    public string UserName { get; set; } = string.Empty;
}

// 1) 全字段查询 + Where + OrderBy
var list = await conn.QuerySet<User>()
    .Where(u => u.IsActive && u.Age >= 18)
    .OrderBy(u => u.Id)
    .ToListAsync();

// 1.3) DISTINCT(查询去重)
var distinctList = await conn.QuerySet<User>()
    .Distinct()
    .Where(u => u.IsActive)
    .ToListAsync();

// 1.1) 多字段排序(支持 new { ... })
var list2 = await conn.QuerySet<User>()
    .Where(u => u.IsActive)
    .OrderBy(u => new { u.Age, u.Id })
    .ToListAsync();

// 1.2) 降序排序(独立方法)
var list3 = await conn.QuerySet<User>()
    .Where(u => u.IsActive)
    .OrderByDescending(u => u.Id)
    .ToListAsync();

// 2) 投影(推荐:DTO/匿名类型)
var dtoList = await conn.QuerySet<User>()
    .Select(u => new { u.Id, u.UserName })
    .Distinct()
    .Where(u => u.IsActive)
    .OrderBy(u => u.Id)
    .ToListAsync();

// 2.1) 投影到 DTO(对象初始化器)
var dtoList2 = await conn.QuerySet<User>()
    .Select(u => new UserBriefDto { Id = u.Id, UserName = u.UserName })
    .Where(u => u.IsActive)
    .ToListAsync();

// 3) 指定列(字符串:仍返回实体 T,但请注意未选列将为默认值)
var columns = await conn.QuerySet<User>()
    .Select("Id", "UserName")
    .ToListAsync();

// 3.1) 字符串列名排序(升序/降序)
var columns2 = await conn.QuerySet<User>()
    .Select("Id", "UserName")
    .OrderBy("Id", "UserName")
    .ToListAsync();

var columns3 = await conn.QuerySet<User>()
    .Select("Id", "UserName")
    .OrderByDescending("Id", "UserName")
    .ToListAsync();

// 3.2) GroupBy(分组)
// 注意:GroupBy 必须配合 Select("Col1", ...) 或 Select(projection) 指定返回列
var grouped = await conn.QuerySet<User>()
    .Select("Age")
    .GroupBy(u => u.Age)
    .ToListAsync();

// 3.3) 聚合 + Having(推荐:投影 + SqlFunc)
// 注意:聚合函数通过 SqlFunc.* 在表达式中声明,用于生成 SQL(不会在运行时执行)。
// 典型生成 SQL 形态:
// SELECT [Age] AS [Age], COUNT(*) AS [Cnt] FROM [Users] GROUP BY [Age] HAVING (COUNT(*) > @value)
var grouped2 = await conn.QuerySet<User>()
    .Select(u => new { u.Age, Cnt = SqlFunc.Count() })
    .GroupBy(u => u.Age)
    .Having(u => SqlFunc.Count() > 1)
    .ToListAsync();

// 4) 兼容方式(不推荐):SelectColumns(...) 已标记 Obsolete,返回“部分字段”的实体,易误用
var legacyColumns = await conn.QuerySet<User>()
    .SelectColumns(u => new { u.Id, u.UserName })
    .ToListAsync();

// 5) JOIN(Inner / Left / Right)
// 注意:JOIN 需要你提供 ON 条件(表达式树或原生 SQL 片段)
var joined = await conn.QuerySet<User>()
    .InnerJoin<Role>((u, r) => u.RoleId == r.Id)
    .Where<Role>((u, r) => u.IsActive && r.IsEnabled) // 双表 WHERE
    .Select<Role>((u, r) => new { u.Id, u.UserName, r.RoleName }) // JOIN 投影(同时引用两张表;匿名类型无需写 object)
    .Distinct()
    .ToListAsync();

// 5.1) JOIN + 原生 SQL WHERE(复杂场景兜底)
var joined2 = await conn.QuerySet<User>()
    .LeftJoin<Role>((u, r) => u.RoleId == r.Id)
    .Where("[Role].[RoleName] LIKE @name", new { name = "%Admin%" })
    .ToListAsync();

// 5.2) 两次 JOIN + 三表投影 + 三表 WHERE
// 注意:第二次 JOIN 可以用“已 Join 的表”作为左表(例如 Role -> Dept)
var joined3 = await conn.QuerySet<User>()
    .InnerJoin<Role>((u, r) => u.RoleId == r.Id)
    .InnerJoin<Role, Dept>((r, d) => r.DeptId == d.Id) // 以 Role 作为左表关联 Dept
    .Select<Role, Dept>((u, r, d) => new { u.Id, u.UserName, r.RoleName, d.DeptName })
    .Where<Role, Dept>((u, r, d) => u.IsActive && r.IsEnabled && d.IsEnabled)
    .ToListAsync();

// 6) UNION / UNION ALL(合并结果集)
// 注意:UNION 要求两侧 SELECT 的列数量/类型可兼容;库不会自动对齐列。
// 另外,为避免两侧参数名冲突(例如都生成 @Age / @w0_0),UNION 会自动对每个子查询做参数前缀隔离。
var unionList = await conn.QuerySet<User>()
    .Where(u => u.Age > 18)
    .UnionAll(
        conn.QuerySet<User>().Where(u => u.Age > 20))
    .OrderBy("Id")   // UNION 结果集列名/别名(只允许“简单标识符”)
    .Paging(0, 20)   // SqlServer/Oracle:UNION 分页必须先 OrderBy(...)
    .ToListAsync();
删除(Delete)
// 1) 条件删除
var affected = await conn.DeleteSet<User>()
    .Where(u => u.IsActive == false)
    .ExecuteAsync();

// 2) 按主键删除
var affected2 = await conn.DeleteSet<User>()
    .ByKey(1)
    .ExecuteAsync();
插入(Insert)
// 1) 自动创建实体并填充
var newId = await conn.InsertSet<User>()
    .Fill(u => { u.UserName = "Alice"; u.IsActive = true; })
    .ExecuteAndGetIdAsync();

// 2) 基于已有实体实例
var user = new User { UserName = "Bob", IsActive = true };
await conn.InsertSet(user).ExecuteAsync();
更新(Update)
// 1) 推荐:对象初始化器(表达式树提取值)
var rows = await conn.UpdateSet<User>()
    .Set(u => new User { UserName = "Alice", IsActive = true })
    .Where(u => u.Id == 1)
    .ExecuteAsync();

// 2) 也支持:匿名对象(字段名按“属性名/列名”处理)
var rows2 = await conn.UpdateSet<User>()
    .Set(new { UserName = "Alice", IsActive = true })
    .Where(u => u.Id == 1)
    .ExecuteAsync();
Fluent 的已知限制/注意事项
  • 分页与排序QuerySet<T>().Paging(skip, take) 目前不支持与自定义 OrderBy(...) 同用(分页 SQL 已内置排序规则)。
  • 分页与排序(降序):同样不支持与 OrderByDescending(...) 同用。
  • 分页与分组QuerySet<T>().Paging(skip, take) 目前不支持GroupBy(...) 同用。
  • DISTINCT 注意事项Distinct() 会对整行去重,可能带来额外开销;并且在不同数据库上与 ORDER BY/分页组合时存在语义差异,请按业务谨慎使用。
  • 分组限制GroupBy(...) 需要显式指定返回列(Select("Col1", ...)Select(projection));不支持“全字段 + GroupBy”。
  • Having 限制Having(...) 需要显式指定返回列(Select("Col1", ...)Select(projection));通常应与 GroupBy(...) 配合使用。
    • 表达式版 Having(predicate) 支持 SqlFunc.Count/Sum/Avg/Min/Max/CountDistinct 等聚合函数声明;
    • 复杂场景可用 Having("COUNT(*) > @min", new { min = 1 }) 直接写 SQL 片段(注意自行保证安全)。
  • 投影限制:投影链(Select(projection))目前不支持 Paging(...),也不支持SelectColumns(...) 混用(SelectColumns 已标记 Obsolete,仅为兼容保留)。
  • JOIN 投影限制
    • Select<TJoin>((t, j) => ...):支持 1 个 JOIN
    • Select<TJoin1, TJoin2>((t, j1, j2) => ...):支持 2 个 JOIN
    • 当前版本 Where(predicate) 的多表谓词仅支持 2 表/3 表,且第一个参数必须是主表 T
  • UNION 限制/注意
    • Union/UnionAll 要求两侧查询的列列表结构兼容(数量/类型),库不会自动补齐/对齐列;
    • UNION 的 OrderBy("Col") 仅支持简单列名/别名(不支持 Table.Col 这类带点号形式);
    • SqlServer/Oracle 下 UNION 的 Paging(skip,take) 需要先 OrderBy(...)(否则无法生成 OFFSET/FETCH)。
  • First/SingleFirstOrDefault()/SingleOrDefault() 不会强制加 TOP/LIMIT,仅对结果集做“取第一条/单条”的行为(多条命中时 SingleOrDefault 会抛异常)。

多数据库支持

  • SQL Server:SqlGeneratorFactory.CreateSqlServer<T>()
  • MySQL:SqlGeneratorFactory.CreateMySql<T>()
  • SQLite:SqlGeneratorFactory.CreateSqlite<T>()

相关包

  • SmartDapper.Repository:Repository + UnitOfWork
  • SmartDapper.Middleware:ASP.NET Core 中间件 + 声明式事务([UnitOfWork]
Product Compatible and additional computed target framework versions.
.NET 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. 
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 SmartDapper:

Package Downloads
SmartDapper.Repository

SmartDapper.Repository 提供通用的 Repository 抽象与 UnitOfWork 实现,封装常用 CRUD、分页和事务边界,帮助你用一致的方式组织数据访问层代码。

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
5.1.0 56 2/3/2026
5.0.0 120 1/29/2026
4.0.0-beta001 50 1/22/2026
3.0.0 62 1/13/2026
2.3.0 123 1/4/2026
2.1.1 70 12/31/2025
2.1.0 57 12/30/2025
2.0.0 59 12/29/2025
1.1.1 113 12/26/2025
1.1.0 108 12/26/2025
1.0.0 150 12/25/2025