Une référence circulaire a été détectée lors de la sérialisation d'un objet de type 'subsonique.Schéma.DatabaseColumn'.
J'essaie de faire un simple retour JSON mais j'ai des problèmes j'ai ce qui suit ci-dessous.
public JsonResult GetEventData()
{
var data = Event.Find(x => x.ID != 0);
return Json(data);
}
Je reçois un HTTP 500 à l'exception comme indiqué dans le titre de cette question. J'ai aussi essayé
var data = Event.All().ToList()
Cela a donné le même problème.
Est-ce un bug ou mon implémentation?
14 réponses
Il semble qu'il existe des références circulaires dans votre hiérarchie d'objets qui ne sont pas prises en charge par le sérialiseur JSON. Avez-vous besoin de toutes les colonnes? Vous pouvez choisir uniquement les propriétés dont vous avez besoin dans la vue:
return Json(new
{
PropertyINeed1 = data.PropertyINeed1,
PropertyINeed2 = data.PropertyINeed2
});
Cela rendra votre objet JSON plus léger et plus facile à comprendre. Si vous avez de nombreuses propriétés, AutoMapper peut être utilisé pour automatiquement mapper entre les objets DTO et afficher les objets.
J'ai eu le même problème et résolu par using Newtonsoft.Json;
var list = JsonConvert.SerializeObject(model,
Formatting.None,
new JsonSerializerSettings() {
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
});
return Content(list, "application/json");
Cela se produit en fait parce que les objets complexes sont ce qui fait que l'objet JSON résultant échoue. Et cela échoue car lorsque l'objet est mappé, il mappe les enfants, ce qui mappe leurs parents,en faisant une référence circulaire. Json prendrait un temps infini pour le sérialiser, donc il empêche le problème avec l'exception.
Le mappage Entity Framework produit également le même comportement, et la solution consiste à ignorer toutes les propriétés indésirables.
Juste expliciter la finale réponse, le code entier serait:
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
);
}
Cela pourrait aussi être le suivant dans le cas où vous ne voulez pas les objets dans une propriété Result
:
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
);
}
Pour résumer les choses, il y a 3 solutions à cela:
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, comme xml et divers autres formats, est un format de sérialisation arborescent. Il ne vous aimera pas si vous avez des références circulaires dans vos objets, comme le serait "l'arbre":
root B => child A => parent B => child A => parent B => ...
Il existe souvent des moyens de désactiver la navigation le long d'un certain chemin; par exemple, avec XmlSerializer
, vous pouvez marquer la propriété parent comme XmlIgnore
. Je ne sais pas si cela est possible avec le sérialiseur json en question, ni si DatabaseColumn
a des marqueurs appropriés ( Très peu probable, car il faudrait faire référence chaque API de sérialisation)
C'est en raison du nouveau modèle DbContext T4 utilisé pour générer les entités EntityFramework. Afin de pouvoir effectuer le suivi des modifications, Ce modèle utilise le modèle Proxy, en enveloppant vos beaux poco avec eux. Cela provoque alors les problèmes lors de la sérialisation avec JavaScriptSerializer.
Alors les 2 solutions sont:
- soit vous venez de sérialiser et de renvoyer les propriétés dont vous avez besoin sur le client
-
Vous pouvez désactiver la fonction génération de proxies en le définissant sur la configuration du contexte
Contexte.Configuration.ProxyCreationEnabled = false;
Très bien expliqué dans l'article.
Http://juristr.com/blog/2011/08/javascriptserializer-circular-reference/
Utilisation De Newtonsoft.Json: dans votre Global.méthode ASAX Application_Start ajouter cette ligne:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
Ajoutez [JsonIgnore]
aux propriétés virtuelles de votre modèle.
Évitez de convertir directement l'objet table. Si les relations sont définies entre d'autres tables, il peut lancer cette erreur. Vous pouvez plutôt créer une classe de modèle, attribuer des valeurs à l'objet de classe, puis le sérialiser.
Les réponses fournies sont bonnes, mais je pense qu'elles peuvent être améliorées en ajoutant une perspective" architecturale".
Enquête
MVC's Controller.Json
la fonction fait le travail, mais elle est très pauvre en fournissant une erreur pertinente dans ce cas. En utilisant Newtonsoft.Json.JsonConvert.SerializeObject
, l'erreur spécifie exactement quelle est la propriété qui déclenche la référence circulaire. Ceci est particulièrement utile lors de la sérialisation de hiérarchies d'objets plus complexes.
Architecture appropriée
Un ne devrait jamais essayer de sérialiser les modèles de données (par exemple les modèles EF), car les propriétés de navigation D'ORM sont la voie de la perdition en matière de sérialisation. Le flux de données doit être le suivant:
Database -> data models -> service models -> JSON string
Les Modèles de Service peuvent être obtenus à partir de modèles de données utilisant des mappeurs automatiques (par exemple Automapper). Bien que cela ne garantisse pas le manque de références circulaires, une conception appropriée devrait le faire: les modèles de service devraient contenir exactement ce que le consommateur de services exige (c'est-à-dire les propriétés).
Dans ces rares cas, lorsque le client demande une hiérarchie impliquant le même type d'objet à différents niveaux, le service peut créer une structure linéaire avec une relation parent->enfant (en utilisant uniquement des identifiants, pas des références).
Les applications modernes ont tendance à éviter de charger des structures de données complexes à la fois et les modèles de service devraient être minces. Par exemple:
- accéder à une donnée d'en - tête événement uniquement (identifiant, nom, date, etc.) est chargé - > modèle de service (JSON) contenant uniquement l'en-tête données
- liste des participants gérés-accédez à une fenêtre contextuelle et chargez la liste paresseuse - > modèle de service (JSON) contenant uniquement la liste des Participants
J'utilise le correctif, car en utilisant Knockout dans les vues MVC5.
Sur action
return Json(ModelHelper.GetJsonModel<Core_User>(viewModel));
Fonction
public static TEntity GetJsonModel<TEntity>(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_;
}
Vous pouvez remarquer les propriétés qui provoquent la référence circulaire. Ensuite, vous pouvez faire quelque chose comme:
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<ActionResult> GetEvent()
{
var events = await db.Event.Find(x => x.ID != 0);
List<EventViewModel> model = events.Select(event => new EventViewModel(){
Id = event.Id,
Property1 = event.Property1,
Property1 = event.Property2
}).ToList();
return Json(new{ data = model }, JsonRequestBehavior.AllowGet);
}
Une alternative plus facile pour résoudre ce problème est de retourner une chaîne et de formater cette chaîne en json avec JavaScriptSerializer.
public string GetEntityInJson()
{
JavaScriptSerializer j = new JavaScriptSerializer();
var entityList = dataContext.Entitites.Select(x => new { ID = x.ID, AnotherAttribute = x.AnotherAttribute });
return j.Serialize(entityList );
}
Il est important de la partie "Select", qui choisit les propriétés que vous voulez dans votre vue. Certains objets ont une référence pour le parent. Si vous ne choisissez pas les attributs, la référence circulaire peut apparaître, si vous prenez simplement les tables dans leur ensemble.
Ne faites pas ceci:
public string GetEntityInJson()
{
JavaScriptSerializer j = new JavaScriptSerializer();
var entityList = dataContext.Entitites.toList();
return j.Serialize(entityList );
}
Faites ceci à la place si vous ne voulez pas toute la table:
public string GetEntityInJson()
{
JavaScriptSerializer j = new JavaScriptSerializer();
var entityList = dataContext.Entitites.Select(x => new { ID = x.ID, AnotherAttribute = x.AnotherAttribute });
return j.Serialize(entityList );
}
Cela permet de rendre une vue avec moins de données, juste avec les attributs dont vous avez besoin,et rend votre Web plus rapide.