Po pierwsze , zawsze powinieneś mieć odpowiednią obsługę transakcji we wszystkich swoich procedurach, aby nie miało znaczenia, czy są one wywoływane przez kod aplikacji, inną procedurę, indywidualnie w zapytaniu ad-hoc, zadaniu agenta SQL lub w inny sposób . Ale pojedyncze instrukcje DML lub kod, który nie wprowadza żadnych modyfikacji, nie wymaga wyraźnej Transakcji. Tak więc polecam:
- Zawsze miej strukturę TRY / CATCH, aby możliwe było prawidłowe wykreślanie błędów
- Opcjonalnie dołącz 3 elementy obsługi transakcji do poniższego kodu, jeśli masz wiele instrukcji DML (ponieważ jedna instrukcja jest transakcją samą w sobie). JEDNAK poza dodaniem dodatkowego kodu, który nie jest specjalnie potrzebny, jeśli ktoś woli mieć spójny szablon, nie przeszkadza mu trzymanie w 3 blokach IF związanych z transakcją. Ale w takim przypadku nadal zalecałbym, aby nie przechowywać 3 bloków IF związanych z transakcją dla procesów SELECT tylko (tj. Tylko do odczytu).
Robiąc 2 lub więcej instrukcji DML, musisz użyć czegoś podobnego do następującego (co można również zrobić dla pojedynczych operacji DML, jeśli wolisz być spójny):
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;
Wykonując tylko 1 instrukcję DML lub po prostu WYBIERZ, możesz uniknąć następujących czynności:
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;
Po drugie , powinieneś obsłużyć transakcję na warstwie aplikacji tylko wtedy, gdy musisz wykonać więcej niż 1 zapytanie / procedurę składowaną i wszystkie muszą być zgrupowane w operację atomową. Wykonanie pojedynczego działania SqlCommand.Execute___
musi odbywać się tylko w trybie próbnym / połowowym, ale nie w transakcji.
Ale, czy to boli zrobić Transakcji w warstwie aplikacji, gdy tylko co pojedynczą rozmowę? Jeśli wymaga MSDTC (Microsoft Distributed Transaction Coordinator), jest to trochę trudniejsze w systemie, aby to zrobić na warstwie aplikacji, gdy nie jest to wyraźnie potrzebne. Osobiście wolę unikać transakcji opartych na warstwie aplikacji, chyba że jest to absolutnie konieczne, ponieważ ogranicza to potencjalne transakcje osierocone (jeśli coś poszło nie tak z kodem aplikacji przed zatwierdzeniem lub wycofaniem). Odkryłem również, że czasami utrudnia to debugowanie niektórych sytuacji. Ale powiedział, że jest nie widzę niczego technicznie złego również przeprowadzenie transakcji w warstwie aplikacji przy podejmowaniu pojedynczy procpołączenie; ponownie, pojedyncza instrukcja DML jest własną transakcją i nie wymaga żadnej jawnej obsługi transakcji na żadnej z warstw.