如何对数据库执行插入并使用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
如果你使用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();
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();
一个迟到的答案,但这里是我们最终使用的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的插入对象:
如果你想要你可以得到Phone
和Email
甚至整个插入的行:
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表重复列引用。
更多关于它的文档:link
您获得的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();
不确定是不是因为我正在使用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();
如果你正在使用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.Contrib.Extensions。包括这个之后你可以写:
public int Add(Transaction transaction)
{
using (IDbConnection db = Connection)
{
return (int)db.Insert(transaction);
}
}