ฉันจะทำการแทรกและส่งกลับข้อมูลประจำตัวที่แทรกด้วย Dapper ได้อย่างไร


170

ฉันจะทำการแทรกไปยังฐานข้อมูลและส่งกลับข้อมูลประจำตัวที่แทรกด้วย 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

คำตอบ:


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 .... แดน SCOPEIDENTITY กำลังจะกลับมาnumericใช่มั้ย? อาจใช้รหัสเดิมของคุณและselect @id? (นี่เป็นเพียงการเพิ่มนักแสดง) ฉันจะทำบันทึกเพื่อให้แน่ใจว่างานนี้โดยอัตโนมัติในอนาคตสร้าง dapper ตัวเลือกอื่นสำหรับตอนนี้คือselect cast(SCOPE_IDENTITY() as int)- อีกครั้งน่าเกลียดเล็กน้อย ฉันจะแก้ไขสิ่งนี้
Marc Gravell

2
@MarcGravell: ว้าว! Great Marc นั่นเป็นสิ่งที่ดี! ฉันไม่ทราบว่าscope_identityประเภทการคืนสินค้านั้นคือnumeric(38,0)อะไร +1 การค้นหาที่ดีจริงๆ ไม่เคยคิดมาก่อนและฉันแน่ใจว่าฉันไม่ใช่คนเดียว
Robert Koritnik

5
เฮ้คำตอบนี้เป็นที่นิยมในการรับค่าข้อมูลประจำตัวกลับมาจากการสอบถาม Dapper คุณพูดถึงว่าสิ่งนี้ได้รับการปรับปรุงอย่างมากเมื่อผูกกับวัตถุ คุณสามารถแก้ไขและให้ข้อมูลอัปเดตเกี่ยวกับวิธีการที่คุณทำตอนนี้ได้หรือไม่? ฉันตรวจสอบการแก้ไขในไฟล์ทดสอบบน github ใกล้กับความคิดเห็น Nov26'12 ของคุณ แต่ไม่เห็นสิ่งใดที่เกี่ยวข้องกับคำถาม: / ข้อสันนิษฐานของฉันคือQuery<foo>ค่าแทรกนั้นเลือก * where id = SCOPE_IDENTITY ()

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
FYI, นี่อาจช้ากว่าการใช้ SCOPE_IDENTITY และได้รับการแก้ไขในการปรับปรุง # 5 เป็น SQL Server 2008 R2 Service Pack 1
Michael Silver

2
@MichaelSilver คุณแนะนำให้ใช้SCOPE_IDENTITYหรือ@@ IDENTITYก่อนหน้าOUTPUTหรือไม่ KB: 2019779ถูกแก้ไข ?
Kiquenet

1
@ Kiquenet ถ้าฉันเขียนรหัสกับฐานข้อมูลที่ไม่ได้รับการแก้ไขฉันอาจจะใช้ส่วนคำสั่ง OUTPUT เพียงเพื่อให้แน่ใจว่ามันทำงานได้ตามที่คาดไว้
Michael Silver

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:

หากคุณต้องการคุณสามารถรับ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 ถูกใช้ภายในเนื้อความของทริกเกอร์ต้องใช้ชื่อแทนตารางเพื่ออ้างอิงทริกเกอร์ที่แทรกและตารางที่ถูกลบเพื่อหลีกเลี่ยงการทำซ้ำการอ้างอิงคอลัมน์กับตาราง INSERTED และ DELETED ที่เกี่ยวข้องกับ OUTPUT

เพิ่มเติมในลิงก์เอกสาร:


1
@Kiquenet TransactionScope วัตถุที่จะใช้กับแบบสอบถาม ดูเพิ่มเติมได้ที่นี่: dapper-tutorial.net/transactionและที่นี่: stackoverflow.com/questions/10363933/ …
Tadija Bagarić

เราสามารถใช้ 'ExecuteScalarAsync <int>' ที่นี่แทน 'QuerySingle <int>' ได้หรือไม่
Ebleme

6

InvalidCastException ที่คุณได้รับเป็นเพราะ SCOPE_IDENTITY เป็นทศนิยม (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();

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> เลือกนักแสดง (SCOPE_IDENTITY () เป็น int) </code> และควรทำงานในปี 2000 ด้วย
David Aleu

คุณลอง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 ฉันรวมมันกับเทมเพลต T4 เพื่อนั่งร้าน DAO ทั้งหมดสำหรับเว็บไซต์ของฉัน github.com/Paymentsense/Dapper.SimpleSave github.com/Paymentsense/Dapper.SimpleLoad
Lodlaiden
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.