7
votes

Comment gérer DataContext imbriqué dans le BL?

public class TestBL
{
    public static void AddFolder(string folderName)
    {
        using (var ts = new TransactionScope())
        {
            using (var dc = new TestDataContext())
            {
                var folder = new Folder { FolderName = folderName };

                dc.Folders.InsertOnSubmit(folder);
                dc.SubmitChanges();

                AddFile("test1.xyz", folder.Id);
                AddFile("test2.xyz", folder.Id);
                AddFile("test3.xyz", folder.Id);

                dc.SubmitChanges();
            }

            ts.Complete();
        }
    }

    public static void AddFile(string filename, int folderId)
    {
        using (var dc = new TestDataContext())
        {
            dc.Files.InsertOnSubmit(
                new File { Filename = filename, FolderId = folderId });

            dc.SubmitChanges();
        }
    }
}
This is an example of nested DataContext (untested). The problem starts when a TransactionScope is added to our little experiment (as shown above). The first AddFile at the AddFolder function will escalate the transaction to DTC (which is bad by all means), because AddFile initializes new DataContext, thus opening a second connection to the DB.
How can I use nested DataContext that will not occur a DTC usage?
Is this all just plain wrong? Should I use the DataContext differently?

1 commentaires

Malheureusement, j'ai estimé que cette question n'a pas eu l'attention qu'elle méritait et n'a pas été entièrement répondue :(


4 Réponses :


3
votes

Nul doute que l'escalade à la DTC doit être évité dans la mesure du possible. Lorsque je lisais votre question pour la première fois, mon gut a déclaré que votre transaction ne s'imposerait pas à la DTC, car vous utilisez la même chaîne de connexion dans les deux contextes de données. Cependant, selon Cet article , j'avais tort.

Vous n'êtes pas seul dans le confusion sur les meilleures pratiques avec contextes de données. Si vous recherchez le Web pour cela, il y a des réponses surtout sur la carte. Dans votre exemple, vous pouvez transmettre le contexte de données dans la méthode AddFile. Ou, vous pouvez refroidir cet accès aux données dans une classe qui maintient la durée de vie du contexte de données jusqu'à ce que le dossier et les fichiers soient tous enregistrés. Rick Strahl a affiché un article sur plusieurs techniques.

Néanmoins, aucune des réponses que j'ai vues autour de Linq à SQL semble très satisfaisante. Avez-vous envisagé d'éviter la gestion de votre couche de données en utilisant un orj? J'ai utilisé Nettiers avec un grand succès, mais j'entends de bonnes choses sur plinqo . Celles-ci ont tous deux besoin codesmith , mais il y a De nombreuses alternatives .


2 commentaires

C'est une façon de regarder ça. Malheureusement, je ne changerai pas mon orje bientôt, alors je vais devoir trouver une solution appropriée au problème.


Il apparaît que la modification de l'orèse serait la solution sage que Microsoft fournit une solution très basique qui ne convient pas aux besoins d'entreprise, donc je suis passé à NHibernate :)



0
votes

J'ai proposé un moyen de manipuler de telles situations.

Exemple de base de la classe d'une entité BL (l'entité héritera de cette classe) xxx

contrôleur implémenté < / p> xxx

Utilisation simple d'un contrôleur implémenté xxx

utilisation plus complexe d'un Contrôleur implémenté (2 contrôleurs sur le même dataContext) xxx

J'aimerais voir plus d'idées sur la résolution de cette énigme et des commentaires sur le code ci-dessus.


0 commentaires

0
votes

Vous n'avez pas besoin de faire 2 aller-retour ou plus pour cette transaction. Je crois que Linq-DataContext est intelligent de reconnaître que ces fichiers appartiennent à l'objet de dossier et inséreront la ligne de dossier d'abord et les fichiers après cela (tout dans le contexte d'une transaction, par exemple, TRAN / COMMIS). Cependant, vous devez faire:

public class TestBL
{
    public static void AddFolder(string folderName)
    {
        using (var ts = new TransactionScope())
        {
            using (var dc = new TestDataContext())
            {
                var folder = new Folder { FolderName = folderName };

                AddFile(dc, "test1.xyz", folder);
                AddFile(dc, "test2.xyz", folder);
                AddFile(dc, "test3.xyz", folder);

                dc.SubmitChanges();
            }

            ts.Complete();
        }
    }

    private static void AddFile(DataContext dc, string filename, Folder folder)
    {
            dc.Files.InsertOnSubmit(
                new File { Filename = filename, Folder = folder });
    }

    public static void AddFile(string filename, int folderId)
    {
        using (var dc = new TestDataContext())
        {
            var folder = new Folder { FolderId = folderId };
            dc.Attach(folder, false);
            AddFile(dc, filename, folder);

            dc.SubmitChanges();
        }
    }
}


1 commentaires

Le lien que vous avez donné ici a parfaitement expliqué les relations de transaction / DTC de manière parfaite, bien que cela soit un peu différent de ce que j'ai demandé.



1
votes

En plus de passer le DataContext à addFiles sous forme de paramètre, vous pouvez également transmettre la valeur de connexion de DataContext à un autre DataContext. Cela garantirait que l'autre DataContext a la même connexion.

Chaque DataContext a également une propriété de transaction, que vous pourriez probablement définir et transmettre au lieu d'utiliser l'objet Trans transaction.


2 commentaires

Je ne pense pas qu'il soit possible de passer la propriété de connexion, mais peut-être que vous vouliez dire autre chose - pourriez-vous ajouter un exemple de code?


@Eran: L'objet DataContext dispose d'un constructeur surchargé qui accepte tout objet IDBConnection. Donc, vous pourriez faire de nouveaux datacontext (olddatacontext.connection)