如何使用Dapper执行插入并返回插入的标识?

ppi*_*icz 161 c# sql-server dapper

如何对数据库执行插入并使用Dapper返回插入的标识?

我尝试过这样的事情:

string sql = "DECLARE @ID int; " +
             "INSERT INTO [MyTable] ([Stuff]) VALUES (@Stuff); " +
             "SELECT @ID = SCOPE_IDENTITY()";

var id = connection.Query<int>(sql, new { Stuff = mystuff}).First();
Run Code Online (Sandbox Code Playgroud)

但它没有用.

@Marc Gravell谢谢你的回复.我已经尝试过你的解决方案但是,下面仍有相同的异常跟踪

System.InvalidCastException: Specified cast is not valid

at Dapper.SqlMapper.<QueryInternal>d__a`1.MoveNext() in (snip)\Dapper\SqlMapper.cs:line 610
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Dapper.SqlMapper.Query[T](IDbConnection cnn, String sql, Object param, IDbTransaction transaction, Boolean buffered, Nullable`1 commandTimeout, Nullable`1 commandType) in (snip)\Dapper\SqlMapper.cs:line 538
at Dapper.SqlMapper.Query[T](IDbConnection cnn, String sql, Object param) in (snip)\Dapper\SqlMapper.cs:line 456
Run Code Online (Sandbox Code Playgroud)

Mar*_*ell 267

如果使用它确实支持输入/输出参数(包括RETURN值)DynamicParameters,但在这种情况下,更简单的选项就是:

string sql = @"
INSERT INTO [MyTable] ([Stuff]) VALUES (@Stuff);
SELECT CAST(SCOPE_IDENTITY() as int)";

var id = connection.Query<int>(sql, new { Stuff = mystuff}).Single();
Run Code Online (Sandbox Code Playgroud)

  • @ppiotrowicz hmmm .... darn SCOPEIDENTITY将返回`numeric`,是吗?也许使用你的原始代码和`select @ id`?(这只是添加演员).我将做一个注释,以确保它在未来的dapper构建中自动运行.现在的另一个选择是`select cast(SCOPE_IDENTITY()as int)` - 再次,有点难看.我会解决这个问题. (10认同)
  • 嘿,这个答案是从一个精巧的查询中获取身份值的第一个答案.您提到在绑定到对象时,这会得到极大的改进; 你可以编辑并更新你现在如何做到这一点吗?我检查了你的11月12日评论附近的github上的测试文件中的修订版,但没有看到任何与该问题相关的内容:/我的假设是`Query <foo>`插入值然后选择*where id = SCOPE_IDENTITY(). (5认同)
  • Nitpicky,但不是使用`Query`并从返回的集合中获取第一个值,我认为`ExecuteScalar <T>`在这种情况下更有意义,因为通常返回一个值. (3认同)
  • @MarcGravell:哇!伟大的马克,这是一个很好的!我没有意识到`scope_identity`返回类型是`numeric(38,0)`.+1一个非常好的发现.从来没有真的,我相信我不是唯一的. (2认同)
  • @Xerxes是什么让你认为这违反了CQS?CQS不是关于SQL操作是否返回网格.这是一个纯粹而简单的命令.尽管使用了"Query"这个词,但这不是CQS术语中的查询. (2认同)
  • `@@IDENTITY 和 SCOPE_IDENTITY()` 是 ***evil*** 吗?更好地使用`OUTPUT Inserted.Id`? (2认同)

jww*_*jww 49

KB:2019779,"使用SCOPE_IDENTITY()和@@ IDENTITY时可能会收到不正确的值",OUTPUT子句是最安全的机制:

string sql = @"
DECLARE @InsertedRows AS TABLE (Id int);
INSERT INTO [MyTable] ([Stuff]) OUTPUT Inserted.Id INTO @InsertedRows
VALUES (@Stuff);
SELECT Id FROM @InsertedRows";

var id = connection.Query<int>(sql, new { Stuff = mystuff}).Single();
Run Code Online (Sandbox Code Playgroud)

  • 仅供参考,这可能比使用SCOPE_IDENTITY慢,并在更新#5中修复为SQL Server 2008 R2 Service Pack 1. (14认同)
  • @MichaelSilver你建议使用***SCOPE_IDENTITY***或***@@ IDENTITY***之前***OUTPUT***?_KB:2019779_被**固定**? (2认同)

Tad*_*rić 38

一个迟到的答案,但这里是我们最终使用的答案的替代方案SCOPE_IDENTITY():OUTPUT INSERTED

仅返回插入对象的ID:

它允许您获取插入行的全部或部分属性:

string insertUserSql = @"INSERT INTO dbo.[User](Username, Phone, Email)
                        OUTPUT INSERTED.[Id]
                        VALUES(@Username, @Phone, @Email);";

int newUserId = conn.QuerySingle<int>(insertUserSql,
                                new
                                {
                                    Username = "lorem ipsum",
                                    Phone = "555-123",
                                    Email = "lorem ipsum"
                                }, tran);
Run Code Online (Sandbox Code Playgroud)

返回带ID的插入对象:

如果你想你能得到PhoneEmail甚至整个插排:

string insertUserSql = @"INSERT INTO dbo.[User](Username, Phone, Email)
                        OUTPUT INSERTED.*
                        VALUES(@Username, @Phone, @Email);";

User newUser = conn.QuerySingle<User>(insertUserSql,
                                new
                                {
                                    Username = "lorem ipsum",
                                    Phone = "555-123",
                                    Email = "lorem ipsum"
                                }, tran);
Run Code Online (Sandbox Code Playgroud)

此外,使用此功能,您可以返回已删除更新的行的数据.如果您使用触发器,请小心,因为:

从OUTPUT返回的列反映了INSERT,UPDATE或DELETE语句完成之后但在执行触发器之前的数据.

对于INSTEAD OF触发器,生成的返回结果就像INSERT,UPDATE或DELETE实际发生一样,即使触发操作没有发生任何修改也是如此.如果在触发器主体内部使用包含OUTPUT子句的语句,则必须使用表别名来引用触发器插入和删除的表,以避免使用与OUTPUT关联的INSERTED和DELETED表重复列引用.

更多关于它的文档:链接


bpr*_*ard 5

您获得的InvalidCastException是由于SCOPE_IDENTITY是十进制(38,0).

您可以通过如下方式将其作为int返回:

string sql = @"
INSERT INTO [MyTable] ([Stuff]) VALUES (@Stuff);
SELECT CAST(SCOPE_IDENTITY() AS INT)";

int id = connection.Query<int>(sql, new { Stuff = mystuff}).Single();
Run Code Online (Sandbox Code Playgroud)