Dapper ile ekleme ve iade ekleme kimliğini nasıl gerçekleştiririm?


170

Veritabanına bir ekleme nasıl yaparım ve Dapper ile eklenen kimliği nasıl döndürürüm?

Ben böyle bir şey denedim:

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

Ama işe yaramadı.

@Marc Gravell cevap için teşekkürler. Çözümünüzü denedim, ancak yine de aynı istisna izlemesi aşağıda

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

Yanıtlar:


287

It does destek (dahil giriş / çıkış parametreleri RETURNkullanırsanız değeri) DynamicParameters, ancak bu durumda basit bir seçenektir basitçe:

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

SQL Server'ın daha yeni sürümlerinde aşağıdaki OUTPUTmaddeyi kullanabileceğinizi unutmayın :

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

11
@ppiotrowicz hmmm .... lanet SCOPEIDENTITY geri dönecek numericha? Belki de orijinal kodunuzu kullanın ve select @id? (bu sadece bir döküm ekler). Bunun gelecekteki dapper yapılarında otomatik olarak çalıştığından emin olmak için not edeceğim. Şimdilik başka bir seçenek select cast(SCOPE_IDENTITY() as int)- yine biraz çirkin. Bunu düzeltirim.
Marc Gravell

2
@MarcGravell: Vay canına! Harika Marc, bu iyi bir tane! scope_identityDönüş türünün farkında değildim numeric(38,0). + 1 gerçekten iyi bir bulma. Asla gerçekten olsa ve eminim sadece ben değilim.
Robert Koritnik

5
Hey, bu cevap, bir dapper sorgusundan kimlik değeri almak için bir numaralı hit. Bir nesneye bağlanırken bunun büyük ölçüde iyileştirildiğinden bahsettiniz; bunu şimdi nasıl yapacağınıza dair düzenleyebilir ve güncelleme yapabilir misiniz? Nov26'12 yorumunuzun yakınındaki github'daki Testler dosyasındaki düzeltmeleri kontrol ettim, ancak şu soru ile ilgili bir şey göremiyorum: / Benim varsayımım Query<foo>değerlerin eklenmesinden sonra * = id = SCOPE_IDENTITY () 'i seçtiğidir.

2
@Xerxes, bunun CQS'yi ihlal ettiğini düşündüren nedir? CQS, bir SQL işleminin bir ızgara döndürüp döndürmediğiyle ilgili değildir. Bu saf ve basit bir emirdir. Bu kelimeyi kullanmasına rağmen, CQS açısından bir sorgu değildir Query.
Marc Gravell

3
Nitpicky, ancak Queryiade koleksiyonundan ilk değeri kullanmak ve almak yerine ExecuteScalar<T>, bu durumda daha fazla mantıklı olduğunu düşünüyorum , çünkü en fazla bir değer normal olarak iade edilir.
Peter Majeed

53

KB: 2019779 , "SCOPE_IDENTITY () ve @@ IDENTITY kullanırken yanlış değerler alabilirsiniz", OUTPUT deyimi en güvenli mekanizmadır:

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
FYI, bu SCOPE_IDENTITY kullanmaktan daha yavaş olabilir ve # 5 numaralı güncellemede SQL Server 2008 R2 Service Pack 1'e düzeltildi.
Michael Silver

2
@MichaelSilver Eğer kullanım tavsiye edersiniz SCOPE_IDENTITY veya @@ KİMLİĞİNİ daha önce ÇIKIŞ ? KB: 2019779 edildi FIXED ?
Kiquenet

1
@Kiquenet, kodu sabit olmayan bir DB'ye karşı yazıyor olsaydım, büyük olasılıkla OUTPUT deyimini yalnızca beklendiği gibi çalıştığından emin olmak için kullanırdım.
Michael Silver

1
@ bu tek bir kayıt eklemek için harika çalışıyor ama bir koleksiyona An enumerable sequence of parameters (arrays, lists, etc) is not allowed in this context
geçersem

44

Geç bir cevap, ama burada kullandığımız cevaplara bir alternatif varSCOPE_IDENTITY() : OUTPUT INSERTED

Eklenen nesnenin yalnızca kimliğini döndür:

Eklenen satırın tüm özelliklerini veya bazı niteliklerini almanızı sağlar:

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

Girilen nesneyi kimliğiyle döndür:

İsterseniz, Phoneve Emailhatta eklenen satırın tamamını alabilirsiniz :

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

Ayrıca, bununla silinen veya güncellenen satırların verilerini döndürebilirsiniz . Tetikleyicileri kullanıyorsanız dikkatli olun çünkü (daha önce belirtilen bağlantıdan):

OUTPUT öğesinden döndürülen sütunlar, INSERT, UPDATE veya DELETE deyimi tamamlandıktan sonra ancak tetikleyiciler yürütülmeden önceki verileri yansıtır.

INSTEAD OF tetikleyicileri için, döndürülen sonuçlar, tetikleyici işleminin sonucu olarak hiçbir değişiklik yapılmasa bile INSERT, UPDATE veya DELETE gerçekleşmiş gibi oluşturulur. Bir tetikleyicinin gövdesi içinde OUTPUT yan tümcesi içeren bir ifade kullanılıyorsa, sütun referanslarını OUTPUT ile ilişkili INSERTED ve DELETED tablolarıyla yinelemekten kaçınmak için tetikleyici eklenen ve silinen tablolara başvuruda bulunmak için tablo diğer adları kullanılmalıdır.

Dokümanlar'da daha fazlası: bağlantı


1
@Kiquenet TransactionScope nesnesi sorgu ile kullanılacak. Daha fazla bilgiyi burada bulabilirsiniz: dapper-tutorial.net/transaction ve burada: stackoverflow.com/questions/10363933/…
Tadija Bagarić

Burada 'QuerySingle <int>' yerine 'ExecuteScalarAsync <int>' kullanabilir miyiz?
Ebleme

6

Aldığınız InvalidCastException, SCOPE_IDENTITY öğesinin Ondalık (38,0) olması nedeniyle .

Aşağıdaki gibi yayınlayarak int olarak döndürebilirsiniz:

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'e karşı çalışıyorum ya da değil çünkü emin değilim ama işe almak için bunu yapmak zorunda kaldı.

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 olarak) </code> 'ı deneyin; 2000'de de çalışmalıdır.
David Aleu

denedin select cast(SCOPE_IDENTITY() as int)mi
Kiquenet

1

Hayatınızı kolaylaştırmak için harika bir kütüphane var Dapper.Contrib.Extensions. Bunu ekledikten sonra şunları yazabilirsiniz:

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

0

Dapper kullanıyorsanız.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 nedir?
Kiquenet

@Kirquenet, Dapper, Dapper.SimpleCRUD, Dapper.SimpleCRUD.ModelGenerator, Dapper.SimpleLoad ve Dapper.SimpleSave'i bir süre önce çalıştığım bir projede kullandım. Bunları nuGet ithalatları ile ekledim. Sitem için tüm DAO'yu iskele etmek için onları bir T4 şablonuyla birleştirdim. github.com/Paymentsense/Dapper.SimpleSave github.com/Paymentsense/Dapper.SimpleLoad
Lodlaiden
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.