如何执行插入并使用Dapper返回插入的身份?


170

如何执行插入到数据库并使用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

Answers:


286

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

var id = connection.QuerySingle<int>( @"
INSERT INTO [MyTable] ([Stuff]) VALUES (@Stuff);
SELECT CAST(SCOPE_IDENTITY() as int)", new { Stuff = mystuff});

请注意,在较新版本的SQL Server上,可以使用以下OUTPUT子句:

var id = connection.QuerySingle<int>( @"
INSERT INTO [MyTable] ([Stuff])
OUTPUT INSERTED.Id
VALUES (@Stuff);", new { Stuff = mystuff});

11
@ppiotrowicz hmmm ....织补范围即将恢复numeric,是吗?也许使用您的原始代码和select @id?(这只是添加了一个强制转换)。我将做笔记以确保在将来的dapper版本中自动运行。现在的另一个选择是select cast(SCOPE_IDENTITY() as int)-再次丑陋。我会解决这个问题。
马克·格雷韦尔

2
@MarcGravell:哇!马克,太好了!我没有意识到scope_identity返回类型是numeric(38,0)。+1个非常好的发现。从来没有,我敢肯定我不是唯一的一个。
罗伯特·科里特尼克

5
嘿,这个答案是从精简查询中获取身份值的第一击。您提到绑定到对象时,这有了很大的改进。您可以编辑并提供有关如何执行此操作的更新吗?我在github上的Tests文件中检查了修订版本,靠近您的Nov26'12注释,但是没有看到与该问题相关的任何内容:/我的假设是Query<foo>插入值然后选择*,其中id = SCOPE_IDENTITY()。

2
@Xerxes是什么让您认为这违反了CQS?CQS与SQL操作是否返回网格无关。这是一条命令,纯净而简单。尽管使用了单词,但这不是CQS术语中的查询Query
Marc Gravell

3
我很挑剔,但Query我认为ExecuteScalar<T>在这种情况下更有意义,因为通常最多返回一个值,而不是使用并从返回的集合中获取第一个值。
Peter Majeed 2015年

53

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

14
仅供参考,这可能是慢于使用SCOPE_IDENTITY和固定在更新#5到SQL Server 2008 R2的Service Pack 1
迈克尔·西尔弗

2
@MichaelSilver您是否建议在输出之前使用SCOPE_IDENTITY@@ IDENTITYKB:2019779修复
Kiquenet '17

1
@Kiquenet,如果我是针对未固定的数据库编写代码,则可能会使用OUTPUT子句以确保其按预期工作。
Michael Silver

1
@this非常适合插入单个记录,但是如果我通过集合,我会得到An enumerable sequence of parameters (arrays, lists, etc) is not allowed in this context
MaYaN

44

较晚的答案,但这是我们最终使用的答案的替代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为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子句的语句,则必须使用表别名来引用触发器插入和删除的表,以避免将列引用与与OUTPUT关联的INSERTED和DELETED表重复。

在文档中进一步了解它:链接


1
@Kiquenet TransactionScope对象与查询一起使用。更可以在这里找到:dapper-tutorial.net/transaction这里:stackoverflow.com/questions/10363933/...
TadijaBagarić

我们可以在这里使用“ ExecuteScalarAsync <int>”而不是“ QuerySingle <int>”吗?
Ebleme '19

6

您收到的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

不知道是否是因为我是否在使用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();

2
尝试<code> select cast(SCOPE_IDENTITY()int)</ code>,它也应该在2000年工作。
David Aleu 2012年

你试过了select cast(SCOPE_IDENTITY() as int)吗?
Kiquenet '17

1

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

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

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

什么是Dapper.SimpleSave?
Kiquenet

@Kirquenet,我在一段时间前使用的项目中使用了Dapper,Dapper.SimpleCRUD,Dapper.SimpleCRUD.ModelGenerator,Dapper.SimpleLoad和Dapper.SimpleSave。我通过nuGet导入添加了它们。我将它们与T4模板结合在一起,为我的站点搭建了所有DAO。github.com/Paymentsense/Dapper.SimpleSave github.com/Paymentsense/Dapper.SimpleLoad
Lodlaiden
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.