Pertama , Anda harus selalu memiliki penanganan transaksi yang tepat dalam semua prosedur Anda sehingga tidak masalah jika mereka dipanggil oleh kode aplikasi, dengan prosedur lain, secara individual dalam permintaan ad-hoc, oleh pekerjaan SQL Agent, atau cara lain . Tetapi pernyataan DML tunggal, atau kode yang tidak melakukan modifikasi, tidak memerlukan Transaksi eksplisit. Jadi, yang saya rekomendasikan adalah:
- Selalu memiliki struktur TRY / CATCH sehingga kesalahan dapat digelembungkan dengan benar
- Secara opsional sertakan 3 bagian penanganan Transaksi dalam kode di bawah ini jika Anda memiliki beberapa pernyataan DML (karena satu pernyataan adalah transaksi itu sendiri). NAMUN, di luar menambahkan beberapa kode tambahan di mana itu tidak diperlukan secara khusus, jika seseorang lebih memilih untuk memiliki template yang konsisten, maka tidak ada salahnya untuk tetap dalam 3 blok IF terkait transaksi. Tetapi dalam hal ini saya masih merekomendasikan untuk tidak menyimpan 3 blok IF terkait transaksi untuk proksi SELECT saja (yaitu hanya baca).
Saat melakukan 2 atau lebih pernyataan DML, Anda perlu menggunakan sesuatu di sepanjang baris berikut ini (yang juga dapat dilakukan untuk operasi DML tunggal jika orang lebih suka konsisten):
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;
Saat melakukan hanya 1 pernyataan DML atau hanya SELECT, Anda bisa lolos hanya dengan yang berikut:
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;
Kedua , Anda harus menangani transaksi di lapisan aplikasi hanya jika Anda perlu menjalankan lebih dari 1 prosedur permintaan / tersimpan dan semuanya harus dikelompokkan ke dalam operasi atom. Melakukan tunggal SqlCommand.Execute___
hanya perlu di coba / tangkap, tetapi tidak dalam Transaksi.
Tapi, apakah salahnya melakukan Transaksi di lapisan aplikasi ketika hanya melakukan satu panggilan? Jika memerlukan MSDTC (Koordinator Transaksi Terdistribusi Microsoft) maka itu sedikit lebih berat pada sistem untuk melakukan ini pada lapisan aplikasi ketika tidak secara tegas diperlukan. Secara pribadi, saya lebih suka menghindari transaksi berbasis-lapisan aplikasi kecuali benar-benar diperlukan karena mengurangi potensi transaksi yatim (jika ada yang salah dengan kode aplikasi sebelum melakukan komit atau kembalikan). Saya juga menemukan bahwa kadang-kadang membuat debugging situasi tertentu sedikit lebih sulit. Tapi itu dikatakan, saya tidak melihat ada yang salah secara teknis dengan juga menangani transaksi pada lapisan aplikasi ketika membuat satu procpanggilan; sekali lagi, pernyataan DML tunggal adalah transaksi sendiri dan tidak memerlukan penanganan transaksi eksplisit di kedua lapisan.