2009-05-20 8 views
0

Voici les technologies pertinentes que je travaille:Pouvez-vous inclure les modifications linq-to-sql et les mises à jour de l'adaptateur de table de jeu de données ADO.NET dans une seule transaction?

  • point de Devart Connect pour Oracle (pour faciliter Linq-à-Sql pour Oracle).
  • Datasets ADO.NET fortement typés.
  • 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.
  • J'aimerais commencer à convertir ce code en Linq-to-Sql, mais je voudrais le faire au coup par coup pour minimiser le risque de perte de code.

Voilà ma preuve de schéma concept:

Parent Tableau

  • Parent.Id
  • Parent.Name

Table enfant

  • 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 thread s'arrêtera indéfiniment sur l'appel de mise à jour de l'adaptateur de données enfant car il attendra indéfiniment un verrou en écriture sur la table enfant. [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 ceci? Considérant que:
    • Je veux engager une mise à jour sur la table de parent avec SubmitChanges de Linq à Sql méthode ...
    • Et je veux engager une mise à jour sur la table des enfants avec un Jeu de données ADO.NET adaptateur de table.

Voici deux notes intéressantes:

  1. Toute cette choses fonctionne en inverse. C'est-à-dire, si je voulais soumettre des modifications à la table parente avec un adaptateur de données et change à la table enfant avec linq-à-sql ... que fonctionnerait.
  2. J'ai essayé d'attacher explicitement la transaction à l'adaptateur de données, mais le compilateur ne l'autorisera pas car il s'agit d'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(); 
    

Répondre

1

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

0

Utilisez une classe TransactionScope.

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

1

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

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

Le problème était que la colonne d'identité de l'objet enfant n'était pas définie correctement. Si DotConnect LINQ n'assume pas de clé d'identité, les propriétés des objets semblent être définies de façon ad hoc, ce qui entraîne des mises à jour non séquentielles, entraînant des violations d'intégrité.

est ici 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, configurez une clé auto-incrémentée pour l'objet enfant.
  • 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 incorporer la nouvelle auto-généré clé primaire:

    • Dans EntityDeveloper pour dotConnect, ouvrez votre modèle de stockage LINQ (fichier .LQML).
    • Définissez la clé d'entité de l'objet enfant sur 'Valeur générée automatiquement' et Auto-Synch sur 'OnInsert'.
    • Enregistrez le modèle de stockage et, dans Visual Studio, nettoyez et reconstruisez la solution.
    • Supprime tout code qui définit explicitement la clé primaire de l'enfant.
      • LINQ reconnaîtra implicitement ceci comme auto-incrémenté et récupérera l'ID créé par le déclencheur.
  • Dans le code, après avoir créé l'objet enfant, l'attacher au parent, comme ci-dessous:

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

Voici d'autres ressources:

Cheers!

Questions connexes