จะใช้ธุรกรรมกับ dapper.net ได้อย่างไร?


106

ฉันต้องการเรียกใช้คำสั่งแทรกหลายรายการในหลายตาราง ฉันใช้ dapper.net ฉันไม่เห็นวิธีจัดการธุรกรรมกับ dapper.net เลย

โปรดแบ่งปันแนวคิดของคุณเกี่ยวกับวิธีใช้ธุรกรรมกับ dapper.net

คำตอบ:


107

นี่คือข้อมูลโค้ด:

using System.Transactions;    
....    
using (var transactionScope = new TransactionScope())
{
    DoYourDapperWork();
    transactionScope.Complete();
}

โปรดทราบว่าคุณจำเป็นต้องเพิ่มการอ้างอิงไปยังSystem.Transactionsแอสเซมบลีเนื่องจากไม่ได้อ้างอิงตามค่าเริ่มต้น


7
จำเป็นต้องย้อนกลับอย่างชัดเจนเมื่อเกิดข้อผิดพลาดหรือ System.Transactions จัดการโดยอัตโนมัติหรือไม่
Norbert Norbertson

6
@NorbertNorbertson มันทำโดยอัตโนมัติในDispose()วิธีการ หากComplete()ยังไม่ถูกเรียกธุรกรรมจะถูกย้อนกลับ
the_joric

4
ควรกล่าวถึงเนื่องจากคำตอบอื่น ( stackoverflow.com/a/20047975/47672 ): ต้องเปิดการเชื่อมต่อภายในการTransctionScopeใช้บล็อกในกรณีที่คุณเลือกคำตอบนี้
0x49D1

2
ดูเพิ่มเติม ( stackoverflow.com/a/20047975/444469 ) - DoYouDapperWork (Execute, Query, etc ... ) ต้องการการทำธุรกรรมในพารามิเตอร์
Matthieu

การย้อนกลับจะถูกเรียกโดยอัตโนมัติหรือไม่หากมีปัญหา?
แกนดัล์ฟ

92

ฉันต้องการใช้วิธีการที่ง่ายกว่าโดยรับธุรกรรมโดยตรงจากการเชื่อมต่อ:

// This called method will get a connection, and open it if it's not yet open.
using (var connection = GetOpenConnection())
using (var transaction = connection.BeginTransaction())
{
    connection.Execute(
        "INSERT INTO data(Foo, Bar) values (@Foo, @Bar);", listOf5000Items, transaction);
    transaction.Commit();
}

@ANeves: เราอาจใช้เฟรมเวิร์ก Dapper ที่แตกต่างกันเพราะอันนี้มี: github.com/StackExchange/dapper-dot-net
andrecarlucci

26
ต้องโทรไปที่ connection.open () ก่อน. begintransaction
Timeless

การเชื่อมต่อจะไม่ถูกบังคับโดยอัตโนมัติในขอบเขตธุรกรรมเว้นแต่คุณจะเปิดการเชื่อมต่อภายในขอบเขตธุรกรรม ฉันไม่รู้ว่ารหัสของคุณทำงานอย่างไรถ้า GetOpenConnection เปิดตัวเองอย่างน่าอัศจรรย์ภายในขอบเขตธุรกรรม แต่ฉันขอเดิมพันว่ามันไม่ได้
Erik Bergstedt

@ErikBergstedt คุณกำลังบอกว่าการเชื่อมต่อจะต้องเปิดได้หลังจากที่เราโทรหา.BeginTransaction()เท่านั้นหรือไม่? หากเป็นเช่นนั้นวิธีการส่วนขยายนี้จะส่งเสริมการใช้ธุรกรรมที่ไม่ถูกต้อง (IMO ควรโยน "ไม่สามารถเปิดธุรกรรมได้หลังจากที่การเชื่อมต่อเปิดอยู่แล้ว")
ANeves คิดว่า SE เป็นสิ่งชั่วร้าย

2
จุดที่ดีที่จะรวมธุรกรรมเป็นพารามิเตอร์Executeด้วยเนื่องจากจำเป็น
Arve Systad


10

เมื่อพิจารณาว่าตารางทั้งหมดของคุณอยู่ในฐานข้อมูลเดียวฉันไม่เห็นด้วยกับTransactionScopeโซลูชันที่แนะนำในบางคำตอบที่นี่ อ้างถึงนี้คำตอบ

  1. TransactionScopeโดยทั่วไปจะใช้สำหรับธุรกรรมแบบกระจาย ธุรกรรมที่ครอบคลุมฐานข้อมูลที่แตกต่างกันอาจอยู่ในระบบที่ต่างกัน สิ่งนี้ต้องการการกำหนดค่าบางอย่างบนระบบปฏิบัติการและ SQL Server โดยที่สิ่งนี้จะไม่ทำงาน ไม่แนะนำให้ทำเช่นนี้หากแบบสอบถามทั้งหมดของคุณขัดกับฐานข้อมูลอินสแตนซ์เดียว
    แต่ด้วยฐานข้อมูลเดียวสิ่งนี้อาจมีประโยชน์เมื่อคุณต้องรวมรหัสในธุรกรรมที่ไม่อยู่ภายใต้การควบคุมของคุณ ด้วยฐานข้อมูลเดียวไม่จำเป็นต้องมีการกำหนดค่าพิเศษเช่นกัน

  2. connection.BeginTransactionเป็นไวยากรณ์ ADO.NET เพื่อใช้ธุรกรรม (ใน C #, VB.NET ฯลฯ ) กับฐานข้อมูลเดียว สิ่งนี้ใช้ไม่ได้กับหลายฐานข้อมูล

ดังนั้น, connection.BeginTransaction()เป็นวิธีที่ดีกว่าที่จะไป

แม้จะเป็นวิธีที่ดีในการจัดการการทำธุรกรรมที่จะใช้ UnitOfWork ที่อธิบายไว้ในนี้คำตอบ


4
ไม่จำเป็นต้องมีฐานข้อมูลหลายฐานข้อมูลเพื่อรับประโยชน์จาก TransactionScope ยูทิลิตี้เฉพาะอย่างยิ่งก็คือสภาพแวดล้อม เหมาะสำหรับการตัดรหัสที่คุณไม่ได้เป็นเจ้าของหรือไม่สามารถแก้ไขได้ในธุรกรรม ตัวอย่างเช่นสามารถใช้เพื่อให้ได้ผลอย่างมากเมื่อโค้ดการทดสอบหน่วย / การรวมที่เรียกฐานข้อมูลในตำแหน่งที่คุณต้องการย้อนกลับ เพียงลอย TransactionScope ทดสอบโค้ดและกำจัดในระหว่างการล้างข้อมูลทดสอบ
Larry Smith

3
@LarrySmith: เห็นด้วย; แต่คำถามไม่เกี่ยวกับเรื่องนี้ OP เพียงบอกว่าเขาต้องการแทรกในหลาย ๆ ตารางในธุรกรรมเดียว บางคำตอบรวมถึงคำตอบที่ได้รับการยอมรับแนะนำให้ใช้TransactionScopeซึ่งไม่มีประสิทธิภาพสำหรับสิ่งที่ OP ต้องการ ฉันยอมรับว่าTransactionScopeเป็นเครื่องมือที่ดีในหลาย ๆ กรณี แต่ไม่ใช่สิ่งนี้
Amit Joshi

5

คำตอบของแดเนียลได้ผลสำหรับฉัน เพื่อความสมบูรณ์นี่คือตัวอย่างข้อมูลที่แสดงการกระทำและการย้อนกลับโดยใช้ขอบเขตธุรกรรมและ dapper:

using System.Transactions;
    // _sqlConnection has been opened elsewhere in preceeding code 
    using (var transactionScope = new TransactionScope())
    {
        try
        {
            long result = _sqlConnection.ExecuteScalar<long>(sqlString, new {Param1 = 1, Param2 = "string"});

            transactionScope.Complete();
        }
        catch (Exception exception)
        {
            // Logger initialized elsewhere in code
            _logger.Error(exception, $"Error encountered whilst executing  SQL: {sqlString}, Message: {exception.Message}")

            // re-throw to let the caller know
            throw;
        }
    } // This is where Dispose is called 

2
@usr ที่ขึ้นอยู่กับความชอบส่วนบุคคล ฉันชอบที่จะรู้ว่ามีข้อผิดพลาดเกิดขึ้นเป็นครั้งแรกและไม่เห็นว่าข้อความบันทึกเป็นขยะ นอกจากนี้คำตอบของฉันยังคงให้คุณค่ากับโฆษณาด้วยการสาธิตวิธีหนึ่งในการใช้ธุรกรรมกับ
dapper

@CodeNaked ก่อนอื่นคุณสั่งผิดที่นั่น บล็อกจับจะถูกโจมตีก่อนหากมีข้อยกเว้นจากนั้นจึงสิ้นสุดขอบเขตสำหรับการใช้งาน ประการที่สองดูที่คำตอบนี้และเอกสาร MSDN ที่อ้างอิง: stackoverflow.com/a/5306896/190476 การโทรกำจัดครั้งที่สองไม่เป็นอันตรายวัตถุที่ออกแบบมาอย่างดีจะไม่สนใจการเรียกที่สอง การโหวตลงคะแนนไม่ชอบธรรม!
Sudhanshu Mishra

@dotnetguy - ฉันไม่ได้พยายามที่จะสื่อสารว่าDisposeวิธีใดเรียกว่าแรกหรือที่สองเพียงแค่เรียกว่าสองครั้ง สำหรับประเด็นที่ว่า "การโทรทิ้งครั้งที่สองไม่เป็นอันตราย" นั่นเป็นข้อสันนิษฐานที่สำคัญ ฉันได้เรียนรู้ว่าเอกสารและการใช้งานจริงมักไม่เห็นด้วย แต่ถ้าคุณต้องการคำพูดของ Microsoft: msdn.microsoft.com/en-us/library/…
CodeNaked

3
ดังนั้นคำเตือนการวิเคราะห์โค้ดคือเหตุผลที่คุณจะลดคะแนน? นั่นไม่ได้ทำให้คำตอบผิดหรือทำให้เข้าใจผิดนั่นคือเมื่อการโหวตลดลงมีความเหมาะสม ทำไมคุณไม่แก้ไขคำตอบและเสนอวิธีแก้ปัญหาที่ดีกว่าในขณะที่ยังคงฟังก์ชันการทำงานไว้ Stack overflow เป็นข้อมูลเกี่ยวกับการช่วยเหลือและการวิจารณ์ที่สร้างสรรค์
Sudhanshu Mishra
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.