EF 4.1 – Code First – Erreur de sérialisation de référence circulaire JSON

Je reçois une erreur de sérialisation de référence circulaire bien que, à ma connaissance, je n’ai aucune référence circulaire. Je récupère un ensemble de commandes de la firebase database et les envoie au client en tant que JSON. Tout le code est montré ci-dessous.

C’est l’erreur:

Erreur

Une référence circulaire a été détectée lors de la sérialisation d’un object de type ‘System.Data.Entity.DynamicProxies.Order_83CECF2AA4DE38232F9077D4B26941AB96BC61230419EA8AC42C9100E6072812’. Description: une exception non gérée s’est produite lors de l’exécution de la demande Web en cours. Consultez la trace de la stack pour plus d’informations sur l’erreur et son origine dans le code.

Détails de l’exception: System.InvalidOperationException: une référence circulaire a été détectée lors de la sérialisation d’un object de type ‘System.Data.Entity.DynamicProxies.Order_83CECF2AA4DE38232F9077D4B26941AB96BC61230419EA8AC42C9100E6072812’.

Erreur source:

Une exception non gérée a été générée lors de l’exécution de la demande Web en cours. Les informations concernant l’origine et l’emplacement de l’exception peuvent être identifiées à l’aide de la trace de stack d’exception ci-dessous.

Mes cours sont les suivants:

Commande

public class Order { [Key] public int OrderId { get; set; } public int PatientId { get; set; } public virtual Patient Patient { get; set; } public int CertificationPeriodId { get; set; } public virtual CertificationPeriod CertificationPeriod { get; set; } public int AgencyId { get; set; } public virtual Agency Agency { get; set; } public int PrimaryDiagnosisId { get; set; } public virtual Diagnosis PrimaryDiagnosis { get; set; } public int ApprovalStatusId { get; set; } public virtual OrderApprovalStatus ApprovalStatus { get; set; } public int ApproverId { get; set; } public virtual User Approver { get; set; } public int SubmitterId { get; set; } public virtual User Submitter { get; set; } public DateTime ApprovalDate { get; set; } public DateTime SubmittedDate { get; set; } public Boolean IsDeprecated { get; set; } } 

Patient

 public class Patient { [Key] public int PatientId { get; set; } public ssortingng FirstName { get; set; } public ssortingng LastName { get; set; } public ssortingng MiddleInitial { get; set; } public bool IsMale; public DateTime DateOfBirth { get; set; } public int PatientAddressId { get; set; } public Address PatientAddress { get; set; } public bool IsDeprecated { get; set; } } 

Période de certificateion

 public class CertificationPeriod { [Key] public int CertificationPeriodId { get; set; } public DateTime startDate { get; set; } public DateTime endDate { get; set; } public bool isDeprecated { get; set; } } 

Agence

 public class Agency { [Key] public int AgencyId { get; set; } public ssortingng Name { get; set; } public int PatientAddressId { get; set; } public virtual Address Address { get; set; } } 

Diagnostic

 public class Diagnosis { [Key] public int DiagnosisId { get; set; } public ssortingng Icd9Code { get; set; } public ssortingng Description { get; set; } public DateTime DateOfDiagnosis { get; set; } public ssortingng Onset { get; set; } public ssortingng Details { get; set; } } 

OrderApprovalStatus

 public class OrderApprovalStatus { [Key] public int OrderApprovalStatusId { get; set; } public ssortingng Status { get; set; } } 

Utilisateur

 public class User { [Key] public int UserId { get; set; } public ssortingng Login { get; set; } public ssortingng FirstName { get; set; } public ssortingng LastName { get; set; } public ssortingng NPI { get; set; } public ssortingng Email { get; set; } } 

NOTE: LA CLASSE D’ADRESSE EST UNE NOUVELLE ADDITION DURANT L’EDIT

Adresse

 public class Address { [Key] public int AddressId { get; set; } public ssortingng StreetAddress { get; set; } public ssortingng City { get; set; } public ssortingng State { get; set; } public ssortingng Zip { get; set; } public ssortingng Phone { get; set; } public ssortingng Title { get; set; } public ssortingng Label { get; set; } } 

Le code qui exécute la sérialisation est ici:

Extrait de OrderController

  public ActionResult GetAll() { return Json(ppEFContext.Orders, JsonRequestBehavior.AllowGet); } 

Merci

    Vous pouvez essayer de supprimer le mot-clé virtual de toutes les propriétés de navigation pour désactiver le chargement différé et la création de proxy, puis utiliser le chargement rapide pour charger explicitement le graphe d’object requirejs:

     public ActionResult GetAll() { return Json(ppEFContext.Orders .Include(o => o.Patient) .Include(o => o.Patient.PatientAddress) .Include(o => o.CertificationPeriod) .Include(o => o.Agency) .Include(o => o.Agency.Address) .Include(o => o.PrimaryDiagnosis) .Include(o => o.ApprovalStatus) .Include(o => o.Approver) .Include(o => o.Submitter), JsonRequestBehavior.AllowGet); } 

    En vous référant à votre précédent article, il semblerait que votre application ne repose de toute façon pas sur un chargement paresseux, car vous y avez introduit les propriétés virtuelles pour charger le graphique d’object paresseusement, ce qui peut causer des problèmes de sérialisation.

    modifier

    Il n’est pas nécessaire de supprimer le mot-clé virtual des propriétés de navigation (ce qui rendrait le chargement paresseux complètement impossible pour le modèle). Il suffit de désactiver la création de proxy (qui désactive également le chargement différé) pour les circonstances spécifiques où les proxies sont perturbants, comme la sérialisation:

     ppEFContext.Configuration.ProxyCreationEnabled = false; 

    Cela désactive la création de proxy uniquement pour l’instance de contexte spécifique ppEFContext .

    (Je viens de le voir, @WillC en a déjà parlé ici. Merci pour cette modification.)

    Lorsque vous savez que vous devez effectuer une sérialisation à partir d’un contexte particulier, vous pouvez désactiver la création de proxy pour cette requête particulière, comme ci-dessous. Cela a fonctionné pour moi et était meilleur que la révision de mes classes de modèle.

     using (var context = new MeContext()) { context.Configuration.ProxyCreationEnabled = false; return context.cars.Where(w => w.Brand == "Ferrari") } 

    Cette approche supprime le type d’object proxy pour cette instance particulière du contexte afin que les objects renvoyés soient la classe réelle et que la sérialisation ne pose donc pas de problème.

    c’est à dire:

     {Models.car} 

    au lieu de

     {System.Data.Entity.DynamicProxies.car_231710A36F27E54BC6CE99BB50E0FE3B6BD4462EC‌​A19695CD1BABB79605296EB} 

    Le problème est que vous sérialisez réellement un object proxy généré par une structure d’entité. Malheureusement, cela a quelques problèmes lorsqu’il est utilisé avec le sérialiseur JSON. Vous pouvez envisager de mapper vos entités à des classes POCO simples et spéciales pour des raisons de compatibilité JSON.

    Il y a un atsortingbut à append aux objects Entity Framework

     [ScriptIgnore] 

    Cela rend le code non effectuer des références circulaires.

    Je pense qu’ils ont corrigé cela dans la dernière version.

    Consultez les documents d’aide dans la section ” Sérialisation et désérialisation de JSON -> Sérialisation et conservation des références d’objects “.

    Définissez ce paramètre lors de l’initialisation du sérialiseur JSON.Net:

     PreserveReferencesHandling = PreserveReferencesHandling.Objects; 

    Un exemple serait donc ceci:

     var serializerSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects }; ssortingng json = JsonConvert.SerializeObject(people, Formatting.Indented, serializerSettings); 

    J’ai vérifié que cela fonctionne avec ma première solution de code et une référence circulaire dans les propriétés de navigation. Si vous regardez le JSON résultant, il devrait avoir les propriétés “$ id” et “$ ref” partout.

    Une solution alternative consisterait à utiliser des types anonymes à la suite d’une requête LINQ.

    Dans mon projet, j’utilise beaucoup le chargement paresseux, et la désactivation n’était pas la bonne chose à faire.

    Une solution alternative, si seulement certaines valeurs des objects sont nécessaires, est de construire une classe anonyme et de la renvoyer, comme dans l’exemple ci-dessous:

     public JsonResult AjaxFindByName(ssortingng term) { var customers = context.Customers .Where(c => c.Name.ToUpper().Contains(term.ToUpper())).Take(10) .AsEnumerable() .Select(c => new { value = c.Name, SSN = Ssortingng.Format(@"{0:000\-00\-0000}", c.SSN), CustomerID = c.CustomerID }); return Json(customers, JsonRequestBehavior.AllowGet); } 

    La référence circulaire se produit car vous utilisez un chargement rapide sur l’object.

    Vous avez deux méthodes:

    • Désactiver le chargement rapide lorsque vous chargez votre requête (linq ou lambda) DbContext.Configuration.ProxyCreationEnabled = false;
    • Supprimer le mot-clé virtuel du modèle de domaine
    • Détachez les objects (= pas de fonctionnalité de chargement désirée et pas de proxy)
      • Repository.Detach (entityObject)
      • DbContext.Entry (entityObject) .EntityState = EntityState.Detached
    • Cloner les propriétés
      • Vous pouvez utiliser quelque chose comme AutoMapper pour cloner l’object, n’utilisez pas l’interface ICloneable, car il clone également les propriétés ProxyProperties dans l’object, donc cela ne fonctionnera pas.
    • Si vous créez une API, essayez d’utiliser un projet distinct avec une configuration différente (qui ne renvoie pas de proxy)

    PS Proxies est l’object créé par EF lorsque vous le chargez depuis Entity Framework. En bref: Cela signifie qu’il contient les valeurs d’origine et les valeurs mises à jour afin qu’elles puissent être mises à jour ultérieurement. Il gère d’autres choses à 😉

    Pour ceux qui utilisent les classes proxy EF / Linq2SQL, ma solution consistait simplement à supprimer la référence parent sur mes entités enfants.

    Donc, dans mon modèle, j’ai sélectionné la relation et changé la référence parent pour qu’elle soit interne plutôt que publique.

    Peut ne pas être une solution idéale pour tous, mais a travaillé pour moi.

    Vous pouvez supprimer le mot-clé virtual :

    public virtual Patient Patient { get; set; } public virtual Patient Patient { get; set; } -> public Patient Patient { get; set; } public Patient Patient { get; set; }

    Gardez à l’esprit que lorsque vous supprimez le mot-clé virtuel, le chargement différé sera désactivé.