Détection de la boucle d’auto-évaluation de la structure d’entité

J’ai une erreur étrange. Je teste une API Web .NET 4.5, Entity Framework et MS SQL Server. J’ai déjà créé la firebase database et configuré les clés et relations principales et étrangères appropriées.

J’ai créé un modèle .edmx et importé deux tables: Employee et Department. Un département peut avoir plusieurs employés et cette relation existe. J’ai créé un nouveau contrôleur appelé EmployeeController en utilisant les options d’échafaudage pour créer un contrôleur d’API avec des actions de lecture / écriture utilisant Entity Framework. Dans l’assistant, sélectionnez Employee comme modèle et entité correcte pour le contexte de données.

La méthode créée ressemble à ceci:

public IEnumerable GetEmployees() { var employees = db.Employees.Include(e => e.Department); return employees.AsEnumerable(); } 

Lorsque j’appelle mon API via / api / Employee, j’obtiens cette erreur:

Le type ‘ObjectContent`1’ n’a pas réussi à sérialiser le corps de la réponse pour le type de contenu ‘application / json; … System.InvalidOperationException “,” StackTrace “: null,” InnerException “: {” Message “:” Une erreur est survenue. “,” ExceptionMessage “:” Boucle auto-référencée détectée avec le type “System.Data.Entity.DynamicProxies” .Employee_5D80AD978BC68A1D8BD675852F94E8B550F4CB150ADB8649E8998B7F95422552 ‘. Chemin ‘[0] .Department.Employees’. “,” ExceptionType “:” Newtonsoft.Json.JsonSerializationException “,” StackTrace “:” …

Pourquoi est-il auto-référençant [0] .Department.Employees? Cela n’a pas beaucoup de sens. Je m’attendrais à ce que cela se produise si j’avais un référencement circulaire dans ma firebase database, mais ceci est un exemple très simple. Qu’est-ce qui pourrait mal tourner?

La réponse correcte pour le formateur Json par défaut basé sur Json.net consiste à définir ReferenceLoopHandling sur Ignore .

Ajoutez simplement ceci à Application_Start dans Global.asax:

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

C’est la bonne façon. Il ignorera la référence renvoyant à l’object.

D’autres réponses ont porté sur la modification de la liste renvoyée en excluant les données ou en créant un object de façade, ce qui n’est parfois pas une option.

Utiliser l’atsortingbut JsonIgnore pour restreindre les références peut prendre du temps et si vous souhaitez sérialiser l’arborescence à partir d’un autre point, cela posera un problème.

Cela se produit car vous essayez de sérialiser directement la collection d’objects EF. Étant donné que le département a une association avec l’employé et l’employé au service, le sérialiseur JSON lira la lecture infinie d.Employee.Departments.Employee.Departments etc …

Pour corriger cela juste avant la sérialisation, créez un type anonyme avec les accessoires que vous voulez

exemple de code (pseudo):

 departments.select(dep => new { dep.Id, Employee = new { dep.Employee.Id, dep.Employee.Name } }); 

J’ai eu le même problème et j’ai constaté que vous pouviez simplement appliquer l’atsortingbut [JsonIgnore] à la propriété de navigation que vous ne voulez pas sérialiser. Il sérialisera toujours les entités parents et enfants, mais évitera simplement la boucle d’auto-référence.

Le problème principal est que la sérialisation d’un modèle d’entité qui a une relation avec un autre modèle d’entité (relation de clé étrangère). Cette relation provoque l’auto-référencement ce qui va lancer une exception lors de la sérialisation à json ou xml. Il y a beaucoup d’options. Sans sérialisation des modèles d’entités à l’aide de modèles personnalisés. Valeurs ou données provenant de données de modèle d’entité mappées à des modèles personnalisés (mappage d’object) à l’aide d’ Automapper ou de Valueinjector, renvoyer la demande et elle se sérialisera sans autre problème. Ou vous pouvez sérialiser le modèle d’entité afin de désactiver d’abord les proxys dans le modèle d’entité

 public class LabEntities : DbContext { public LabEntities() { Configuration.ProxyCreationEnabled = false; } 

Pour conserver les références aux objects en XML, vous avez deux options. L’option la plus simple consiste à append [DataContract (IsReference = true)] à votre classe de modèle. Le paramètre IsReference active les références d’object. Rappelez-vous que DataContract rend l’optimisation de la sérialisation, vous devrez donc également append des atsortingbuts DataMember aux propriétés:

 [DataContract(IsReference=true)] public partial class Employee { [DataMember] ssortingng dfsd{get;set;} [DataMember] ssortingng dfsd{get;set;} //exclude the relation without giving datamember tag List Departments{get;set;} } 

Au format Json dans global.asax

 var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.All; 

au format xml

 var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter; var dcs = new DataContractSerializer(typeof(Employee), null, int.MaxValue, false, /* preserveObjectReferences: */ true, null); xml.SetSerializer(dcs); 

L’erreur de message signifie que vous avez une boucle d’auto-référence.

Le json que vous produisez ressemble à cet exemple (avec une liste d’un employé):

 [ employee1 : { name: "name", department : { name: "departmentName", employees : [ employee1 : { name: "name", department : { name: "departmentName", employees : [ employee1 : { name: "name", department : { and again and again.... } ] } } ] } } 

]

Vous devez dire au contexte de la firebase database que vous ne voulez pas obtenir toutes les entités liées lorsque vous demandez quelque chose. L’option pour DbContext est Configuration.LazyLoadingEnabled

Le meilleur moyen que j’ai trouvé est de créer un contexte pour la sérialisation:

 public class SerializerContext : LabEntities { public SerializerContext() { this.Configuration.LazyLoadingEnabled = false; } } 

Je suis conscient que cette question est assez ancienne, mais elle est toujours populaire et je ne vois aucune solution pour ASP.net Core.

Dans le cas de ASP.net Core , vous devez append un nouveau fichier JsonOutputFormatter dans le fichier Startup.cs :

  public void ConfigureServices(IServiceCollection services) { services.AddMvc(options => { options.OutputFormatters.Clear(); options.OutputFormatters.Add(new JsonOutputFormatter(new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, }, ArrayPool.Shared)); }); //... } 

Après l’avoir implémenté, le sérialiseur JSON ignorera simplement les références de boucle. Cela signifie que: il retournera null au lieu de charger à l’infini des objects se référant l’un à l’autre.

Sans solution ci-dessus utilisant:

 var employees = db.Employees.ToList(); 

Chargerait les Employees et leur serait lié.

Après avoir défini ReferenceLoopHandling sur Ignore , les Departments seront définis sur null sauf si vous les incluez dans votre requête:

 var employees = db.Employees.Include(e => e.Department); 

De plus, gardez à l’esprit que cela effacera tous les fichiers OutputFormatters , si vous ne voulez pas que vous puissiez essayer de supprimer cette ligne:

 options.OutputFormatters.Clear(); 

Mais la supprimer provoque à nouveau une self referencing loop dans mon cas pour une raison quelconque.

Ajouter une ligne Configuration.ProxyCreationEnabled = false; dans le constructeur de votre modèle contextuel, définition de classe partielle.

  public partial class YourDbContextModelName : DbContext { public YourDbContextModelName() : base("name=YourDbContextConn_SsortingngName") { Configuration.ProxyCreationEnabled = false;//this is line to be added } public virtual DbSet Employees{ get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { } } 

Je n’avais qu’un seul modèle que je voulais utiliser, alors j’ai fini avec le code suivant:

 var JsonImageModel = Newtonsoft.Json.JsonConvert.SerializeObject(Images, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }); 

Je pourrais également envisager d’append des échantillons explicites pour chaque contrôleur / action, comme indiqué ici:

http://blogs.msdn.com/b/yaohuang1/archive/2012/10/13/asp-net-web-api-help-page-part-2-providing-custom-samples-on-the-help- page.aspx

c’est-à-dire config.SetActualResponseType (typeof (SomeType), “Values”, “Get”);

auto-référencement comme exemple

================================================== ===========

 public class Employee { public int Id { get; set; } public ssortingng FirstName { get; set; } public ssortingng LastName { get; set; } public ssortingng Email { get; set; } public int ManagerId { get; set; } public virtual Employee Manager { get; set; } public virtual ICollection Employees { get; set; } public Employee() { Employees = new HashSet(); } } 

================================================== ===========

  HasMany(e => e.Employees) .WithRequired(e => e.Manager) .HasForeignKey(e => e.ManagerId) .WillCascadeOnDelete(false);