Tout d'abord , vous devez toujours avoir une gestion des transactions appropriée dans toutes vos procédures afin que peu importe qu'elles soient appelées par le code d'application, par une autre procédure, individuellement dans une requête ad hoc, par un travail de l'Agent SQL ou par tout autre moyen . Mais les instructions DML simples ou le code qui n'apporte aucune modification n'ont pas besoin d' une transaction explicite. Donc, ce que je recommande, c'est:
- Ayez toujours la structure TRY / CATCH pour que les erreurs puissent être propulsées correctement
- Incluez éventuellement les 3 éléments de gestion des transactions dans le code ci-dessous si vous avez plusieurs instructions DML (car une seule instruction est une transaction en soi). CEPENDANT, en dehors de l'ajout de code supplémentaire là où il n'est pas spécifiquement nécessaire, si l'on préfère avoir un modèle cohérent, cela ne fait pas de mal de conserver les 3 blocs IF liés à la transaction. Mais dans ce cas, je recommanderais toujours de ne pas conserver les 3 blocs IF liés aux transactions pour les processus SELECT uniquement (c'est-à-dire en lecture seule).
Lorsque vous effectuez 2 instructions DML ou plus, vous devez utiliser quelque chose dans le sens de ce qui suit (ce qui peut également être fait pour des opérations DML simples si l'on préfère être cohérent):
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;
Lorsque vous effectuez une seule instruction DML ou simplement un SELECT, vous pouvez vous en sortir avec les éléments suivants:
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;
Deuxièmement , vous devez gérer la transaction au niveau de la couche d'application uniquement si vous devez exécuter plus d'une requête / procédure stockée et qu'elles doivent toutes être regroupées en une opération atomique. Faire un single SqlCommand.Execute___
doit seulement être dans un try / catch, mais pas dans une transaction.
Mais, est-ce que cela fait mal de faire une transaction au niveau de la couche d'application lors d'un seul appel? S'il nécessite MSDTC (Microsoft Distributed Transaction Coordinator), il est un peu plus lourd sur le système de le faire au niveau de l'application lorsque cela n'est pas expressément nécessaire. Personnellement, je préfère éviter les transactions basées sur la couche d'application, sauf si cela est absolument nécessaire, car cela réduit le potentiel de transactions orphelines (si quelque chose s'est mal passé avec le code de l'application avant de faire la validation ou la restauration). J'ai également constaté que cela rend parfois le débogage de certaines situations un peu plus difficile. Mais cela étant dit, je ne vois rien de mal techniquement à gérer également la transaction au niveau de la couche d'application lors de la création d'un seul processus.appel; encore une fois, une seule instruction DML est sa propre transaction et ne nécessite aucune gestion de transaction explicite sur l'une ou l'autre couche.