web-dev-qa-db-fra.com

Sommes-nous tenus de gérer la transaction en code C # ainsi qu'en procédure stockée

Avons-nous vraiment besoin de la gestion des transactions en c # ainsi que du processus de stockage de base de données des deux côtés

C #:

Using(transaction with transaction scope)
{
     Execute stored proc;
     Transaction. Complete;
}

Procédure stockée SQL:

Create process
As
Begin try
    Begin transaction
    Commit
End try
Begin catch
    Rollback
End catch
14
Rakesh Gaur

First, vous devriez toujours avoir une gestion correcte des transactions dans toutes vos procédures afin que cela n'ait pas d'importance si elles sont appelées par le code de l'application, par une autre procédure, individuellement dans une requête ad-hoc, par un SQL Travail d'agent, ou 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 correctement propagées
  • 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 de ne pas conserver les 3 blocs IF liés à la transaction pour les procs SELECT-only (c'est-à-dire en lecture seule).

Lorsque vous effectuez 2 instructions DML ou plus, vous avez besoin d'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 pour ê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;

Second, 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 ils doivent tous être regroupés en une opération atomique. Faire un seul SqlCommand.Execute___ doit seulement être dans un try/catch, mais pas dans une transaction.

Mais, cela fait-il mal d'effectuer une transaction au niveau de la couche d'application lorsque vous effectuez 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 la couche d'application lorsqu'il 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 techniquement mal avec aussi gérer la transaction au niveau de la couche d'application lors d'un seul appel proc ; encore une fois, une seule instruction DML est sa propre transaction et n'a pas besoin de gestion de transaction explicite sur l'une ou l'autre couche.

21
Solomon Rutzky