0

Ich habe versucht, die SO zu suchen, aber alle Ergebnisse, die ich gefunden habe, scheinen sich damit zu beschäftigen, PK von Entitäten zu aktualisieren, die bereits in der Datenbank gespeichert wurden. Mein Fall ist anders.Mehrere verknüpfte 1-1 Beziehungen synchronisieren keine PKs wie erwartet

Ich habe 3 Tabellen in einer Datenbank mit Beziehungen 1-0.1. Die Beziehungen wie folgt aussehen:

A <- B <- C 

wo ‚< -‘ eine Beziehung und zeigt auf den Haupt Ende. I.e. Jedes B hat immer ein verwandtes A, aber ein A kann kein B haben. Mit anderen Worten, A's Kardinalität ist 1 und B's ist 0..1.

Jede Beziehung wird durch eine FK repräsentiert, die vom PK der untergeordneten Entität zur PK der übergeordneten Entität geht. Jede PK ist eine uniqueidentifierId Spalte mit vom Client generiertem Wert. Ich habe ein EF 4 Modell aus der Datenbank generiert, das die gleichen Beziehungen mit der gleichen Kardinalität hat.

Ich versuche, Kind-Entitäten B und C zu einer vorhandenen A Einheit hinzuzufügen. Aus Gründen der Gestaltung wird das Paar neuer Instanzen in einem Code-Frieden erstellt, und die Entität A ist mit der Entität B in einer anderen Einheit verknüpft. Außerdem möchte ich nicht, dass letzterer weiß, dass C existiert.

Hier ist, wie die B und C Schaffung Code wie folgt aussieht:

public B CreateB() 
{ 
    return new B 
    { 
     Id = Guid.NewGuid(), 
     C = new C(), 
    }; 
} 

Und jetzt den Link und speichern Sie den Code ein:

// a is an instance of A that has been loaded from DB 
// and hence has a persistent Id value. 
// b is a just-created instance of B 
// that has a non-persistent Id value and null reference to A. 
void SaveBlahBlahBlah(A a, B b) 
{ 
    // At this point b and c have the same Id value. 
    // It differs from a's Id, but that's expected, they haven't been linked yet. 
    b.A = a; 
    // At this point b receives a's Id value, but c keeps the original one, 
    // therefore the existing b-c link gets broken! 

    using(var ctx = new MyContext()) 
    { 
    ctx.As.Attach(a); // This throws exception saying 
    // I've violated referential integrity. 
    // It doesn't say which relationship is broken, 
    // but I guess it's the B-C one since 
    // the debugger shows them to have different values if PKs 

    ctx.Bs.AddObject(b); 

    ctx.SaveChanges(); 
    } 
} 

ich das versucht habe sowohl mit dem Code-Generator-Standard EF (diejenige, die die EF Entity Klasse als Basisklasse für generierte Entitäten verwendet) und mit dem Code-Generator des Self-Tracking Entities. Das Ergebnis ist das gleiche.

So stürzt der Code ab. Der Grund liegt wahrscheinlich darin, dass nach A und B die verknüpften B und C unterschiedliche PK-Werte erhalten, was für Entitäten mit 1-1-Beziehung illegal ist.

Was ich erwartet war, war C automatisch zu bekommen ist PK synchronisiert mit dem Wert B von A Instanz bekommen. Das scheint vernünftig, weil ich mit einem Objektdiagramm arbeite, ich habe eine bestehende B - C Beziehung, die in Ordnung ist, und ich erwarte, dass es OK bleibt nach der Verknüpfung B mit A. Warum sollte es brechen? Ich würde es verstehen, wenn entweder B oder C in DB existierte und ich nicht in der Lage war, ihre PKs zu ändern. Aber das ist nicht der Fall, beide wurden gerade erstellt.

Ich kann die Kette von Schlüsseln nicht trennen, indem ich separate PK-Spalten für FKs verwende, da EF beide Seiten einer 1-1-Beziehung als PKs benötigt.

Ich möchte die Schlüssel nicht manuell synchronisieren, da es in der Tat mehr 1-1-bezogene Tabellen gibt und der Sync-Code an vielen Stellen erscheinen müsste.

Ich glaube, dass ich in der Lage sein werde, die T4-Vorlage des STE-Generators zu aktualisieren, um PK-Aktualisierungen 1-1 Beziehungen zu kaskadieren. Aber ich bin mit T4 nicht sehr vertraut und nicht allzu glücklich, das zu tun.

Ich habe 2 Fragen:

  1. Ist meine Erwartung von kaskadierten PK Updates in meinem Fall aus irgendwelchen Gründen nicht in Ordnung? (Scheint aber bizarr) I.e., ist es ein Fehler oder eine Funktion?
  2. Gibt es andere und vorzugsweise einfachere Möglichkeiten, das Problem zu beheben, als die STE-Vorlage zu ändern? Vielleicht einige magische Optionen in EF-Mappings oder Kontext?

Vielen Dank im Voraus.

Antwort

3

Das Problem besteht darin, dass der Dienst, der die Zuordnung von IDs von einem referenzierten Objekt zu einem anderen behandelt, der Kontext ist. Aber zu der Zeit, wenn Sie die Assoziation tatsächlich herstellen, ist kein Objekt in dem Kontext. Das wäre normalerweise kein Problem, da die Beziehung beim Hinzufügen von B zum Kontext behoben wird.

Leider tun Sie das nicht. Stattdessen erstellen Sie eine zusätzliche Beziehung mit A, aber dann lügen Sie den Kontext und behaupten, dass alles bereits behoben ist. Genauer gesagt, rufen Sie EntitySet.Attach, die wirklich nur für bereits fixierte Objekte gedacht ist.

Auf der anderen Seite, Code wie diesen sollte gut funktionieren:

public B CreateB() 
{ 
    return new B 
    { 
     Id = Guid.NewGuid(), 
     C = new C(), 
    }; 
} 

void SaveBlahBlahBlah(A a, B b) 
{  
    using(var ctx = new MyContext()) 
    {  
    ctx.Bs.AddObject(b); 

    ctx.SaveChanges(); 
    } 
} 

Beachten Sie, dass alles, was ich hier getan habe, ist den problematischen Code löschen, die nichts mit der Beziehung zwischen B und C zu tun hat .

Kurz gesagt, Vorsicht vor Attach. Sie müssen wissen, was Sie tun, wenn Sie es anrufen.

UPDATE

Eine Version, die bestehenden Instanzen A Griffe:

void SaveBlahBlahBlah(A a, B b) 
{  
    Debug.Assert(a.B != b); 

    using(var ctx = new MyContext()) 
    {  
    ctx.As.Attach(a); 

    a.B = b; // it's crucial that this link is set after attaching a to context! 

    ctx.Bs.AddObject(b); 

    ctx.SaveChanges(); 
    } 
} 
+1

+1 Sehr klare Erklärung! –

+0

Danke. Ich sollte beachten, dass ich 'Attach' verwendet habe, weil EF ohne es versucht hat, 'A' einzufügen, was zu einem Fehler führte, da' A' bereits in der DB existierte. Ich habe das Modell und den Geschmack (ich benutzte damals EFCF) seit dieser Zeit geändert. Vielleicht wird dein Vorschlag funktionieren, ich werde es versuchen. –

+0

Sie können 'Attach' verwenden, aber Sie müssen es tun, * bevor * Sie die Beziehung mit B machen. –

Verwandte Themen