2010-11-01 13 views
39

Ich verwende DBContext und habe zwei Klassen, deren Eigenschaften alle virtuell sind. Ich kann im Debugger sehen, dass ich ein Proxy-Objekt bekomme, wenn ich den Kontext abfrage. Eine Auflistungseigenschaft ist jedoch immer noch null, wenn ich versuche, sie hinzuzufügen. Ich dachte, dass der Proxy sicherstellen würde, dass die Sammlung initialisiert wird. Warum ist die erste Proxy-Sammlung meines Entity Framework-Codes ungültig und warum kann ich sie nicht festlegen?

Weil mein Poco Objekt außerhalb seiner Datenkontext verwendet werden, habe ich einen Scheck für die Sammlung im Konstruktor null zu sein und schaffen es, falls erforderlich:

public class DanceStyle 
{ 
    public DanceStyle() 
    { 
     if (DanceEvents == null) 
     { 
      DanceEvents = new Collection<DanceEvent>(); 
     } 
    } 
    ... 
    public virtual ICollection<DanceEvent> DanceEvents { get; set; } 
} 

, die außerhalb des Datenkontext funktioniert, aber wenn Ich erhalte ein Objekt mit einer Abfrage, obwohl der Test wahr ist, wenn ich versuche, es zu setzen, bekomme ich folgende Ausnahme: 'Die Eigenschaft' DanceEvents 'vom Typ' DanceStyle_B6089AE40D178593955F1328A70EAA3D8F0F01DDE9F9FBD615F60A34F9178B94 'kann nicht gesetzt werden, da die Sammlung bereits auf eine EntityCollection gesetzt ist. "

Ich kann sehen, es ist null und ich kann nicht hinzufügen, aber auch kann ich es auf eine Sammlung festlegen, weil der Proxy sagt, dass es bereits festgelegt ist. Deshalb kann ich es nicht benutzen. Ich bin verwirrt. Hier

ist die DanceEvent Klasse:

public class DanceEvent 
{ 
    public DanceEvent() 
    { 
     if (DanceStyles == null) 
     { 
      DanceStyles = new Collection<DanceStyle>(); 
     } 
    } 
    ... 
    public virtual ICollection<DanceStyle> DanceStyles { get; set; } 
} 

ich die anderen Wert-Typ-Eigenschaften aus dem obigen Code weggelassen. Ich habe keine anderen Zuordnungen für diese Klassen in der Kontextklasse.

Antwort

13

fand ich die Lösung für dieses Problem hier: Code First adding to collections? How to use Code First with repositories?

ich aus allen Eigenschaften mit Ausnahme von Sammlungen und faul geladenen Objekte, das heißt, alle nativen Typen ‚virtueller‘ entfernt.

Aber ich verstehe immer noch nicht, wie Sie mit der Situation enden können, wo Sie eine Null-Sammlung haben, die Sie nicht verwenden können und keine Möglichkeit haben, sie auf eine gültige Sammlung zu setzen.

Ich fand auch this answer from Rowan Miller auf einem MSDN-Forum

Hallo,

Wenn Sie alle Ihre Eigenschaften virtuellen machen, dann wird EF-Proxy-Klassen zur Laufzeit generieren, die aus leitet Ihre POCO eingestuft, erlauben diese Proxies EF um Änderungen in Echtzeit herauszufinden, anstatt die ursprünglichen Werte Ihres Objekts erfassen zu müssen und dann beim Speichern nach Änderungen zu suchen (dies hat natürlich Vorteile bei der Leistung und der Speichernutzung, aber der Unterschied ist vernachlässigbar, es sei denn, Sie haben eine große Anzahl Entitäten, die in den Speicher geladen werden). Diese werden als "Änderungsnachverfolgungs-Proxies" bezeichnet. Wenn Sie Ihre Navigationseigenschaften virtuell machen, wird weiterhin ein Proxy generiert, der jedoch viel einfacher ist und nur eine gewisse Logik enthält, um beim Zugriff auf eine Navigationseigenschaft Lazy Loading durchzuführen.

Da Ihr ursprünglicher Code Änderungsverfolgungs-Proxies generiert hat, hat EF Ihre Auflistungseigenschaft durch einen speziellen Auflistungstyp ersetzt, um Informationen zu Änderungen zu erhalten. Da Sie versuchen, die Sammlung zurück auf eine einfache Liste im Konstruktor zu setzen, erhalten Sie die Ausnahme.

Sofern Sie keine Leistungsprobleme feststellen, würde ich dem Vorschlag von Terrence folgen und nur "virtual" aus Ihren Nicht-Navigationseigenschaften entfernen.

~ Rowan

So scheint es, dass ich das Problem nur mit einem vollen 'Change Tracking-Proxy', wenn alle meine Eigenschaften virtuell sind. Aber warum kann ich die virtuelle Eigenschaft auf dem Änderungsverfolgungsproxy immer noch nicht verwenden? Dieser Code sprengt auf Linie drei, weil ds2.DanceEvents null ist und nicht im Konstruktor festgelegt werden:

DanceStyle ds2 = ctx.DanceStyles.Where(ds => ds.DanceStyleId == 1).Single(); 
DanceEvent evt = CreateDanceEvent(); 
ds2.DanceEvents.Add(evt); 

ich immer noch verwirrt bin, auch wenn mein Code jetzt wegen der fix oben arbeitet.

47

Wie Sie in der Antwort auf Ihre eigene Frage richtig festgestellt haben, funktioniert das Entfernen des Schlüsselworts "virtual" aus den Auflistungseigenschaften um das Problem, indem verhindert wird, dass Entity Framework einen Änderungsverfolgungsproxy erstellt. Für viele Benutzer ist dies jedoch keine Lösung, da Änderungsverfolgungsproxys sehr praktisch sein können und dazu beitragen können, Probleme zu vermeiden, wenn Sie vergessen, Änderungen an den richtigen Stellen in Ihrem Code zu erkennen.

Ein besserer Ansatz wäre, Ihre POCO-Klassen so zu ändern, dass sie die Auflistungseigenschaften in ihrem get-Accessor statt im Konstruktor instanziieren. Hier ist Ihre POCO-Klasse, modifizierte, um Change Tracking-Proxy-Erstellung zu ermöglichen:

public class DanceEvent 
{ 
    private ICollection<DanceStyle> _danceStyles; 
    public virtual ICollection<DanceStyle> DanceStyles 
    { 
     get { return _danceStyles ?? (_danceStyles = new Collection<DanceStyle>()); } 
     protected set { _danceStyles = value; } 
    } 
} 

In dem obigen Code die Sammlung Objekt ist nicht mehr automatisch, sondern hat ein dahinter liegendes Feld. Es ist besser, wenn Sie den Setter geschützt lassen und verhindern, dass Code (außer dem Proxy) diese Eigenschaften nachträglich ändert. Sie werden feststellen, dass der Konstruktor nicht mehr benötigt wurde und entfernt wurde.

+0

Das ist eine andere Möglichkeit, aber es erklärt nicht meinen Kommentar: "Das funktioniert außerhalb des Datenkontexts, aber wenn ich ein Objekt mit einer Abfrage abrufen, obwohl der Test wahr ist, wenn ich versuche, es zu setzen, ich bekomme folgende Ausnahme: 'Die Eigenschaft' DanceEvents 'vom Typ' DanceStyle_B6089AE40D178593955F1328A70EAA3D8F0F01DDE9F9FBD615F60A34F9178B94 'kann nicht gesetzt werden, da die Sammlung bereits auf eine EntityCollection gesetzt ist.' Ich kann sehen, dass es null ist und ich kann es nicht hinzufügen, aber ich kann es auch nicht auf eine Sammlung setzen, weil der Proxy sagt, dass es bereits gesetzt ist. Deshalb kann ich es nicht benutzen. Ich bin verwirrt. " –

+0

Ich kann nicht wiedergeben, was Sie beschreiben. Wenn die Entität als ein Proxy instanziiert wird (entweder als Ergebnis davon, dass sie von einer Abfrage zurückgegeben wird oder wenn die DbSet.Create-Methode verwendet wird), werden die Auflistungseigenschaften meiner Instanz mit EntityCollection-Objekten instanziiert. Sie sollten diese Eigenschaften niemals festlegen müssen - fügen Sie einfach Entitäten von ihnen hinzu oder entfernen Sie sie. – Pando

+0

Es ist möglich, dass sich das Verhalten geändert hat, seit ich meine Frage vor zwei Jahren geschrieben habe. –

3

Alte Frage ...

Poco Klasse:

public partial class MyPOCO 
{ 
    public MyPOCO() 
    { 
     this.MyPocoSub = new HashSet<MyPocoSub>(); 
    } 

    //VIRTUAL 
    public virtual ICollection<MyPocoSub> MyPocoSub { get; set; } 
} 

und Proxy-Code:

public override ICollection<MyPocoSubSet> MyPocoSubSets 
    { 
     get 
     { 
      ICollection<MyPocoSubSet> myPocoSubSets = base.MyPocoSubSets; 
      if (!this.ef_proxy_interceptorForMyPocoSubSets(this, myPocoSubSets)) 
      { 
       return base.MyPocoSubSets; 
      } 
      return myPocoSubSets; 
     } 
     set 
     { 
      if (value != this.RelationshipManager.GetRelatedEnd("WindowsFormsApplication.Models.MyPocoSubSet_MyPOCO", "MyPocoSubSet_MyPOCO_Source")) 
      { 
       // EXCEPTION 
       throw new InvalidOperationException("The property 'MyPocoSubSets' on type 'MyPOCO_A78FCE6C6A890855C68B368B750864E3136B589F9023C7B1D90BF7C83FD291AC' cannot be set because the collection is already set to an EntityCollection."); 
      } 
      base.MyPocoSubSets = value; 
     } 
    } 

Wie Sie diese Ausnahme in Proxy-Klasse angehoben sehen in ExtityFramework 5. Diese Mittel dieses Verhalten existiert noch.