2015-09-20 5 views
23

ich mit EF während der Aktualisierung folgende Fehlermeldung erhalten:Die Beziehung kann nicht geändert werden, da ein oder mehrere der Fremdschlüsseleigenschaften sind nicht auf NULL festlegbare

Der Vorgang ist fehlgeschlagen: Die Beziehung nicht, weil man verändert werden könnte oder Weitere Fremdschlüsseleigenschaften sind nicht nullfähig. Wenn eine Änderung an einer Beziehung vorgenommen wird, wird die zugehörige Fremdschlüsseleigenschaft auf einen Nullwert festgelegt. Wenn der Fremdschlüssel keine Nullwerte unterstützt, muss eine neue Beziehung definiert werden, der Fremdschlüsseleigenschaft muss ein anderer Wert ungleich Null zugewiesen werden, oder das nicht verwandte Objekt muss gelöscht werden.

Gibt es eine allgemeine Art und Weise, welche Fremdschlüssel-Eigenschaften über Fehler verursachen zu finden?

[Update]

Für einen Fall folgenden Code Ursache über Fehler (arbeitete ich in einer getrennten Umgebung, so habe ich graphdiff meiner Objekte Graph zu aktualisieren), wenn es will _uow.Commit(); auszuführen:

public void CopyTechnicalInfos(int sourceOrderItemId, List<int> targetOrderItemIds) 
{ 
    _uow = new MyDbContext(); 
    var sourceOrderItem = _uow.OrderItems 
      .Include(x => x.NominalBoms) 
      .Include("NominalRoutings.NominalSizeTests") 
      .AsNoTracking() 
      .FirstOrDefault(x => x.Id == sourceOrderItemId); 


    var criteria = PredicateBuilder.False<OrderItem>(); 
    foreach (var targetOrderItemId in orderItemIds) 
    { 
     int id = targetOrderItemId; 
     criteria = criteria.OR(x => x.Id == id); 
    } 
    var targetOrderItems = _uow.OrderItems 
           .AsNoTracking() 
           .AsExpandable() 
           .Where(criteria) 
           .ToList(); 

    foreach (var targetOrderItem in targetOrderItems) 
    { 
     //delete old datas and insert new datas 
     targetOrderItem.NominalBoms = sourceOrderItem.NominalBoms; 
     targetOrderItem.NominalBoms.ForEach(x => x.Id = 0); 

     targetOrderItem.NominalRoutings = sourceOrderItem.NominalRoutings; 
     targetOrderItem.NominalRoutings.ForEach(x => x.Id = 0); 
     targetOrderItem.NominalRoutings 
         .ForEach(x => x.NominalTests.ForEach(y => y.Id = 0)); 
     targetOrderItem.NominalRoutings 
         .ForEach(x => x.NominalSizeTests.ForEach(y => y.Id = 0)); 
     _uow.OrderItems.UpdateGraph(targetOrderItem, 
            x => x.OwnedCollection(y => y.NominalBoms) 
             .OwnedCollection(y => y.NominalRoutings, 
              with => with 
             .OwnedCollection(t => t.NominalTests))); 
    } 
    _uow.Commit(); 
} 
+0

Können Sie Ihren Code nicht eingrenzen und hier posten? Ich (wie auch einige andere) habe diese Art von Ausnahme noch nie erlebt, daher brauchen wir mehr Kontext, um das Problem zu diagnostizieren. – Hopeless

+0

@Hopeless: Ich meine generell, wie kann ich Fremdschlüsseleigenschaften finden, die den Fehler verursacht. aber wenn nötig, kann ich meinen Code auch posten. – Masoud

Antwort

53

In Entity Framework können Sie mit Fremdschlüsselzuordnungen arbeiten. Das heißt, ein Fremdschlüssel für ein anderes Objekt wird als ein Paar von zwei Eigenschaften ausgedrückt: eine primitive Fremdschlüsseleigenschaft (z. B. NominalRouting.OrderItemId) und eine Objektreferenz (NominalRouting.OrderItem).

Dies bedeutet, dass Sie entweder einen primitiven Wert oder eine Objektreferenz festlegen können, um eine Fremdschlüsselzuordnung herzustellen. Wenn Sie einen von ihnen festlegen, versucht EF, den anderen möglichst synchron zu halten. Leider kann dies auch zu Konflikten zwischen primitiven Fremdschlüsselwerten und ihren begleitenden Referenzen führen.

Es ist schwer zu sagen, was genau in Ihrem Fall passiert. Jedoch weiß ich do, dass Ihr Ansatz des "Kopierens" von Objekten von einem Elternteil zum anderen ist ... nicht ideal. Erstens ist es nie eine gute Idee, Primärschlüsselwerte zu ändern. Indem Sie sie auf 0 setzen, sehen Sie das Objekt wie neu aus, aber das ist es nicht. Zweitens weisen Sie anderen übergeordneten Objekten mehrmals die gleichen untergeordneten Objekte zu. I Denken Sie, dass Sie am Ende mit einer großen Anzahl von Objekten mit einem Fremdschlüssel Wert aber nicht Referenz enden.

Ich sagte "kopieren", weil das ist, was Sie scheinbar versuchen zu erreichen. Wenn dies der Fall ist, sollten Sie Objekte und Add ordnungsgemäß zu jedem targetOrderItem klonen. Zur gleichen Zeit frage ich mich, warum Sie (scheinbar) alle diese Objekte klonen. Es sieht so aus, als ob viele-zu-viele-Assoziationen hier angebracht sind. Aber das ist ein anderes Thema.

Nun Ihre eigentliche Frage: wie die widersprüchlichen Assoziationen zu finden?

Das ist sehr, sehr schwer. Es würde Code erfordern, um das konzeptionelle Modell zu durchsuchen und Eigenschaften zu finden, die an Fremdschlüsselzuordnungen beteiligt sind. Dann müssten Sie ihre Werte finden und Unstimmigkeiten finden. Hart genug, aber trivial im Vergleich zu bestimmen, wenn ein möglichen Konflikt ist ein tatsächlichen Konflikt. Lassen Sie mich das an zwei Beispielen verdeutlichen.Hier hat eine Klasse OrderItem eine erforderliche Fremdschlüsselzuordnung bestehend aus den Eigenschaften Order und OrderId.

var item = new OrderItem { OrderId = 1, ... }; 
db.OrderItems.Add(item); 
db.SaveChanges(); 

So gibt es ein Element mit OrderId zugeordnet und Order = null, und EF ist glücklich.

var item = db.OrderItems.Include(x => x.Order).Find(10); 
// returns an OrderItem with OrderId = 1 
item.Order = null; 
db.SaveChanges(); 

Wieder ein Element mit OrderId zugeordnet und Order = null, aber EF wirft die Ausnahme „Die Beziehung konnte nicht geändert werden ...“.

(und es gibt mehr möglichen Konfliktsituationen)

So ist es nicht genug, um für unerreichte Werte in OrderId/Order Paaren zu suchen, werden Sie müssen auch Unternehmen Staaten und genau wissen, in welcher Kombination von Zuständen eine Nichtübereinstimmung prüfen ist nicht erlaubt. Mein Ratschlag: Vergiss es, repariere deinen Code.

Es gibt jedoch einen schmutzigen Trick. Wenn EF versucht, fremde Schlüsselwerte und Referenzen zu finden, sammelt es irgendwo tief in einer verschachtelten Baumstruktur von if s die Konflikte, über die wir sprechen, in eine Elementvariable des ObjectStateManager mit dem Namen _entriesWithConceptualNulls. Es ist möglich, um seinen Wert zu erhalten, indem eine Reflexion zu tun:

#if DEBUG 

db.ChangeTracker.DetectChanges(); // Force EF to match associations. 
var objectContext = ((IObjectContextAdapter)db).ObjectContext; 
var objectStateManager = objectContext.ObjectStateManager; 
var fieldInfo = objectStateManager.GetType().GetField("_entriesWithConceptualNulls", BindingFlags.Instance | BindingFlags.NonPublic); 
var conceptualNulls = fieldInfo.GetValue(objectStateManager); 

#endif 

conceptualNulls ein HashSet<EntityEntry> ist, EntityEntry eine interne Klasse ist, so dass Sie nur die Sammlung im Debugger untersuchen eine Vorstellung von widersprüchlichen Einheiten zu erhalten. Nur zu Diagnosezwecken !!!

+0

Danke für Ihre Antwort, Sie sagten "es ist nie eine gute Idee, Primärschlüsselwerte zu ändern" und "Sie sollten Objekte richtig klonen ...", das Ergebnis ist das gleiche in diesen 2 Fällen, warum denken Sie, dass das zweite besser ist ? – Masoud

+0

Im Allgemeinen funktioniert es am besten, wenn Entitäten separat identifizierbare Objekte sind, so dass EF sie richtig verfolgen kann. Ich weiß nicht, was du mit "diesen 2 Fällen" meinst. Wenn dies bedeutet, dass Sie etwas anderes versucht haben, schauen wir es uns besser in einer anderen Frage an. Diese Frage konzentriert sich darauf, wie FK-Fehler gefunden (nicht behoben) werden. –

+2

Vielen Dank für den schmutzigen Trick! Ohne sie war es schwierig für mich, die an den Konflikten beteiligten Entitäten zu erkennen. – reexmonkey

Verwandte Themen