可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
How do I perform an insert to database and return inserted identity with Dapper?
I've tried something like this:
string sql = "DECLARE @ID int; " + "INSERT INTO [MyTable] ([Stuff]) VALUES (@Stuff); " + "SELECT @ID = SCOPE_IDENTITY()"; var id = connection.Query(sql, new { Stuff = mystuff}).First();
But it did't work.
@Marc Gravell thanks, for reply. I've tried your solution but, still same exception trace is below
System.InvalidCastException: Specified cast is not valid at Dapper.SqlMapper.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
回答1:
It does support input/output parameters (including RETURN
value) if you use DynamicParameters
, but in this case the simpler option is simply:
string sql = @" INSERT INTO [MyTable] ([Stuff]) VALUES (@Stuff); SELECT CAST(SCOPE_IDENTITY() as int)"; var id = connection.Query(sql, new { Stuff = mystuff}).Single();
回答2:
KB:2019779,"You may receive incorrect values when using SCOPE_IDENTITY() and @@IDENTITY", The OUTPUT clause is the safest mechanism:
回答3:
A late answer, but here is an alternative to the SCOPE_IDENTITY()
answers that we ended up using: OUTPUT INSERTED
Return only ID of inserted object:
It allows you to get all or some attributes of the inserted row:
string insertUserSql = @"INSERT INTO dbo.[User](Username, Phone, Email) OUTPUT INSERTED.[Id] VALUES(@Username, @Phone, @Email);"; int newUserId = conn.QuerySingle(insertUserSql, new { Username = "lorem ipsum", Phone = "555-123", Email = "lorem ipsum" }, tran);
Return inserted object with ID:
If you wanted you could get Phone
and Email
or even the whole inserted row:
string insertUserSql = @"INSERT INTO dbo.[User](Username, Phone, Email) OUTPUT INSERTED.* VALUES(@Username, @Phone, @Email);"; User newUser = conn.QuerySingle(insertUserSql, new { Username = "lorem ipsum", Phone = "555-123", Email = "lorem ipsum" }, tran);
Also, with this you can return data of deleted or updated rows. Just be careful if you are using triggers because:
Columns returned from OUTPUT reflect the data as it is after the INSERT, UPDATE, or DELETE statement has completed but before triggers are executed.
For INSTEAD OF triggers, the returned results are generated as if the INSERT, UPDATE, or DELETE had actually occurred, even if no modifications take place as the result of the trigger operation. If a statement that includes an OUTPUT clause is used inside the body of a trigger, table aliases must be used to reference the trigger inserted and deleted tables to avoid duplicating column references with the INSERTED and DELETED tables associated with OUTPUT.
More on it in the docs: link
回答4:
The InvalidCastException you are getting is due to SCOPE_IDENTITY being a Decimal(38,0).
You can return it as an int by casting it as follows:
string sql = @" INSERT INTO [MyTable] ([Stuff]) VALUES (@Stuff); SELECT CAST(SCOPE_IDENTITY() AS INT)"; int id = connection.Query(sql, new { Stuff = mystuff}).Single();
回答5:
Not sure if it was because I'm working against SQL 2000 or not but I had to do this to get it to work.
string sql = "DECLARE @ID int; " + "INSERT INTO [MyTable] ([Stuff]) VALUES (@Stuff); " + "SET @ID = SCOPE_IDENTITY(); " + "SELECT @ID"; var id = connection.Query(sql, new { Stuff = mystuff}).Single();
回答6:
If you're using Dapper.SimpleSave:
//no safety checks public static int Create(object param) { using (SqlConnection conn = new SqlConnection(GetConnectionString())) { conn.Open(); conn.Create((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)); } }