8
votes

Transaction dans une transaction en C #

J'importe un fichier plat de factures dans une base de données à l'aide de C #. J'utilise les transactionsCope pour rétrécir la totalité de l'opération si un problème est rencontré.

Il s'agit d'un fichier d'entrée délicat, dans une ligne d'une seule ligne n'est pas nécessaire un enregistrement égal. Il comprend également des enregistrements liés. Une facture aurait une ligne d'en-tête, des articles de ligne, puis une ligne totale. Certaines des factures devront être ignorées, mais je ne sais peut-être pas que cela doit être ignoré avant d'atteindre la ligne totale.

Une stratégie consiste à stocker l'en-tête, les éléments de la ligne et la ligne totale en mémoire et tout enregistrer une fois que la ligne totale est atteinte. Je poursuis cela maintenant.

Cependant, je me demandais si cela pouvait être fait d'une manière différente. Création d'une transaction «imbriquée» autour de la facture, insérant la ligne d'en-tête et des éléments de ligne, puis mettez à jour la facture lorsque la ligne totale est atteinte. Cette transaction "imbriquée" reviendrait si elle est déterminée que la facture doit être ignorée, mais la transaction globale continuerait.

est-ce possible, pratique et comment allez-vous configurer cela?


0 commentaires

5 Réponses :


2
votes

Au lieu d'utiliser des transactions imbriquées, vous pouvez créer une transaction par facture. De cette façon, seules les mises à jour réussies de factures entières se produiront.

Si vous nuez les transactions comme vous le décrivez, vous risquez d'avoir le jeu de données entier à rouler, ce qui n'est pas ce que vous voulez.


1 commentaires

Je veux que tout retourna s'il y a un problème.



2
votes

Personnellement, je verrais d'abord si la facture doit être ajoutée - si elle le fait, alors faites vos insertions (dans une transaction). Sinon, passez simplement à la prochaine facture.

Je ne pense pas que ce soit bien d'insérer et ensuite faire un retour dans la façon dont vous décrivez.


2 commentaires

+1 pour la solution de Kiss. Je traiterais d'abord l'intégralité du fichier dans une zone de transfert (en mémoire, ou s'il est énorme une table d'intermédiaire de la base de données), puis enregistrez les données valides sur la table principale.


Jusqu'à présent, tenant la facture actuelle et l'élément de ligne associé en mémoire fonctionne. J'utilise Linq2SQL pour les stocker pour que je n'ai pas besoin d'écrire beaucoup de SQL pour traiter le graphique d'objet. Une fois qu'il détermine, la facture doit être enregistrée, elle soumet et dispose du contexte de données.



0
votes

Une transaction interne défaillante réduirait la transaction extérieure, de sorte que vous ne pouvez pas aller à cet itinéraire.

Vous pouvez probablement le simuler, cependant, en utilisant une table TEM (ou une charge). Insérez chaque facture de manière transactionnelle dans la table de chargement, puis passez de la table de chargement à une table permanente atomique.


0 commentaires

42
votes

ni le transactionsCope code> NOR SQL Server supporte les transactions imbriquées.

Vous pouvez nier transactionsCOPE code> des instances, mais cela n'a que l'apparence sortante d'une transaction imbriquée. En réalité, il y a quelque chose d'appelé une transaction "ambiante", et il ne peut y en avoir qu'un à la fois. Quelle transaction est la transaction ambiante dépend de ce que vous utilisez pour transactionsCOPOPtion code> lorsque vous créez la portée. P>

Pour expliquer plus en détail, considérez les éléments suivants: P>

using (var outer = new TransactionScope())
{
    DoOuterWork();

    using (var inner1 = new TransactionScope(TransactionScopeOption.Suppress))
    {
        DoWork1();
        inner1.Complete();
    }

    using (var inner2 = new TransactionScope(TransactionScopeOption.RequiresNew))
    {
        DoWork2();
        inner2.Complete();
    }

    using (var inner3 = new TransactionScope(TransactionScopeOption.Required))
    {
        DoWork3();
        inner3.Complete();
    }

    outer.Complete();
}
  • inner1 code> est exécuté dans une transaction implicite, indépendamment de externe code>. Rien qui arrive dans dowork1 code> est garanti d'être atomique. Si cela échoue à mi-chemin, vous aurez des données incohérentes. Tout travail qui se produit ici est toujours engagé, peu importe ce qui arrive à externe code>. P> li>

  • inner2 code> est exécuté dans une nouvelle transaction, indépendamment de externe code>. Ceci est une transaction em> différente externe code> mais c'est pas forte> imbriquée. Si elle échoue, le travail qui s'est passé dans externe code> ( dooreterwork () code>) et l'un des autres étendues peuvent toujours être commis, mais voici le rub: s'il est complète, puis roulant l'ensemble de l'ensemble de la transaction code> externe em> pas em> pas em> retourner le travail effectué à l'intérieur inner2 code>. strong> C'est pourquoi il n'est pas véritablement imbriqué. En outre, inner2 code> n'aura pas accès à des lignes verrouillées par externe code>, afin que vous puissiez vous retrouver avec des blocages ici si vous n'êtes pas prudent. P> li>

  • inner3 code> est exécuté dans la transaction même forte> comme extérieur code>. Ceci est le comportement par défaut. Si dowork3 () code> échoue et inner3 code> ne finit jamais, l'ensemble de la transaction code> externe code> est renvoyé. De même, si inner3 code> est terminé avec succès, mais externe code> est renvoyé, puis tout travail effectué dans dowork3 () code> est également roulé. P> li> ul>

    Vous pouvez donc espérons que rien de ces options ne sont réellement imbriquées et ne vous donnera pas ce que vous voulez. L'option requise code> se rapproche d'une transaction imbriquée, mais ne vous donne pas la possibilité de commiser ou de rouler indépendamment des unités de travail spécifiques à l'intérieur de la transaction. P>

    La chose la plus proche que vous puissiez obtenir Pour les transactions tristes imbriquées dans SQL Server est l'instruction SAVE TRAN CODE> COMMUNAIRE avec certains Essayez / attrayez les blocs CODE>. Si vous pouvez mettre votre logique à l'intérieur d'une ou plusieurs procédures stockées, ce serait une bonne option. P>

    Sinon, vous devrez utiliser des transactions distinctes pour chaque facture selon la suggestion de ODED. P>


3 commentaires

Merci pour les détails. Cela n'était pas clair dans la documentation MSDN.


+1 pour une explication très excellente et très approfondie. Bien fait, monsieur.


+1 Tout comme @Remi - très belle explication, et cela a aidé à me sauver ce soir. J'aimerais pouvoir + plus encore! Merci!



3
votes

Ceci est accompli avec un Point de sauvegarde de transaction . Il ressemble généralement à quelque chose comme ceci: xxx

J'ai utilisé un pseudo pseudo basé sur des transact-sql et ce n'est pas un accident. Les points de sauvegarde sont un concept de base de données et les transactions .NET ne les supportent pas. Vous pouvez utiliser SQLTransaction directement et exploiter SQLTransAction.save ou vous pouvez utiliser des procédures stockées T-SQL modélisées après un Modèle de sécurité d'exception . Je vous recommanderais d'éviter les transactions .NET (c.-à-d. Transactionscope) dans ce cas.


0 commentaires