找回密码
 立即注册
首页 业界区 业界 Dapper轻量级扩展库SmartDapper

Dapper轻量级扩展库SmartDapper

单于易槐 2026-2-9 13:35:08
SmartDapper

SmartDapper 是一个基于 Dapper 的轻量级扩展库,提供 表达式树转 SQL链式构建器(Fluent Builder:QuerySet/InsertSet/UpdateSet/DeleteSet,支持 Where/Select/Join/GroupBy/OrderBy/Union/Set/Fill 等)通用 CRUD分页查询多数据库适配(SQL Server / MySQL / SQLite)。默认参数化执行,以降低 SQL 注入风险。
安装
  1. dotnet add package SmartDapper
复制代码
快速开始(SQL 生成 + Dapper 执行)
  1. 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();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(sql, parameters).ToList();
复制代码
使用扩展方法(IDbConnection)
  1. using Microsoft.Data.SqlClient;using SmartDapper.Extensions;using var conn = new SqlConnection("Server=.;Database=MyDb;Trusted_Connection=True;TrustServerCertificate=True;");// ---------------------------// 结果映射(可选,但推荐:列名/属性名不一致时)// ---------------------------// 让 Dapper 在 materialize(结果集->实体)时优先使用 [Column("DbColumn")] 映射,// 并可选开启下划线匹配(user_name -> UserName)。//// 注意:这是 Dapper 的全局设置(进程内对该实体类型的所有查询都会受影响),建议只在启动阶段执行一次。DapperMappingExtensions.UseColumnAttributeTypeMap(matchNamesWithUnderscores: true);// 或者:一次性对“实体所在程序集”内的类型批量注册(默认按 [Table]/[Key]/[Column] 识别实体)// DapperMappingExtensions.UseColumnAttributeTypeMap(matchNamesWithUnderscores: true, typeof(User).Assembly);// ---------------------------// 查询(非链式 / 非 Fluent)// ---------------------------var list = await conn.GetAllListAsync(u => u.IsActive);var (items, total) = await conn.GetPagedListAsync(skip: 0, take: 20);// 按主键查单条(不存在返回 null)var one = await conn.GetAsync(id: 1);// First / Single(注意:Single 在多条命中时会抛异常)var first = await conn.FirstOrDefaultAsync(u => u.IsActive);var single = await conn.SingleOrDefaultAsync(u => u.Id == 1);// 是否存在 / 计数var exists = await conn.AnyAsync(u => u.IsActive);var count = await conn.CountAsync(u => u.IsActive);// ---------------------------// 新增(非链式)// ---------------------------var newUser = new User { UserName = "Alice", IsActive = true };await conn.InsertAsync(newUser);// 插入并返回自增ID(要求实体主键标注 [Key] 且为 Identity)var newId = await conn.InsertAndGetIdAsync(newUser);// ---------------------------// 更新(非链式)// ---------------------------// 1) 按主键更新指定字段await conn.UpdateAsync(    updateFields: u => new { IsActive = false },    keyValue: 1);// 2) 按条件更新指定字段await conn.UpdateAsync(    updateFields: u => new { IsActive = true },    predicate: u => u.UserName == "Alice");// ---------------------------// 删除(非链式)// ---------------------------// 按主键删除await conn.DeleteAsync(id: 1);// 按条件删除await conn.DeleteAsync(u => u.IsActive == false);// ---------------------------// 软删除(SoftDelete,非链式)// ---------------------------// 前提:实体需要标注 [SoftDelete](可标在实体类上或 IsDeleted 字段属性上),否则会抛异常(避免误以为“已软删”)。//// 1) 可选:开启查询自动过滤已删除数据(全局开关)DapperExtensions.ConfigureSoftDelete(o =>{    o.FilterSoftDeleted = true;});// 2) 逻辑删除 / 恢复:按主键await conn.SoftDeleteAsync(id: 1);await conn.RestoreAsync(id: 1);// 3) 逻辑删除 / 恢复:按条件await conn.SoftDeleteAsync(u => u.UserName == "Alice");await conn.RestoreAsync(u => u.UserName == "Alice");
复制代码
多连接统一注册(可选,DI 场景)

SmartDapper 核心是“以你传入的 IDbConnection 为准”,你可以在一个项目里自由创建/使用多个连接。
如果你希望在 DI 中统一注册多个连接配置,可以使用 AddSmartDapperConnections(...) 注册一个按 key 创建连接的工厂:
  1. using System.Data;using Microsoft.Data.SqlClient;using SmartDapper.Extensions;var builder = WebApplication.CreateBuilder(args);builder.Services.AddSmartDapperConnections(o =>{    // 方式 1:按连接字符串 + 工厂    o.Add("Main", builder.Configuration.GetConnectionString("Main")!, cs => new SqlConnection(cs));    o.Add("Log", builder.Configuration.GetConnectionString("Log")!, cs => new SqlConnection(cs));    // 可选:指定默认 key(不设则默认取第一个 Add 的 key)    o.DefaultKey = "Main";});// 使用:按 key 创建连接(连接由你负责释放;可手写 using,也可用辅助方法自动 using)// var factory = sp.GetRequiredService();//// 方式 1:手写 using// using var conn = factory.Create("Main");// var users = await conn.GetAllListAsync();//// 方式 2:辅助方法(内部 Create + Dispose,减少重复代码)// var users2 = await factory.UseConnectionAsync("Main", c => c.GetAllListAsync());//// 默认连接(等价于 factory.CreateDefault() / factory.UseDefaultXxx...)// var usersDefault = await factory.UseDefaultConnectionAsync(c => c.GetAllListAsync());//// 方式 3:事务辅助方法(内部 BeginTransaction + Commit/Rollback + Dispose)// await factory.UseTransactionAsync("Main", async (c, tx) =>// {//     await c.InsertAsync(new User { /* ... */ }, transaction: tx);//     await c.UpdateAsync(new User { /* ... */ }, transaction: tx);// });//// 默认连接的事务版本// await factory.UseDefaultTransactionAsync(async (c, tx) =>// {//     await c.InsertAsync(new User { /* ... */ }, transaction: tx);//     await c.UpdateAsync(new User { /* ... */ }, transaction: tx);// });
复制代码
SQL 日志输出配置(可选,推荐在启动时设置)

SmartDapper 在执行 CRUD/查询时会记录“生成的 SQL”。默认策略

  • Information:仅输出 SQL(不输出参数,避免敏感信息泄露)
  • Debug:输出 SQL + 参数(便于本地/测试环境排查)
你可以在程序初始化阶段通过以下方式进行配置:
方式 1:DI 风格(推荐)
  1. using Microsoft.Extensions.Logging;using SmartDapper.Extensions;var builder = WebApplication.CreateBuilder(args);// 1) 默认无需配置:Info 只输出 SQL;Debug 输出 SQL + 参数// 2) 如需在 Info 级别也输出参数(注意敏感信息风险)builder.Services.AddSmartDapper(configureSqlLogging: o =>{    o.SqlLogLevel = LogLevel.Information;    o.ParameterLogLevel = LogLevel.Information;});// 3) 如需彻底禁用参数输出(即使 Debug)builder.Services.AddSmartDapper(configureSqlLogging: o =>{    o.ParameterLogLevel = LogLevel.None;});
复制代码
方式 1.1:统一注入(日志 + 软删一起配置,推荐)
  1. using Microsoft.Extensions.Logging;using SmartDapper.Extensions;var builder = WebApplication.CreateBuilder(args);builder.Services.AddSmartDapper(    configureSqlLogging: o =>    {        o.SqlLogLevel = LogLevel.Information;        o.ParameterLogLevel = LogLevel.Information;    },    configureSoftDelete: o =>    {        o.FilterSoftDeleted = true;    },    configureSqlServer: o =>    {        // SQL Server:是否在 SELECT 的 FROM 表名后追加 WITH (NOLOCK)        o.UseNoLock = true;    });
复制代码
结果映射(TypeMap)注册(可选,DI 场景推荐)

当你的“数据库列名”和“实体属性名”不一致时(例如 user_name vs UserName),建议在启动阶段注册一次映射规则:
  1. using SmartDapper.Extensions;var builder = WebApplication.CreateBuilder(args);// 按“实体所在程序集”批量注册(默认按 [Table]/[Key]/[Column] 识别实体)builder.Services.AddSmartDapperColumnMapping(    matchNamesWithUnderscores: true,    typeof(User).Assembly);
复制代码
全局 QueryFilter(可选,类似其它 ORM 的全局过滤)

SmartDapper 支持注册“全局 QueryFilter”,在查询时自动把过滤条件注入到 WHERE(并与用户传入的 Where(...) 条件用 AND 组合)。
典型用途:

  • 软删除(接口风格):ISoftDelete(可选接口)+ it => it.IsDeleted == false
  • 多租户/数据隔离:IHasTenant + it => it.TenantId == tenantId
  • 数据权限/只看有效数据:IHasCompany / IIsActive 等
兼容说明:QueryFilter 与现有软删开关 FilterSoftDeleted 完全兼容,可以同时开启;最终会一起 AND 到 WHERE 里。
提示:如果你同时开启了 FilterSoftDeleted=true,又通过 QueryFilter 写了同样的软删条件,会产生“重复条件”(不影响结果,只是冗余)。
方式 1:DI 风格(推荐)

注意:AddSmartDapper(...) 的参数较多,建议使用命名参数,避免顺序误用。
  1. using SmartDapper.Extensions;builder.Services.AddSmartDapper(    configureQueryFilters: o =>    {        // 例:接口软删除(需要实体实现 ISoftDelete,或你自定义的接口/基类)        o.AddTableFilter(it => it.IsDeleted == false);    });
复制代码
方式 2:非 DI 场景(直接配置)
  1. using SmartDapper.Extensions;DapperExtensions.ConfigureQueryFilters(o =>{    o.AddTableFilter(it => it.IsDeleted == false);});
复制代码
方式 3:IDbConnection 语法糖(更接近 db.QueryFilter...)

说明:C# 不支持扩展“属性”,因此调用形态为 db.QueryFilter().AddTableFilter(...)。
  1. using SmartDapper.Extensions;using var conn = new SqlConnection("...");conn.QueryFilter()    .AddTableFilter(it => it.IsDeleted == false);
复制代码
方式 2:非 DI 场景(直接配置)
  1. using Microsoft.Extensions.Logging;using SmartDapper.Extensions;// SQL 日志DapperExtensions.ConfigureSqlLogging(o =>{    o.SqlLogLevel = LogLevel.Information;    o.ParameterLogLevel = LogLevel.Debug; // 默认值});// 软删(查询是否自动过滤已删除)DapperExtensions.ConfigureSoftDelete(o =>{    o.FilterSoftDeleted = true;});// SQL ServerDapperExtensions.ConfigureSqlServer(o =>{    o.UseNoLock = true;});
复制代码
表达式树支持范围(WHERE / IN / LIKE / UPDATE SET)

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

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

适用于:conn.UpdateSet().Set(...) / conn.Update(updateFields, predicate) 等。
写法示例建议对象初始化器(推荐)x => new User { Name = name, IsActive = true }表达力最强,支持常量/闭包变量单字段x => x.Name支持,但通常仍需搭配明确赋值(更推荐对象初始化器)匿名 newx => new { x.Name, x.IsActive }可用,但更像“字段选择”,不如对象初始化器语义清晰典型不支持/慎用

类型示例说明算术运算x => x.Age + 1 > 18不支持(WHERE 二元运算仅处理比较与 AND/OR)未知方法调用x => x.Name.ToLower() == "a"不支持(仅支持 string 的 Contains/StartsWith/EndsWith)反向 Containsx => 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() / InsertSet() / UpdateSet() / DeleteSet()。构建器对象非线程安全,建议按一次请求/一次操作创建并使用一次。
线程安全说明(重要)


  • 为什么“线程不安全”:构建器内部会保存可变状态(例如:Where 谓词、Select 列、OrderBy 排序、分页、事务、超时等)。如果多个线程/任务共享同一个构建器实例并并发修改,会互相覆盖这些状态,导致生成的 SQL/参数混乱。
  • 推荐用法:每次操作都从 conn.QuerySet() / InsertSet() / ... 新建一个构建器,用完即弃;不要把构建器缓存为静态/单例,也不要在并发场景共享同一个实例。
查询(Select)
  1. using SmartDapper.Extensions;public sealed class UserBriefDto{    public int Id { get; set; }    public string UserName { get; set; } = string.Empty;}// 1) 全字段查询 + Where + OrderByvar list = await conn.QuerySet()    .Where(u => u.IsActive && u.Age >= 18)    .OrderBy(u => u.Id)    .ToListAsync();// 1.3) DISTINCT(查询去重)var distinctList = await conn.QuerySet()    .Distinct()    .Where(u => u.IsActive)    .ToListAsync();// 1.1) 多字段排序(支持 new { ... })var list2 = await conn.QuerySet()    .Where(u => u.IsActive)    .OrderBy(u => new { u.Age, u.Id })    .ToListAsync();// 1.2) 降序排序(独立方法)var list3 = await conn.QuerySet()    .Where(u => u.IsActive)    .OrderByDescending(u => u.Id)    .ToListAsync();// 2) 投影(推荐:DTO/匿名类型)var dtoList = await conn.QuerySet()    .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()    .Select(u => new UserBriefDto { Id = u.Id, UserName = u.UserName })    .Where(u => u.IsActive)    .ToListAsync();// 3) 指定列(字符串:仍返回实体 T,但请注意未选列将为默认值)var columns = await conn.QuerySet()    .Select("Id", "UserName")    .ToListAsync();// 3.1) 字符串列名排序(升序/降序)var columns2 = await conn.QuerySet()    .Select("Id", "UserName")    .OrderBy("Id", "UserName")    .ToListAsync();var columns3 = await conn.QuerySet()    .Select("Id", "UserName")    .OrderByDescending("Id", "UserName")    .ToListAsync();// 3.2) GroupBy(分组)// 注意:GroupBy 必须配合 Select("Col1", ...) 或 Select(projection) 指定返回列var grouped = await conn.QuerySet()    .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()    .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()    .SelectColumns(u => new { u.Id, u.UserName })    .ToListAsync();// 5) JOIN(Inner / Left / Right)// 注意:JOIN 需要你提供 ON 条件(表达式树或原生 SQL 片段)var joined = await conn.QuerySet()    .InnerJoin((u, r) => u.RoleId == r.Id)    .Where((u, r) => u.IsActive && r.IsEnabled) // 双表 WHERE    .Select((u, r) => new { u.Id, u.UserName, r.RoleName }) // JOIN 投影(同时引用两张表;匿名类型无需写 object)    .Distinct()    .ToListAsync();// 5.1) JOIN + 原生 SQL WHERE(复杂场景兜底)var joined2 = await conn.QuerySet()    .LeftJoin((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()    .InnerJoin((u, r) => u.RoleId == r.Id)    .InnerJoin((r, d) => r.DeptId == d.Id) // 以 Role 作为左表关联 Dept    .Select((u, r, d) => new { u.Id, u.UserName, r.RoleName, d.DeptName })    .Where((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()    .Where(u => u.Age > 18)    .UnionAll(        conn.QuerySet().Where(u => u.Age > 20))    .OrderBy("Id")   // UNION 结果集列名/别名(只允许“简单标识符”)    .Paging(0, 20)   // SqlServer/Oracle:UNION 分页必须先 OrderBy(...)    .ToListAsync();
复制代码
删除(Delete)
  1. // 1) 条件删除var affected = await conn.DeleteSet()    .Where(u => u.IsActive == false)    .ExecuteAsync();// 2) 按主键删除var affected2 = await conn.DeleteSet()    .ByKey(1)    .ExecuteAsync();
复制代码
软删除(Soft Delete)

简化策略(推荐):

  • 物理删除:Delete(...) / DeleteSet() 永远执行 DELETE。
  • 逻辑删除(软删):显式调用 SoftDelete(...) / SoftDeleteSet(),并且实体必须标注 [SoftDelete];否则会抛出异常(避免误以为已软删)。
  • 查询过滤:全局开关 FilterSoftDeleted 控制是否过滤已删除数据;默认不过滤(查询包含已删除数据)。过滤仅对标注了 [SoftDelete] 的实体生效。
  1. // 0) 实体声明软删除:可以标在“实体类”或“软删字段属性”上// - 推荐标在属性上:无需配置 ColumnName,默认使用属性名(或 [Column] 映射名)// - Flag 模式默认值:NotDeletedValue=0,DeletedValue=1//// class User// {//     [Key]//     public int Id { get; set; }////     [SoftDelete] // 默认 Mode=Flag,NotDeletedValue=0,DeletedValue=1//     public int IsDeleted { get; set; }// }// 1) 启用软删除(建议在启动阶段配置一次)// - DI 风格(推荐):在 Program.cs 里配置,可与 SQL 日志一起集中配置//   builder.Services.AddSmartDapper(configureSoftDelete: o =>//   {//       o.FilterSoftDeleted = true; // 开启后:查询会自动排除“已删除”(仅对标注了 [SoftDelete] 的实体生效)//   });//// - 非 DI 场景:直接配置DapperExtensions.ConfigureSoftDelete(o =>{    o.FilterSoftDeleted = true; // 开启后:查询会自动排除“已删除”(仅对标注了 [SoftDelete] 的实体生效)});// 2) 查询:默认包含已删除;当 FilterSoftDeleted=true 时自动排除已删除var list = await conn.QuerySet()    .Where(u => u.IsActive)    .ToListAsync();// 3) 物理删除(DELETE)await conn.DeleteSet().ByKey(1).ExecuteAsync();// 4) 逻辑删除(SoftDelete):实体必须标注 [SoftDelete],否则会抛异常await conn.SoftDeleteSet().ByKey(1).ExecuteAsync();// 5) 恢复(反软删)await conn.RestoreSet().ByKey(1).ExecuteAsync();
复制代码
插入(Insert)
  1. // 1) 自动创建实体并填充var newId = await conn.InsertSet()    .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. // 1) 推荐:对象初始化器(表达式树提取值)var rows = await conn.UpdateSet()    .Set(u => new User { UserName = "Alice", IsActive = true })    .Where(u => u.Id == 1)    .ExecuteAsync();// 2) 也支持:匿名对象(字段名按“属性名/列名”处理)var rows2 = await conn.UpdateSet()    .Set(new { UserName = "Alice", IsActive = true })    .Where(u => u.Id == 1)    .ExecuteAsync();
复制代码
Fluent 的已知限制/注意事项


  • 分页与排序:QuerySet().Paging(skip, take) 目前不支持与自定义 OrderBy(...) 同用(分页 SQL 已内置排序规则)。
  • 分页与排序(降序):同样不支持与 OrderByDescending(...) 同用。
  • 分页与分组:QuerySet().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((t, j) => ...):支持 1 个 JOIN
    • Select((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/Single:FirstOrDefault()/SingleOrDefault() 不会强制加 TOP/LIMIT,仅对结果集做“取第一条/单条”的行为(多条命中时 SingleOrDefault 会抛异常)。
多数据库支持


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

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册