データベースへの挿入を実行し、Dapperで挿入されたIDを返すにはどうすればよいですか?
私はこのようなことを試しました:
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
を使用するとsupport入出力パラメーター(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);
また、これにより、deletedまたはupdated行のデータを返すことができます。次の理由でトリガーを使用している場合は注意してください。
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に反対しているからかどうかはわかりませんが、SQLを機能させるにはこれをしなければなりませんでした。
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);
}
}