Pouvez-vous inclure des changements LINQ to SQL et des mises à jour de l'adaptateur de table de jeu de données ADO.NET dans une seule transaction?

StackOverflow https://stackoverflow.com/questions/888299

Question

Voici les technologies pertinentes que je travaille:

  • point de Devart Connect pour Oracle (pour faciliter Linq-à-Sql pour Oracle).
  • fortement typées jeux de données ADO.NET.
  • Une base de données Oracle.

Voici le défi:

  • Mon code existant soumet des mises à jour de base de données avec des ensembles de données ADO.NET et adaptateurs de table.
  • Je voudrais commencer à convertir ce code vers Linq à Sql, mais je voudrais le faire au coup par coup pour réduire au minimum le code désabonnement et le risque.

Voici ma preuve de schéma concept:

Tableau parent

  • Parent.Id
  • Parent.Name

Tableau Enfants

  • Child.Id
  • Child.ParentId
  • Child.Name

Voici ma preuve de bloc de code concept:

using System;
using System.Data.Common;
using DevArtTry1.DataSet1TableAdapters;

namespace DevArtTry1
{
    class Program
    {
        static void Main(string[] args)
        {
            using (DataContext1 dc = new DataContext1())
            {
                dc.Connection.Open();
                using (DbTransaction transaction = dc.Connection.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
                {
                    dc.Transaction = transaction;

                    Parent parent = new Parent();
                    parent.Id = 1;
                    parent.Name = "Parent 1";
                    dc.Parents.InsertOnSubmit(parent);
                    dc.SubmitChanges(); // By virtue of the Parent.Id -> Child.ParentId (M:N) foreign key, this statement will impose a write lock on the child table.

                    DataSet1.CHILDDataTable dt = new DataSet1.CHILDDataTable();
                    DataSet1.CHILDRow row = dt.NewCHILDRow();
                    row.ID = 1;
                    row.PARENTID = 1;
                    row.NAME = "Child 1";
                    dt.AddCHILDRow(row);

                    CHILDTableAdapter cta = new CHILDTableAdapter();
                     // cta.Transaction = transaction;  Not allowed because you can't convert source type 'System.Data.Common.DbTransaction to target type 'System.Data.OracleClient.OracleTransaction.
                    cta.Update(dt); // The thread will encounter a deadlock here, waiting for a write lock on the Child table.
                    transaction.Commit();
                }
            }

            Console.WriteLine("Successfully inserted parent and child rows.");
            Console.ReadLine();
        }
    }
}

  • Comme les commentaires ci-dessus indiquent, le fil s'arrêtera indéfiniment sur l'appel de mise à jour de l'adaptateur de données de l'enfant, car il attendra indéfiniment un verrou d'écriture sur la table des enfants. [Note de la relation de clé étrangère: Parent.Id -> Child.ParentId (M: N)]

Voici ma question:

  • Je veux envelopper tout le bloc de code dans une transaction.
  • Puis-je faire? Étant donné que:
    • Je veux engager une mise à jour sur la table de parent avec LINQ-à-Sql de méthode SubmitChanges ...
    • Et je veux commettre une mettre à jour sur la table des enfants avec un jeu de données ADO.NET adaptateur de table .

Voici deux notes intéressantes:

  1. Ce choses ensemble fonctionne sens inverse. Autrement dit, si je voulais soumettre les modifications à la table des parents avec un adaptateur de données et les modifications de la table des enfants avec LINQ to SQL ... que travailleraient .
  2. J'ai essayé de joindre explicitement la transaction DataAdapter, mais le compilateur ne le permettrai pas parce qu'il est un type de transaction différent.

                    CHILDTableAdapter cta = new CHILDTableAdapter();
                cta.Transaction = transaction; // Not allowed because you can't convert source type 'System.Data.Common.DbTransaction' to target type 'System.Data.OracleClient.OracleTransaction'.
                cta.Update(dt);
                transaction.Commit();
    
Était-ce utile?

La solution

Je ne sais pas quoi que ce soit sur les transactions d'Oracle ... mais sur le côté dotnet vous devriez être bien pour contrôler la même transaction. Assurez-vous que les deux technologies utilisent la même instance de connexion.

Lorsque nous contrôlons les transactions via la connexion plutôt que par l'ORM, nous utilisons la portée de la transaction: http://msdn.microsoft.com/en-us/library/ms172152.aspx

Autres conseils

J'ai eu le même problème, à la rencontre de ces deux erreurs:

  • violation de contrainte d'intégrité (ORA-02291)
  • « ne peut pas insérer entité avec la même clé si la clé n'est pas la base de données générée »

Le problème est que la colonne d'identité de l'objet enfant n'a pas été correctement défini. Si dotConnect LINQ ne suppose pas une clé d'identité, des objets alors des propriétés semblent être mis ad hoc, ce qui les mises à jour non-séquentielle, ce qui conduit à des violations de l'intégrité.

Voici le correctif:

  • LINQ a besoin de savoir qui est une clé d'entité et généré automatiquement la clé primaire de l'enfant.
  • Dans Oracle, la configuration d'une clé auto-incrémentée pour l'objet enfant.
  • Tout d'abord créer une séquence:

      DROP SEQUENCE MyChild_SEQ;
      CREATE SEQUENCE MyChild_SEQ
          MINVALUE 1
          MAXVALUE 999999999999999999999999999
          START WITH 1
          INCREMENT BY 1
          CACHE 20;
    
  • Ensuite, créez le déclencheur OnInsert:

    CREATE OR REPLACE TRIGGER MyChild_AUTOINC 
    BEFORE INSERT
    ON MyChildObject
    FOR EACH ROW
    BEGIN
      SELECT MyChild_SEQ.nextval
      INTO :NEW.MyChild_ID
      FROM dual;
    END MyChild_AUTOINC ; 
    ALTER TRIGGER MyChild_AUTOINC ENABLE
    
  • Modifier le modèle de stockage pour intégrer la nouvelle clé primaire générée automatiquement:

    • Dans la EntityDeveloper pour dotConnect, ouvrez votre modèle de stockage LINQ (fichier .LQML).
    • Définissez la clé d'entité de l'objet enfant 'autogénérés Value' et Auto-synch 'OnInsert'.
    • Enregistrer le modèle de stockage, et dans Visual Studio, propre et reconstruire la solution.
    • Retirez tout code qui définit explicitement la clé primaire de l'enfant.
      • LINQ reconnaîtra implicitement comme auto-incrémentée et récupérer l'ID créé déclencheur.
  • Dans le code, après avoir créé l'objet enfant, l'attacher à la société mère, comme ci-dessous:

    ChildType newChild = new ChildType();
    DataContext.InsertOnSubmit(newChild);
    Parent.Child = newChild;
    

Voici d'autres ressources:

Vive!

Utilisez une classe TransactionScope.

Prenez garde que si vous utilisez différentes bases de données (ou ils résident sur des serveurs distincts), vous devez vérifier votre configuration DTC.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top