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

时间:2011-11-25 14:02:16

标签: 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();

但它没有用。

@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

9 个答案:

答案 0 :(得分:252)

如果您使用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();

答案 1 :(得分:47)

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();

答案 2 :(得分:30)

迟到的答案,但这是我们最终使用的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);

返回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);

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

  

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

     

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

在文档中有更多内容:link

答案 3 :(得分:5)

您获得的InvalidCastException是由于SCOPE_IDENTITY为Decimal(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();

答案 4 :(得分:4)

不确定是不是因为我正在使用SQL 2000,但是我必须这样做才能让它工作。

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

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

答案 5 :(得分:1)

我看到了sql server的答案,那么这里是针对使用事务的MySql


    Dim sql As String = "INSERT INTO Empleado (nombres, apepaterno, apematerno, direccion, colonia, cp, municipio, estado, tel, cel, correo, idrol, relojchecadorid, relojchecadorid2, `activo`,`extras`,`rfc`,`nss`,`curp`,`imagen`,sueldoXHra, IMSSCotiza, thumb) VALUES (@nombres, @apepaterno, @apematerno, @direccion, @colonia, @cp, @municipio, @estado, @tel, @cel, @correo, @idrol, @relojchecadorid, @relojchecadorid2, @activo, @extras, @rfc, @nss, @curp, @imagen,@sueldoXHra,@IMSSCotiza, @thumb)"
    
            Using connection As IDbConnection = New MySqlConnection(getConnectionString())
                connection.Open()
                Using transaction = connection.BeginTransaction
                    Dim res = connection.Execute(sql, New With {reg.nombres, reg.apepaterno, reg.apematerno, reg.direccion, reg.colonia, reg.cp, reg.municipio, reg.estado, reg.tel, reg.cel, reg.correo, reg.idrol, reg.relojchecadorid, reg.relojchecadorid2, reg.activo, reg.extras, reg.rfc, reg.nss, reg.curp, reg.imagen, reg.thumb, reg.sueldoXHra, reg.IMSSCotiza}, commandTimeout:=180, transaction:=transaction)
                    lastInsertedId = connection.ExecuteScalar("SELECT LAST_INSERT_ID();", transaction:=transaction)
                    If res > 0 Then 
transaction.Commit()
return true
end if
                    
                End Using
            End Using

答案 6 :(得分:0)

如果你正在使用Dapper.SimpleSave:

 //no safety checks
 public static int Create<T>(object param)
    {
        using (SqlConnection conn = new SqlConnection(GetConnectionString()))
        {
            conn.Open();
            conn.Create<T>((T)param);
            return (int) (((T)param).GetType().GetProperties().Where(
                    x => x.CustomAttributes.Where(
                        y=>y.AttributeType.GetType() == typeof(Dapper.SimpleSave.PrimaryKeyAttribute).GetType()).Count()==1).First().GetValue(param));
        }
    }

答案 7 :(得分:0)

Dapper.Contrib.Extensions提供了一个强大的库,可以使您的生活更轻松。包含此内容后,您可以编写:

public int Add(Transaction transaction)
{
        using (IDbConnection db = Connection)
        {
                return (int)db.Insert(transaction);
        }
}

答案 8 :(得分:0)

我使用 .net core 3.1 和 postgres 12.3。基于 Tadija Bagarić 的回答,我最终得到了:

using (var connection = new NpgsqlConnection(AppConfig.CommentFilesConnection))
        {

            string insertUserSql = @"INSERT INTO mytable(comment_id,filename,content)
                    VALUES( @commentId, @filename, @content) returning id;";

            int newUserId = connection.QuerySingle<int>(
                                            insertUserSql,
                                            new
                                            {
                                                commentId = 1,
                                                filename = "foobar!",
                                                content = "content"
                                            }
                                            );

          


        }

其中 AppConfig 是我自己的类,它只是为我的连接详细信息设置一个字符串。这是在 Startup.cs ConfigureServices 方法中设置的。