ขั้นแรกคุณควรมีการจัดการธุรกรรมที่เหมาะสมในทุกขั้นตอนของคุณเสมอเพื่อไม่ให้เกิดปัญหาถ้าพวกเขาถูกเรียกด้วยรหัสแอพโดยขั้นตอนอื่นทีละรายการในคิวรีแบบเฉพาะกิจโดยงานตัวแทนของ 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 เดียวคือธุรกรรมของตนเองและไม่ต้องการ การทำธุรกรรมที่ชัดเจนใด ๆ ที่ทั้งสองชั้น