Beim Serialisieren eines Objekts vom Typ ‘SubSonic.Schema .DatabaseColumn’ wurde ein Zirkelverweis erkannt.

Ich versuche, eine einfache JSON-Rückkehr zu machen, aber ich habe Probleme, die ich unten habe.

public JsonResult GetEventData() { var data = Event.Find(x => x.ID != 0); return Json(data); } 

Ich bekomme ein HTTP 500 mit der Ausnahme, wie im Titel dieser Frage gezeigt. Ich habe es auch versucht

 var data = Event.All().ToList() 

Das gab das gleiche Problem.

Ist das ein Bug oder meine Implementierung?

   

Anscheinend gibt es in Ihrer Objekthierarchie Zirkelverweise, die vom JSON-Serializer nicht unterstützt werden. Brauchen Sie alle Spalten? Sie können nur die Eigenschaften auswählen, die Sie in der Ansicht benötigen:

 return Json(new { PropertyINeed1 = data.PropertyINeed1, PropertyINeed2 = data.PropertyINeed2 }); 

Dadurch wird Ihr JSON-Objekt leichter und verständlicher. Wenn Sie viele Eigenschaften haben, kann AutoMapper verwendet werden, um automatisch zwischen DTO-Objekten und View-Objekten zu mappen .

Ich hatte das gleiche Problem und triggerse using Newtonsoft.Json;

 var list = JsonConvert.SerializeObject(model, Formatting.None, new JsonSerializerSettings() { ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore }); return Content(list, "application/json"); 

Dies geschieht tatsächlich, weil die komplexen Objekte das resultierende json-Objekt zum Scheitern bringen. Und es schlägt fehl, weil wenn das Objekt gemappt wird, es die Kinder abbildet, die ihre Eltern abbilden, wodurch ein zirkulärer Bezug entsteht. Json würde unendlich lange brauchen, um es zu serialisieren, so dass es das Problem mit der Ausnahme verhindert.

Entity Framework Mapping erzeugt ebenfalls das gleiche Verhalten, und die Lösung besteht darin, alle unerwünschten Eigenschaften zu vercasting.

Erklärt man nur die endgültige Antwort, wäre der ganze Code:

 public JsonResult getJson() { DataContext db = new DataContext (); return this.Json( new { Result = (from obj in db.Things select new {Id = obj.Id, Name = obj.Name}) } , JsonRequestBehavior.AllowGet ); } 

Es könnte auch Folgendes sein, falls Sie die Objekte nicht in einer Result Eigenschaft haben wollen:

 public JsonResult getJson() { DataContext db = new DataContext (); return this.Json( (from obj in db.Things select new {Id = obj.Id, Name = obj.Name}) , JsonRequestBehavior.AllowGet ); } 

Zusammenfassend gibt es 3 Lösungen:

  private DBEntities db = new DBEntities();//dbcontext //Solution 1: turn off ProxyCreation for the DBContext and restore it in the end public ActionResult Index() { bool proxyCreation = db.Configuration.ProxyCreationEnabled; try { //set ProxyCreation to false db.Configuration.ProxyCreationEnabled = false; var data = db.Products.ToList(); return Json(data, JsonRequestBehavior.AllowGet); } catch (Exception ex) { Response.StatusCode = (int)HttpStatusCode.BadRequest; return Json(ex.Message); } finally { //restore ProxyCreation to its original state db.Configuration.ProxyCreationEnabled = proxyCreation; } } //Solution 2: Using JsonConvert by Setting ReferenceLoopHandling to ignore on the serializer settings. //using using Newtonsoft.Json; public ActionResult Index() { try { var data = db.Products.ToList(); JsonSerializerSettings jss = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; var result = JsonConvert.SerializeObject(data, Formatting.Indented, jss); return Json(result, JsonRequestBehavior.AllowGet); } catch (Exception ex) { Response.StatusCode = (int)HttpStatusCode.BadRequest; return Json(ex.Message); } } //Solution 3: return a new dynamic object which includes only the needed properties. public ActionResult Index() { try { var data = db.Products.Select(p => new { Product_ID = p.Product_ID, Product_Name = p.Product_Name, Product_Price = p.Product_Price }).ToList(); return Json(data, JsonRequestBehavior.AllowGet); } catch (Exception ex) { Response.StatusCode = (int)HttpStatusCode.BadRequest; return Json(ex.Message); } } 

JSON ist wie XML und verschiedene andere Formate ein Baum-basiertes Serialisierungsformat. Es wird dich nicht lieben, wenn du kreisförmige Referenzen in deinen Objekten hast, wie der “Baum” sein würde:

 root B => child A => parent B => child A => parent B => ... 

Es gibt oft Möglichkeiten, die Navigation auf einem bestimmten Pfad zu deaktivieren. Mit XmlSerializer Sie beispielsweise die übergeordnete Eigenschaft als XmlIgnore . Ich weiß nicht, ob dies mit dem fraglichen JSON-Serializer möglich ist, und auch nicht, ob DatabaseColumn über geeignete Marker verfügt ( sehr unwahrscheinlich, da es auf jede Serialisierungs-API verweisen müsste).

Aufgrund der neuen DbContext T4-Vorlage, die zum Generieren der EntityFramework-Entitäten verwendet wird. Um das Änderungs-Tracking durchführen zu können, verwendet diese Vorlage das Proxy-Muster, indem Sie Ihre schönen POCOs mit ihnen verbinden. Dies verursacht dann die Probleme beim Serialisieren mit dem JavaScriptSerializer.

Also dann sind die 2 Lösungen:

  1. Entweder serialisieren Sie einfach und geben die Eigenschaften zurück, die Sie auf dem Client benötigen
  2. Sie können die automatische Generierung von Proxies ausschalten, indem Sie sie auf die Kontextkonfiguration setzen

    context.Configuration.ProxyCreationEnabled = false;

Sehr gut im folgenden Artikel erklärt.

http://juristr.com/blog/2011/08/javascriptserializer-circular-reference/

Verwenden Sie Newtonsoft.Json: Fügen Sie in Ihrer Global.asax Application_Start-Methode diese Zeile hinzu:

 GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; 

[JsonIgnore] Sie den Eigenschaften von virtuals in Ihrem Modell [JsonIgnore] hinzu.

Vermeiden Sie es, das Tabellenobjekt direkt zu konvertieren. Wenn Beziehungen zwischen anderen Tabellen festgelegt sind, kann dieser Fehler ausgetriggers werden. Sie können stattdessen eine Modellklasse erstellen, dem classnobjekt Werte zuweisen und es anschließend serialisieren.

Die bereitgestellten Antworten sind gut, aber ich denke, sie können durch Hinzufügen einer “architektonischen” Perspektive verbessert werden.

Ermittlung

MVC's Controller.Json function erfüllt diese Aufgabe, ist aber in diesem Fall sehr schlecht darin, einen relevanten Fehler zu liefern. Bei Verwendung von Newtonsoft.Json.JsonConvert.SerializeObject gibt der Fehler genau an, was die Eigenschaft ist, die den Newtonsoft.Json.JsonConvert.SerializeObject austriggers. Dies ist besonders nützlich, wenn komplexere Objekthierarchien serialisiert werden.

Richtige Architektur

Man sollte niemals versuchen, Datenmodelle (z. B. EF-Modelle) zu serialisieren, da die Navigationseigenschaften von ORM bei der Serialisierung den Weg zur Verderbnis darstellen. Datenfluss sollte der folgende sein:

 Database -> data models -> service models -> JSON string 

Servicemodelle können aus Datenmodellen mit Auto-Mappern (z. B. Automapper ) abgerufen werden. Dies garantiert zwar nicht den Mangel an zirkulären Referenzen, aber das richtige Design sollte es tun: Servicemodelle sollten genau das enthalten, was der Servicekonsument benötigt (dh die Eigenschaften).

In den seltenen Fällen, in denen der Client eine Hierarchie mit demselben Objekttyp auf verschiedenen Ebenen anfordert, kann der Service eine lineare Struktur mit der Beziehung parent-> child erstellen (nur mit Bezeichnern, nicht mit Referenzen).

Moderne Anwendungen neigen dazu, das Laden komplexer Datenstrukturen auf einmal zu vermeiden, und Servicemodelle sollten schlank sein. Z.B:

  1. Auf ein Ereignis zugreifen – nur Kopfdaten (Kennung, Name, Datum usw.) werden geladen -> Servicemodell (JSON), das nur Kopfdaten enthält
  2. Liste der verwalteten Teilnehmer – Öffnen Sie ein Popup und laden Sie die Liste -> Servicemodell (JSON), die nur die Liste der Teilnehmer enthält

Ich benutze das Update, weil Knockout in MVC5-Ansichten verwendet wird.

Auf Aktion

 return Json(ModelHelper.GetJsonModel(viewModel)); 

function

  public static TEntity GetJsonModel(TEntity Entity) where TEntity : class { TEntity Entity_ = Activator.CreateInstance(typeof(TEntity)) as TEntity; foreach (var item in Entity.GetType().GetProperties()) { if (item.PropertyType.ToString().IndexOf("Generic.ICollection") == -1 && item.PropertyType.ToString().IndexOf("SaymenCore.DAL.") == -1) item.SetValue(Entity_, Entity.GetPropValue(item.Name)); } return Entity_; } 

Sie können die Eigenschaften feststellen, die den Zirkelbezug verursachen. Dann können Sie etwas tun wie:

 private Object DeCircular(Object object) { // Set properties that cause the circular reference to null return object } 
 //first: Create a class as your view model public class EventViewModel { public int Id{get;set} public string Property1{get;set;} public string Property2{get;set;} } //then from your method [HttpGet] public async Task GetEvent() { var events = await db.Event.Find(x => x.ID != 0); List model = events.Select(event => new EventViewModel(){ Id = event.Id, Property1 = event.Property1, Property1 = event.Property2 }).ToList(); return Json(new{ data = model }, JsonRequestBehavior.AllowGet); } 

Eine einfachere Alternative zur Lösung dieses Problems ist die Rückgabe eines Strings und die Formatierung dieses Strings mit JavaScriptSerializer nach json.

 public string GetEntityInJson() { JavaScriptSerializer j = new JavaScriptSerializer(); var entityList = dataContext.Entitites.Select(x => new { ID = x.ID, AnotherAttribute = x.AnotherAttribute }); return j.Serialize(entityList ); } 

Wichtig ist der “Select” -Teil, der die gewünschten Eigenschaften in Ihrer Ansicht auswählt. Einige Objekte haben eine Referenz für das Elternobjekt. Wenn Sie die Attribute nicht auswählen, wird möglicherweise der Zirkelverweis angezeigt, wenn Sie nur die Tabellen als Ganzes verwenden.

Mach das nicht:

 public string GetEntityInJson() { JavaScriptSerializer j = new JavaScriptSerializer(); var entityList = dataContext.Entitites.toList(); return j.Serialize(entityList ); } 

Tun Sie dies stattdessen, wenn Sie nicht die ganze Tabelle wollen:

 public string GetEntityInJson() { JavaScriptSerializer j = new JavaScriptSerializer(); var entityList = dataContext.Entitites.Select(x => new { ID = x.ID, AnotherAttribute = x.AnotherAttribute }); return j.Serialize(entityList ); } 

Dadurch wird eine Ansicht mit weniger Daten gerendert, nur mit den Attributen, die Sie benötigen, und Ihr Web wird schneller ausgeführt.