2009-03-18 8 views
43

Es scheint, dass das Serialisieren von Entity Framework-Objekten in JSON weder mit dem nativen DataContractJsonSerializer von WCF noch mit dem nativen JavaScript-Serializer von ASP.NET möglich ist. Dies liegt an den Problemen der Referenzzählung, die beide Serializer ablehnen. Ich habe auch versucht, Json.NET, die auch speziell bei einer Referenz Zählen Problem fehlschlägt.Entity Framework-Objekte in JSON serialisieren


Edit: Json.NET kann jetzt serialize and deserialize Entity Framework entities.


Meine Objekte sind Entity Framework-Objekte, die zusätzliche Business-Funktionalität durchführen, sind überlastet (z. B. Authentifizierung, etc.) und ich will nicht diese Klassen mit plattformspezifische Attribute dekorieren, usw., wie ich will um eine plattformunabhängige API zu präsentieren.

Ich habe über die einzelnen Schritte tatsächlich gebloggt ich https://blog.programx.co.uk/2009/03/18/wcf-json-serialization-woes-and-a-solution/ obwohl ging

Habe ich etwas offensichtlich verpasst?

+0

Ja JSon.NET serialisiert, aber ich würde IQueryable nicht JSON-String zurückkommen! Wenn ich zurückkehrte IQeryable könnte ich OData nutzen. –

+0

Link auf Bloggingabout.net. Ist gebrochen –

+0

@MichaelFreigeid Ja, ich habe das erkannt, als jemand einen anderen Beitrag gelöscht hat. Was war nett. Scheint, dass Blog beschlossen, meinen Blog zu löschen. Nicht glücklich. Ich kann mich nur entschuldigen. Ich habe mir die Zeit genommen, auf Internetarchive zurückzuschauen und an einem anderen Ort erneut zu veröffentlichen. –

Antwort

71

Die Art, wie ich dies tue, besteht darin, die Daten, die ich serialisieren möchte, in einen anonymen Typ zu projizieren und das zu serialisieren. Dies stellt sicher, dass nur die Information, die ich tatsächlich in dem JSON möchte, serialisiert ist, und ich nicht unbeabsichtigt etwas weiter unten in dem Objektgraph serialisieren. Es sieht so aus:

var records = from entity in context.Entities 
       select new 
       { 
        Prop1 = entity.Prop1, 
        Prop2 = entity.Prop2, 
        ChildProp = entity.Child.Prop 
       } 
return Json(records); 

Ich finde anonyme Typen einfach ideal dafür. Dem JSON ist es offensichtlich egal, um welchen Typ es sich handelt. Und anonyme Typen geben Ihnen vollständige Flexibilität hinsichtlich der Eigenschaften und Struktur, die Sie in JSON einfügen.

+2

vielen Dank dafür, ich habe stundenlang auf dieser Seite gestöbert !! – Peter

+1

Ausgezeichnete Lösung. Gibt es eine praktikable Möglichkeit, ein JavaScript-Objekt wieder in ein EF-Objekt zu deserialisieren? –

+0

Samuel, der Standardmodellbinder, kann im Allgemeinen mit EF-Typen umgehen. Aber ich bevorzuge es, zu einem bearbeitungsspezifischen Modell zu deserialisieren und dann dem EF-Typ zuzuordnen. –

17

Microsoft hat einen Fehler in der Art und Weise gemacht, wie EF-Objekte in Datenverträge umgewandelt wurden. Sie enthalten die Basisklassen und die Backlinks.

Ihre beste Wette besteht darin, für jede Entität, die Sie zurückgeben möchten, entsprechende Datenübertragungsobjektklassen zu erstellen. Diese beinhalten nur die Daten, nicht das Verhalten und nicht die EF-spezifischen Teile einer Entität. Sie erstellen auch Methoden zum Übersetzen in und aus Ihren DTO-Klassen.

Ihre Dienste würden dann die Datenübertragungsobjekte zurückgeben.

+0

Es gibt jetzt eine Option, um die Serialisierung einseitig zu machen. Es ist möglich, dass diese Option nicht existiert hat, als Sie diesen Beitrag gemacht haben. Ich dachte nur, ich würde es hinzufügen, für den Fall, dass andere in der Zukunft darauf stoßen. – Yuck

+12

@Yuck: Fügen Sie bitte einen Link zu den Informationen zu dieser Funktion hinzu. –

+0

Soweit ich weiß EF hat keine solche Einstellung. Dies ist nur für Linq-to-SQL. – Ziad

1

Eine weitere Lösung, wenn Sie eine bessere Code-Konsistenz haben möchten, ist die Verwendung von JavaScriptConverter, das zirkuläre Referenzabhängigkeiten behandelt und solche Verweise nicht serialisiert.

Ich habe über hier gebloggt:

http://hellowebapps.com/2010-09-26/producing-json-from-entity-framework-4-0-generated-classes/

+1

Ich stimme Mehal zu. Ich erweiterte Ihr Beispiel, um einige andere Fälle in meiner Antwort hier zu behandeln http://stackoverflow.com/questions/4053161/serializing-entity-framework-problems –

+0

Ich wünschte, dass dieser Code arbeitete ... – jocull

+0

Der Link ist gebrochen –

2

Meine Lösung war, einfach die Referenzeinheiten auf meinem Kind Eltern zu entfernen.

Also in meinem Modell, wählte ich die Beziehung und änderte die Elternreferenz, um Intern statt öffentlich zu sein.

Vielleicht nicht eine ideale Lösung für alle, aber für mich gearbeitet.

+0

Das scheint ganz gut zu funktionieren! – Farinha

1

FYI fand ich eine alternative Lösung

Sie die übergeordnete Beziehung als privat können so dann die Eigenschaften werden bei der Übersetzung ausgesetzt, die unendliche Eigenschaft Schleife, für Tage

1

ich mit diesem Problem kämpfte Entfernen

Lösung. In deinem edmx Fenster. - Rechtsklick und Codegenerierung Artikel hinzufügen - Wählen Sie Registerkarte Code - wählen EF 4x.POCOC Entity Generator

Wenn Sie es nicht sehen, dann werden Sie es mit nuget installieren müssen, EF suchen.

Das Entity Generator wird alles, was Sie komplexe Art und Entitätsobjekt in einfache Klassen erzeugen in Json serialisiert werden.

1

ich es gelöst, indem nur Objekttypen von Namespace System bekommen und sie dann Wörterbuch konvertieren und dann Liste hinzufügen. Funktioniert gut für mich :)

Es sieht kompliziert aus, aber das war die einzige generische Lösung, die für mich arbeitete ... Ich benutze diese Logik für einen Helfer, den ich mache, so ist es für einen speziellen Einsatz wo Ich muss in der Lage sein, jeden Objekttyp im Entitätsobjekt abzufangen, vielleicht könnte jemand ihn an seine Verwendung anpassen.

List<Dictionary<string, string>> outputData = new List<Dictionary<string, string>>(); 

// convert all items to objects 
var data = Data.ToArray().Cast<object>().ToArray(); 

// get info about objects; and get only those we need 
// this will remove circular references and other stuff we don't need 
PropertyInfo[] objInfos = data[0].GetType().GetProperties(); 
foreach (PropertyInfo info in objInfos) { 
    switch (info.PropertyType.Namespace) 
    { 
      // all types that are in "System" namespace should be OK 
      case "System": 
       propeties.Add(info.Name); 
       break; 
    } 
} 
Dictionary<string, string> rowsData = null; 
foreach (object obj in data) { 
    rowsData = new Dictionary<string, string>(); 
    Type objType = obj.GetType(); 
    foreach (string propertyName in propeties) 
    { 
//if You don't need to intercept every object type You could just call .ToString(), and remove other code 
     PropertyInfo info = objType.GetProperty(propertyName); 
     switch(info.PropertyType.FullName) 
     { 
       case "System.String": 
        var colData = info.GetValue(obj, null); 
        rowsData.Add(propertyName, colData != null ? colData.ToString() : String.Empty); 
        break; 
//here You can add more variable types if you need so (like int and so on...) 
      } 
     } 

     outputData .Add(rowsData); // add a new row 
} 

„Output“ ist sicher für JSON kodieren ... Hoffnung jemand wird diese Lösung hilfreich. Es hat Spaß gemacht, es zu schreiben :)

2

Basiert auf @Craig Stuntz Antwort und ähnlich einem DTO, für meine Lösung habe ich eine Teilklasse des Modells (in einer separaten Datei) und eine Rückgabeobjektmethode mit wie ich erstellt möchte, dass nur die Eigenschaften verwendet werden, die benötigt werden.

namespace TestApplication.Models 
{ 
    public partial class Employee 
    { 
     public object ToObject() 
     { 
      return new 
      { 
       EmployeeID = EmployeeID, 
       Name = Name, 
       Username = Username, 
       Office = Office, 
       PhoneNumber = PhoneNumber, 
       EmailAddress = EmailAddress, 
       Title = Title, 
       Department = Department, 
       Manager = Manager 
      }; 
     } 
    } 
} 

Und dann nenne ich es einfach in meiner Rückkehr:

var employee = dbCtx.Employees.Where(x => x.Name == usersName).Single(); 
return employee.ToObject(); 

Ich denke, die akzeptierte Antwort mehr schnell und einfach ist, ich meine Methode nur alle meine Renditen und trocken zu halten verwenden.