Serialisieren von Entity Framework Probleme
Wie mehrere andere Leute, ich habe Probleme mit der Serialisierung Entity-Framework-Objekte, so dass ich senden können Sie die Daten über AJAX im JSON-format.
Ich habe den folgenden server-side-Methode, die ich bin versucht zu rufen, die mit AJAX durch jQuery
[WebMethod]
public static IEnumerable<Message> GetAllMessages(int officerId)
{
SIBSv2Entities db = new SIBSv2Entities();
return (from m in db.MessageRecipients
where m.OfficerId == officerId
select m.Message).AsEnumerable<Message>();
}
Aufruf per AJAX Ergebnisse in dieser Fehler:
A circular reference was detected while serializing an object of type \u0027System.Data.Metadata.Edm.AssociationType
Ist wegen der Art, wie das Entity Framework erstellt zirkuläre Referenzen zu allen Objekten relevanten und zugänglichen server-Seite.
Stieß ich auf den folgenden code aus ( http://hellowebapps.com/2010-09-26/producing-json-from-entity-framework-4-0-generated-classes/ ), die behauptet, dieses problem zu umgehen, durch die Begrenzung der maximalen Tiefe für Referenzen. Ich habe den folgenden code, denn ich hatte es zu zwicken etwas, um es zu bekommen Arbeit (Alle Spitzen Klammern fehlen, den code auf der website)
using System.Web.Script.Serialization;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System;
public class EFObjectConverter : JavaScriptConverter
{
private int _currentDepth = 1;
private readonly int _maxDepth = 2;
private readonly List<int> _processedObjects = new List<int>();
private readonly Type[] _builtInTypes = new[]{
typeof(bool),
typeof(byte),
typeof(sbyte),
typeof(char),
typeof(decimal),
typeof(double),
typeof(float),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(short),
typeof(ushort),
typeof(string),
typeof(DateTime),
typeof(Guid)
};
public EFObjectConverter( int maxDepth = 2,
EFObjectConverter parent = null)
{
_maxDepth = maxDepth;
if (parent != null)
{
_currentDepth += parent._currentDepth;
}
}
public override object Deserialize( IDictionary<string,object> dictionary, Type type, JavaScriptSerializer serializer)
{
return null;
}
public override IDictionary<string,object> Serialize(object obj, JavaScriptSerializer serializer)
{
_processedObjects.Add(obj.GetHashCode());
Type type = obj.GetType();
var properties = from p in type.GetProperties()
where p.CanWrite &&
p.CanWrite &&
_builtInTypes.Contains(p.PropertyType)
select p;
var result = properties.ToDictionary(
property => property.Name,
property => (Object)(property.GetValue(obj, null)
== null
? ""
: property.GetValue(obj, null).ToString().Trim())
);
if (_maxDepth >= _currentDepth)
{
var complexProperties = from p in type.GetProperties()
where p.CanWrite &&
p.CanRead &&
!_builtInTypes.Contains(p.PropertyType) &&
!_processedObjects.Contains(p.GetValue(obj, null)
== null
? 0
: p.GetValue(obj, null).GetHashCode())
select p;
foreach (var property in complexProperties)
{
var js = new JavaScriptSerializer();
js.RegisterConverters(new List<JavaScriptConverter> { new EFObjectConverter(_maxDepth - _currentDepth, this) });
result.Add(property.Name, js.Serialize(property.GetValue(obj, null)));
}
}
return result;
}
public override IEnumerable<System.Type> SupportedTypes
{
get
{
return GetType().Assembly.GetTypes();
}
}
}
Aber auch, wenn Sie diesen code in folgender Weise:
var js = new System.Web.Script.Serialization.JavaScriptSerializer();
js.RegisterConverters(new List<System.Web.Script.Serialization.JavaScriptConverter> { new EFObjectConverter(2) });
return js.Serialize(messages);
Ich bin immer noch zu sehen, die A circular reference was detected...
Ausnahme geworfen!
- mögliche Duplikate von Serialize Entity-Framework-Objekte in JSON
- Diese Frage geht mit anonymen Typen als eine Lösung, in der Erwägung, dass nehme ich einen anderen Ansatz in der Lösung, die ich versuche, zu arbeiten
- Gibt es einen Grund, Sie sind nicht glücklich, dass die Projektion auf einen anonymen Typ (oder ein statisch definierten Typ). Sicherlich, die Komplexität Ihrer Art braucht Abflachung in irgendeiner Weise? Ich bin mir nicht sicher, warum die genannten Konverter nicht funktionieren - sind Sie glücklich zu post den code für die 'Nachricht' in der Klasse, die Sie versuchen zu serialisieren? Auch - du bist immer noch das zurückstellen der Abfrage-Ausführung in der LINQ-Abfrage, versuchen calling "ToList ()" nach dem 'AsEnumerable ()' - Aufruf.
- David - es gibt viele Gründe, nicht zu wollen-Projekts auf jede einzelne Person zugreifen. TROCKEN für einen start.. betrachten Sie haben ein Unternehmen mit 40 einfache Eigenschaften und nur 1 complex type-Eigenschaft erzeugt einen Zirkelbezug!
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich löste diese Probleme mit den folgenden Klassen:
und
Können Sie jetzt sicher einen Anruf tätigen, wie
new EFJavaScriptSerializer().Serialize(obj)
Update : seit version Telerik v1.3+ können Sie jetzt überschreiben Sie die GridActionAttribute.CreateActionResult Methode und daher können Sie leicht integrieren diese Serializer in bestimmten controller-Methoden durch die Anwendung Ihre benutzerdefinierten
[GridAction]
Attribut:und
und endlich..
RelationshipManager
? Sollte das entfernt werden?Equals
zu. Ich schlage vor, mitHashSet<object>
mit einemIEqualityComparer<object>
dass die Rendite der normalen hash-code, und prüft, reference Gleichheit zu fordern, anstattEquals
.maxDepth
wird ignoriert, auf unterstützte Typen, seit Sie erstellen einen neuen serializer, wenn Sie recurse. Nicht sicher, wie viel Einsatz es ist die Tiefe-Begrenzung code an dieser Stelle. Vielleicht solltest du entfernen und stattdessen eine neue Instanz zu erstellen, geben Sie nur die, die Sie bekam inSerialize
.Sie können auch trennen Sie das Objekt aus dem Kontext und es wird entfernen Sie die Navigations-Eigenschaften, so dass es serialisiert werden kann. Für meine Daten-repository-Klassen, die verwendet werden, mit Json verwende ich etwas wie dieses.
Standardmäßig wird das zurückgegebene Objekt alle komplexen/navigation Eigenschaften, aber durch die Einstellung trennen = true wird Sie diese Eigenschaften entfernen, und kehren nur das Basisobjekt. Für eine Liste von Objekten, die Umsetzung sieht wie folgt aus
DataContext.Detach()
Methode. Dieser entledigte sich des "Objekts entsorgt' Ausnahme...EntityState
es wird ein 2-level-Tiefe.Habe ich gerade erfolgreich getestet mit diesem code.
Kann es sein, dass in Ihrem Fall Ihre Nachricht ein Objekt in einer anderen Baugruppe? Der überschriebenen Eigenschaft
SupportedTypes
ist wieder alles NUR in Ihrer eigenen Assembly also beim serialisieren aufgerufen, dieJavaScriptSerializer
standardmäßig der standard -JavaScriptConverter
.Sollten Sie in der Lage sein zu überprüfen, diese zu Debuggen.
Ihre Fehler aufgetreten ist aufgrund einiger "Referenz" - Klassen, generiert, die von EF für einige Entitäten mit 1:1 Beziehungen und das JavaScriptSerializer fehlgeschlagen zu serialisieren.
Ich habe eine Problemumgehung, durch hinzufügen einer neuen Bedingung :
Den code, um die komplexen Eigenschaften sieht wie folgt aus :
Hoffe, dies hilft Ihnen.
Ich hatte ein ähnliches problem mit schob meine Ansicht über Ajax-UI-Komponenten.
Ich auch gefunden und versucht, das code-Beispiel, das Sie zur Verfügung gestellt. Einige Probleme hatte ich mit diesem code:
SupportedTypes
war nicht greifen die Typen, die ich benötigt, damit der Konverter nicht genanntnew JavaScriptSerializer
Hier sind die fixes implementiert habe ich für solche Fragen:
Wiederverwendung der gleichen serializer
Ich Sie einfach wiederverwendet, die vorhandenen serializer übergeben wird, in
Serialize
um dieses problem zu lösen. Diese brach die Tiefe hack wenn.Abschneiden auf bereits besuchte, als vielmehr auf die Tiefe
Statt abschneiden auf die Tiefe, ich habe eine
HashSet<object>
bereits gesehen-Instanzen (mit einer benutzerdefiniertenIEqualityComparer
dass überprüft Referenz-Gleichheit). Ich konnte einfach nicht recurse wenn ich eine Instanz hatte ich bereits gesehen. Dies ist der gleiche Mechanismus zur Erkennung eingebaut, dieJavaScriptSerializer
sich selbst, so ganz gut geklappt.Das einzige problem bei dieser Lösung ist, dass die Serialisierung Ausgabe nicht sehr deterministisch. Die Reihenfolge der Kürzung ist stark abhängig von der Reihenfolge, die Reflexionen findet sich die Eigenschaften. Sie könnten das so lösen (mit einem perf-hit) durch Sortieren, bevor recursing.
SupportedTypes benötigt die richtigen Arten
Meine
JavaScriptConverter
Leben könnte nicht in der gleichen assembly wie mein Modell. Wenn Sie planen, um die Wiederverwendung dieser converter code, werden Sie wahrscheinlich das gleiche problem.Um dies zu lösen musste ich pre-durchqueren Sie das Objekt Baum, halten ein
HashSet<Type>
schon Typen gesehen (um zu vermeiden, meine eigene unendliche Rekursion), und übergeben, dieJavaScriptConverter
bevor Sie Sie registrieren.Rückblick auf meine Lösung, würde ich jetzt die Verwendung der code generation templates zum erstellen einer Liste der entity-Typen. Das wäre viel mehr kinderleicht (es verwendet einfache iteration), und haben viel bessere perf denn es würde produzieren eine Liste der zur compile-Zeit. Ich würde noch übergeben diese an den Umrichter, so dass es wiederverwendet werden kann zwischen den Modellen.
Meine Letzte Lösung
Warf ich den code ein und versuchte es erneut 🙂
Ich schrieb nur code-Projekt auf neue Typen ("ViewModel" - Typen in Ihrem Fall, es wäre der service-Vertrag-Typen), bevor Sie meine Serialisierung. Die Absicht, mein code war expliziter gemacht, die es mir erlaubt, zu serialisieren, die nur die Daten, die ich wollte, und es hat nicht das potential haben, das Abrutschen in Abfragen, die auf einen Unfall (z.B. serialisieren meine ganze DB).
Meine Typen waren ziemlich einfach, und ich brauchte nicht die meisten von Ihnen für meine Ansicht. Ich könnte schauen Sie in AutoMapper zu tun, einige dieser Projektion in die Zukunft.