เราจำเป็นต้องจัดการธุรกรรมในรหัส C # หรือในขั้นตอนการจัดเก็บ


14

เราจำเป็นต้องมีการจัดการธุรกรรมใน c # หรือไม่รวมถึงการจัดเก็บฐานข้อมูลทั้งสองด้าน

ค#:

Using(transaction with transaction scope)
{
     Execute stored proc;
     Transaction. Complete;
}

โพรซีเดอร์ที่เก็บ SQL:

Create process
As
Begin try
    Begin transaction
    Commit
End try
Begin catch
    Rollback
End catch

คำตอบ:


20

ขั้นแรกคุณควรมีการจัดการธุรกรรมที่เหมาะสมในทุกขั้นตอนของคุณเสมอเพื่อไม่ให้เกิดปัญหาถ้าพวกเขาถูกเรียกด้วยรหัสแอพโดยขั้นตอนอื่นทีละรายการในคิวรีแบบเฉพาะกิจโดยงานตัวแทนของ SQL หรือวิธีอื่น ๆ . แต่คำสั่ง DML เดียวหรือรหัสที่ไม่ได้ทำการแก้ไขใด ๆ ไม่จำเป็นต้องมีธุรกรรมที่ชัดเจน ดังนั้นสิ่งที่ฉันแนะนำคือ:

  • มีโครงสร้าง TRY / CATCH เสมอเพื่อให้สามารถแก้ไขข้อผิดพลาดได้อย่างถูกต้อง
  • เลือกรวม 3 ส่วนการจัดการธุรกรรมในรหัสด้านล่างหากคุณมีใบแจ้งยอด DML หลายใบ (เนื่องจากใบแจ้งยอดเดียวเป็นธุรกรรมในตัวเอง) อย่างไรก็ตามนอกเหนือจากการเพิ่มรหัสเพิ่มเติมบางอย่างที่ไม่จำเป็นต้องใช้โดยเฉพาะถ้าใครต้องการมีแม่แบบที่สอดคล้องกันก็จะไม่เจ็บที่จะเก็บไว้ในบล็อก IF 3 ที่เกี่ยวข้องกับการทำธุรกรรม แต่ในกรณีนี้ฉันยังคงแนะนำไม่ให้เก็บบล็อค IF 3 รายการที่เกี่ยวข้องกับธุรกรรมสำหรับโพรไฟล์แบบเลือกอย่างเดียว (เช่นอ่านอย่างเดียว)

เมื่อทำคำสั่ง DML 2 ข้อขึ้นไปคุณจำเป็นต้องใช้บางสิ่งตามบรรทัดต่อไปนี้ (ซึ่งสามารถทำได้สำหรับการดำเนินการ DML เดียวหากต้องการให้สอดคล้องกัน):

CREATE PROCEDURE [SchemaName].[ProcedureName]
(
    @Param  DataType
    ...
)
AS
SET NOCOUNT ON;
DECLARE @InNestedTransaction BIT;

BEGIN TRY

    IF (@@TRANCOUNT = 0)
    BEGIN
        SET @InNestedTransaction = 0;
        BEGIN TRAN; -- only start a transaction if not already in one
    END;
    ELSE
    BEGIN
        SET @InNestedTransaction = 1;
    END;

    -- { 2 or more DML statements (i.e. INSERT / UPDATE / DELETE) }

    IF (@@TRANCOUNT > 0 AND @InNestedTransaction = 0)
    BEGIN
        COMMIT;
    END;

END TRY
BEGIN CATCH

    IF (@@TRANCOUNT > 0 AND @InNestedTransaction = 0)
    BEGIN
        ROLLBACK;
    END;

    DECLARE @ErrorMessage   NVARCHAR(4000) = ERROR_MESSAGE(),
            @ErrorState     INT = ERROR_STATE(),
            @ErrorSeverity  INT = ERROR_SEVERITY();

    -- optionally concatenate ERROR_NUMBER() and/or ERROR_LINE() into @ErrorMessage

    RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);
    RETURN;

END CATCH;

เมื่อทำคำสั่ง DML เพียง 1 คำสั่งหรือเลือกเพียงคำสั่งเดียวคุณสามารถหลีกเลี่ยงได้ด้วยสิ่งต่อไปนี้:

CREATE PROCEDURE [SchemaName].[ProcedureName]
(
    @Param  DataType
    ...
)
AS
SET NOCOUNT ON;

BEGIN TRY

    -- { 0 or 1 DML statements (i.e. INSERT / UPDATE / DELETE) }

END TRY
BEGIN CATCH

    DECLARE @ErrorMessage   NVARCHAR(4000) = ERROR_MESSAGE(),
            @ErrorState     INT = ERROR_STATE(),
            @ErrorSeverity  INT = ERROR_SEVERITY();

    -- optionally concatenate ERROR_NUMBER() and/or ERROR_LINE() into @ErrorMessage

    RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);
    RETURN;

END CATCH;

ประการที่สองคุณควรจัดการธุรกรรมที่เลเยอร์แอปเท่านั้นกรณีที่คุณต้องดำเนินการแบบสอบถาม / จัดเก็บมากกว่า 1 รายการและทุกรายการจะต้องมีการจัดกลุ่มเป็นการดำเนินการปรมาณู การทำSqlCommand.Execute___เพียงอย่างเดียวจะต้องอยู่ในลอง / จับ แต่ไม่ได้อยู่ในการทำธุรกรรม

แต่การทำธุรกรรมที่เลเยอร์แอปนั้นทำให้เกิดความเสียหายเมื่อโทรเพียงครั้งเดียวหรือไม่? ถ้าต้องการ MSDTC (Microsoft Distributed ทรานแซกชันผู้ประสานงาน) มันเป็นเรื่องที่หนักกว่าในระบบที่จะทำสิ่งนี้ในเลเยอร์ของแอพเมื่อไม่ต้องการโดยชัดแจ้ง โดยส่วนตัวฉันชอบที่จะหลีกเลี่ยงธุรกรรมที่อิงกับเลเยอร์แอปเว้นแต่จะมีความจำเป็นอย่างยิ่งเนื่องจากเป็นการลดโอกาสในการทำธุรกรรมที่ถูกทอดทิ้ง (ถ้ามีบางอย่างผิดพลาดกับรหัสแอปก่อนที่จะทำคอมมิทหรือย้อนกลับ) ฉันก็พบว่าบางครั้งมันก็ทำให้การดีบักสถานการณ์ยากขึ้นเล็กน้อย แต่ที่ถูกกล่าวว่าฉันไม่เห็นอะไรผิดปกติทางเทคนิคกับยังจัดการการทำธุรกรรมที่ชั้น app เมื่อทำคนเดียวprocโทร; อีกครั้งคำสั่ง DML เดียวคือธุรกรรมของตนเองและไม่ต้องการ การทำธุรกรรมที่ชัดเจนใด ๆ ที่ทั้งสองชั้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.