Dapper如何处理MySQL的LAST_INSERT_ID() Dapper获取MySQL自增ID

Dapper 获取 MySQL 自增 ID 的正确方式是 INSERT 后立即用同一连接(推荐加事务)执行 SELECT LAST_INSERT_ID() 并用 ExecuteScalar 获取结果;多语句优化需开启 Allow User Variables;不可依赖 Execute 返回值或 OUTPUT 子句。

Dapper 本身不直接封装 MySQL 的 LAST_INSERT_ID(),但它能通过标准 SQL 执行和返回值机制,安全、高效地获取刚插入记录的自增 ID。关键在于:使用 ExecuteScalar 执行带 SELECT LAST_INSERT_ID() 的语句,或更推荐——在单条语句中用 INSERT ...; SELECT LAST_INSERT_ID()(MySQL 支持多语句)或直接依赖 Dapper 对 ExecuteScalar 插入语句的隐式行为(需配置)。

用 ExecuteScalar 直接查 LAST_INSERT_ID()

这是最清晰、兼容性最好的方式。先执行 INSERT,再立即执行 SELECT LAST_INSERT_ID(),确保在同一个连接和事务上下文中运行:

  • 必须复用同一个 IDbConnection 实例(不能新开连接)
  • 建议显式开启事务,避免并发干扰(尤其高并发场景)
  • MySQL 的 LAST_INSERT_ID() 是连接级变量,只要没被其他 INSERT 覆盖,就安全

示例代码:

using var conn = new MySqlConnection(connStr);
conn.Open();
using var tx = conn.BeginTransaction();
try
{
conn.Execute("INSERT INTO users (name, email) VALUES (@Name, @Email)",
new { Name = "Alice", Email = "a@example.com" }, tx);
var id = conn.ExecuteScalar("SELECT LAST_INSERT_ID()", transaction: tx);
tx.Commit();
Console.WriteLine($"新用户ID:{id}");
}
catch
{
tx.Rollback();
throw;
}

用多语句一次执行(MySQL 特有优化)

MySQL 支持在一个命令中执行多条语句(需连接字符串启用 Allow User Variables=True 或默认允许),Dapper 可用 ExecuteScalar 直接运行 INSERT; SELECT LAST_INSERT_ID()

  • 减少一次网络往返,性能略优
  • 必须确保连接字符串包含 Allow User Variables=True(部分驱动版本需要)
  • 注意:不是所有 MySQL 连接库都默认开启多语句支持(如 MySqlConnector 默认允许,Old MySql.Data 需显式设置)

示例:

var sql = @"INSERT INTO users (name, email) VALUES (@Name, @Email); SELECT LAST_INSERT_ID();";
var id = conn.ExecuteScalar(sql, new { Name = "Bob", Email = "b@example.com" });

依赖 Execute 返回值(隐式,需注意驱动行为)

某些 MySQL 驱动(如较新版本的 MySqlConnector)在调用 Execute 插入后,会将自增 ID 自动设为返回值(类似 SQL Server 的 @@IDENTITY)。但 Dapper 的 Execute 方法本身只返回影响行数(int),**不会自动返回 ID** ——除非你用 ExecuteScalar 或驱动扩展了行为。

  • 不要依赖 Execute(...) 的返回值作为 ID(它返回的是“1”,不是 ID)
  • 若看到某段代码用 Execute 得到 ID,大概率是用了自定义扩展方法或特定驱动的非标行为
  • 最稳妥做法始终是显式查 LAST_INSERT_ID()

使用 Output 子句?MySQL 不支持

SQL Server 支持 OUTPUT INSERTED.Id,但 MySQL **没有等效语法**。别尝试写 INSERT ... OUTPUT,会报语法错误。必须用 LAST_INSERT_ID() 方案。

基本上就这些。核心就一条:INSERT 后立刻用同一连接(最好同事务)执行 SELECT LAST_INSERT_ID(),用 ExecuteScalar 拿结果。不复杂但容易忽略连接复用和事务边界。