Dapper로 삽입 및 반환 삽입 ID를 어떻게 수행합니까?


170

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

답변:


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 .... darn SCOPEIDENTITY가 돌아올 것입니다 numeric. 아마도 원래 코드를 사용 select @id합니까? (이것은 단지 캐스트를 추가합니다). 향후 dapper 빌드에서 이것이 자동으로 작동하도록 메모 할 것입니다. 현재 또 다른 옵션 select cast(SCOPE_IDENTITY() as int)은 약간 추악한 것입니다. 이 문제를 해결하겠습니다.
Marc Gravell

2
@MarcGravell : 와우! 그레이트 마크, 그거 좋은거야! 나는 몰랐어요 scope_identity반환 형식입니다 numeric(38,0). 찾기 좋은 +1 결코 그렇지는 않지만 나는 유일한 사람이 아니라고 확신합니다.
Robert Koritnik

5
이 답변은 dapper 쿼리에서 ID 값을 다시 얻는 데 가장 큰 영향을 미칩니다. 객체에 바인딩 할 때이 기능이 크게 향상되었다고 언급했습니다. 지금 어떻게하는지 편집하고 업데이트 할 수 있습니까? Nov26'12 주석 근처의 github에있는 Tests 파일에서 개정을 확인했지만 질문과 관련이 없습니다 : / 내 가정은 Query<foo>값을 삽입 한 다음 *를 선택하는 것입니다.

2
@Xerxes 이것이 CQS를 위반한다고 생각하는 이유는 무엇입니까? CQS는 SQL 작업이 그리드를 반환하는지 여부에 관한 것이 아닙니다. 이 명령은 순수하고 단순합니다. 이 단어를 사용하더라도 CQS 용어에서는 쿼리가 아닙니다 Query.
Marc Gravell

3
Nitpicky이지만 Query반환 된 컬렉션에서 첫 번째 값을 사용 하고 얻는 것이 아니라 ExecuteScalar<T>최대 하나의 값이 정상적으로 반환 되므로이 경우 더 의미가 있다고 생각 합니다.
Peter Majeed

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 서비스 팩 1로 수정되었습니다.
Michael Silver

2
@MichaelSilver OUTPUT 보다 SCOPE_IDENTITY 또는 @@ IDENTITY 를 사용하는 것이 좋습니다 ? KB : 2,019,779가 되었다 고정 ?
Kiquenet

1
@Kiquenet, 수정되지 않은 DB에 대해 코드를 작성하는 경우 OUTPUT 절을 사용하여 예상대로 작동하는지 확인합니다.
마이클 실버

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가 삽입 된 객체를 반환합니다.

당신이 원하는 경우에 당신은 얻을 수 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/...
tadija는 Bagarić

'QuerySingle <int>'대신 'ExecuteScalarAsync <int>'를 사용할 수 있습니까?
Ebleme

6

SCOPE_IDENTITY가 10 진수 (38,0) 이기 때문에 InvalidCastException이 발생했습니다. 입니다.

다음과 같이 캐스팅하여 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> cast (SCOPE_IDENTITY () int)를 선택하십시오 </ code>를 선택하면 2000에서도 작동합니다.
David Aleu 2016 년

당신은 시도 select cast(SCOPE_IDENTITY() as int)했습니까?
Kiquenet

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 가져 오기를 통해 추가했습니다. 내 사이트의 모든 DAO를 비계하기 위해 T4 템플릿과 결합했습니다. github.com/Paymentsense/Dapper.SimpleSave github.com/Paymentsense/Dapper.SimpleLoad
Lodlaiden
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.