Können Sie Linq to SQL-Änderungen und ADO.NET-Dataset-Tabelle Adapter-Updates in einer einzigen Transaktion enthalten?

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

Frage

Hier sind die relevanten Technologien, die ich arbeite mit:

  • Devart den Punkt Connect for Oracle.
  • (Linq-to-SQL für Oracle zu erleichtern)
  • stark typisierten ADO.NET Datasets.
  • Eine Oracle-Datenbank.

Hier ist die Herausforderung:

  • My Legacy-Code einreicht Datenbank-Updates mit ADO.NET-Datasets und Tischadapter.
  • Ich möchte diesen Code über Linq-to-Sql Umwandlung beginnen, aber ich möchte es tun stückchenweise Code Churn und das Risiko zu minimieren.

Hier ist mein Proof of Concept-Schema:

Übergeordnete Tabelle

  • Parent.Id
  • Parent.Name

Child Tabelle

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

Hier ist mein Proof of Concept Codeblock:

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();
        }
    }
}

  • Wie die obigen Ausführungen zeigen, wird der Faden halt auf unbestimmte Zeit auf das Kind Datenaktualisierung Aufruf des Adapters, da sie auf unbestimmte Zeit für eine Schreibsperre auf der Child-Tabelle warten. [Beachten Sie die Fremdschlüsselbeziehung: Parent.Id -> Child.ParentId (M: N)]

Hier ist meine Frage:

  • Ich möchte den gesamten Codeblock wickeln in einer Transaktion.
  • Kann ich das tun? Bedenkt, dass:
    • Ich möchte ein Update auf der übergeordneten Tabelle begehen mit Linq-to-Sql SubmitChanges Methode ...
    • Und ich möchte ein begehen Aktualisierung auf der Child-Tabelle mit einem ADO.NET-Datensatz Tabelle Adapter .

Hier sind zwei interessante Fußnoten:

  1. Diese ganzen Dinge funktioniert in umkehren. Das heißt, wenn ich es wollte einreichen Änderungen an der übergeordneten Tabelle mit einem Datenadapter und Änderungen das Kind Tisch mit Linq-to-SQL ... dass funktionieren würde, .
  2. Ich habe versucht, explizit die Transaktion an den Dataadapter zu befestigen, aber der Compiler wird es nicht zulassen, da es sich um eine andere Art von Transaktion ist.

                    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();
    
War es hilfreich?

Lösung

Ich weiß nichts über die Oracle-Transaktionen ... aber auf der dotnet Seite sollten Sie sich gut, sich um die Transaktion zu steuern. Stellen Sie sicher, dass beide Technologien werden mit der gleichen Verbindungsinstanz.

Wenn wir Transaktionen durch die Verbindung anstatt durch die ORM zu steuern, verwenden wir Transaktionsbereich: http://msdn.microsoft.com/en-us/library/ms172152.aspx

Andere Tipps

Ich hatte das gleiche Problem, diese beiden Fehler zu begegnen:

  • Integrität Einschränkungsverletzung (ORA-02291)
  • „Kann nicht einfügen Einheit mit dem gleichen Schlüssel, wenn Schlüssel nicht Datenbank erzeugt“

Das Problem war, dass die Identität Spalte des Kindes Objekt nicht richtig eingestellt wurde. Wenn dotConnect LINQ keine Identitätsschlüssel übernehmen, dann Objekte Eigenschaften scheinen ad hoc festgelegt werden, was zu einer nicht-sequenziellen Aktualisierungen, was zu einer Integritätsverletzungen.

Hier ist das Update:

  • LINQ muss wissen, dass der Primärschlüssel des Kindes ein Unternehmen Schlüssel und eine automatisch generierte.
  • In Oracle-Setup automatisch inkrementierte Schlüssel für das Kind-Objekt.
  • Zuerst eine Sequenz erstellen:

      DROP SEQUENCE MyChild_SEQ;
      CREATE SEQUENCE MyChild_SEQ
          MINVALUE 1
          MAXVALUE 999999999999999999999999999
          START WITH 1
          INCREMENT BY 1
          CACHE 20;
    
  • Als Nächstes erstellen Sie die OnInsert Trigger:

    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
    
  • das Speichermodell ändern Sie die neuen automatisch generierten Primärschlüssel zu übernehmen:

    • Im EntityDeveloper für dotConnect, öffnen Sie Ihre LINQ Speichermodell (.LQML-Datei).
    • Stellen Sie die Entitätsschlüssel zu 'Auto generiert Value' Kind-Objekt, und Auto-Synch zu 'OnInsert'.
    • Speichern Sie das Speichermodell und in Visual Studio, sauber und die Lösung neu zu erstellen.
    • beliebigen Code entfernen, die explizit der Primärschlüssel des Kindes setzt.
      • LINQ wird dies implizit erkennen, wie auto-inkrementiert, und rufen Sie die Trigger-ID erstellt.
  • Im Code nach dem Kind Objekt zu erzeugen, hängen Sie es an die Eltern, wie folgt:

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

Hier sind weitere Informationen:

Cheers!

eine Transaction Klasse verwenden.

Beachten Sie, dass, wenn Sie verschiedene Datenbanken verwenden (oder sie befinden sich auf verschiedene Server) Sie benötigen eine DTC-Konfiguration zu überprüfen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top