Trước tiên , bạn phải luôn luôn xử lý giao dịch đúng cách trong tất cả các quy trình của mình để không gặp vấn đề gì nếu chúng được gọi bằng mã ứng dụng, bởi một quy trình khác, riêng lẻ trong một truy vấn đặc biệt, bởi một công việc của Tác nhân SQL hoặc một số phương tiện khác . Nhưng các câu lệnh DML đơn lẻ hoặc mã không thực hiện bất kỳ sửa đổi nào sẽ không cần Giao dịch rõ ràng. Vì vậy, những gì tôi muốn giới thiệu là:
- Luôn có cấu trúc TRY / CATCH để các lỗi có thể được xử lý đúng
- Tùy chọn bao gồm 3 phần xử lý giao dịch trong mã bên dưới nếu bạn có nhiều câu lệnh DML (vì bản thân một câu lệnh là một giao dịch). TUY NHIÊN, ngoài việc thêm một số mã bổ sung khi không cần thiết, nếu một người thích có một mẫu nhất quán, thì việc giữ trong 3 khối IF liên quan đến giao dịch sẽ không gây hại gì. Nhưng trong trường hợp đó, tôi vẫn khuyên bạn không nên giữ 3 khối IF liên quan đến Giao dịch cho các procs chỉ (chỉ đọc).
Khi thực hiện 2 hoặc nhiều câu lệnh DML, bạn cần sử dụng một số thứ theo các dòng sau (cũng có thể được thực hiện cho các hoạt động DML đơn lẻ nếu thích nhất quán):
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;
Khi thực hiện chỉ 1 câu lệnh DML hoặc chỉ một CHỌN, bạn có thể thoát khỏi chỉ bằng cách sau:
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;
Thứ hai , bạn nên xử lý các giao dịch tại các lớp ứng dụng duy nhất nếu bạn cần phải thực hiện hơn 1 truy vấn / thủ tục lưu trữ và tất cả họ đều cần phải được nhóm lại thành một hoạt động nguyên tử. Thực hiện một lần duy nhất SqlCommand.Execute___
chỉ cần trong một lần thử / bắt chứ không phải trong Giao dịch.
Nhưng, có đau không khi thực hiện Giao dịch ở lớp ứng dụng khi chỉ thực hiện một cuộc gọi? Nếu nó yêu cầu MSDTC (Điều phối viên giao dịch phân tán của Microsoft) thì hệ thống sẽ nặng hơn một chút khi thực hiện việc này ở lớp ứng dụng khi không cần thiết. Cá nhân, tôi thích tránh các giao dịch dựa trên lớp ứng dụng trừ khi thực sự cần thiết vì nó cắt giảm tiềm năng của các giao dịch mồ côi (nếu có lỗi xảy ra với mã ứng dụng trước khi thực hiện cam kết hoặc khôi phục). Tôi cũng đã thấy rằng đôi khi việc gỡ lỗi một số tình huống khó khăn hơn một chút. Nhưng điều đó đang được nói, tôi không thấy bất cứ điều gì sai về mặt kỹ thuật cũng xử lý các giao dịch tại các lớp ứng dụng khi thực hiện một single procgọi; một lần nữa, một câu lệnh DML là giao dịch của chính nó và không cần bất kỳ xử lý giao dịch rõ ràng ở một trong hai lớp.