Riferimento circolare rilevato eccezione durante la serializzazione dell’object su JSON

Proprio come menzionato in questo post, ricevo un errore di serializzazione Json durante la serializzazione di un Entity Framework Proxy:

È stato rilevato un riferimento circolare durante la serializzazione di un object di tipo ‘System.Data.Entity.DynamicProxies.PurchaseOrder_446B939192F161CDBC740067F174F7A6059B0F9C0EEE68CD3EBBD63CF9AF5BD0’.

Ma la differenza è che non ho un riferimento circolare nelle mie entity framework e si verifica solo nel nostro ambiente di produzione. A livello locale tutto funziona bene …

Le mie quadro:

public interface IEntity { Guid UniqueId { get; } int Id { get; } } public class Entity : IEntity { public int Id { get; set; } public Guid UniqueId { get; set; } } public class PurchaseOrder : Entity { public string Username { get; set; } public string Company { get; set; } public string SupplierId { get; set; } public string SupplierName { get; set; } public virtual ICollection Lines { get; set; } } public class PurchaseOrderLine : Entity { public string Code { get; set; } public string Name { get; set; } public decimal Quantity { get; set; } } 

L’azione GetCurrent sul mio PurchaseOrderController che genera l’eccezione:

 public class PurchaseOrderController : Controller { private readonly IUnitOfWork _unitOfWork; public PurchaseOrderController(IUnitOfWork unitOfWork) { _unitOfWork = unitOfWork; } public JsonResult GetCurrent() { return Json(EnsurePurchaseOrder(), JsonRequestBehavior.AllowGet); } private PurchaseOrder EnsurePurchaseOrder() { var company = RouteData.GetRequiredString("company"); var repository = _unitOfWork.GetRepository(); var purchaseOrder = repository .Include(p => p.Lines) .FirstOrDefault ( p => p.Company == company && p.Username == User.Identity.Name ); if (purchaseOrder == null) { purchaseOrder = repository.Create(); purchaseOrder.UniqueId = Guid.NewGuid(); purchaseOrder.Company = company; purchaseOrder.Username = User.Identity.Name; _unitOfWork.SaveChanges(); } return purchaseOrder; } } 

Le tue quadro POCO sono perfettamente serializzabili. Il tuo problema è che i proxy dinamici creati dal runtime EF in genere non lo sono. È ansible impostare context.Configuration.ProxyCreationEnabled su false ma in context.Configuration.ProxyCreationEnabled si perde il caricamento lento. La mia raccomandazione forte è di usare Json.NET che supporta la serializzazione per le entity framework EF:

Supporto per Entity Framework di ADO.NET aggiunto accidentalmente a Json.NET

Popolare framework JSON ad alte prestazioni per .NET

Opzione 1 (raccomandata)

Prova a distriggersre la creazione dell’object Proxy sul tuo DbContext .

 DbContext.Configuration.ProxyCreationEnabled = false; 

In genere questo scenario è dovuto al fatto che l’applicazione utilizza oggetti POCO (sia T4 generati che Code-First). Il problema sorge quando Entity Framework vuole tenere traccia delle modifiche nell’object che non è incorporato negli oggetti POCO. Per risolvere questo problema, EF crea oggetti proxy privi degli attributi negli oggetti POCO e non serializzabili.

I motivi per cui raccomando questo approccio; l’utilizzo di un sito Web significa che probabilmente non è necessario modificare il rilevamento (stato) sugli oggetti di Entity Framework, liberare memoria e CPU perché il rilevamento delle modifiche è disabilitato e funzionerà in modo coerente su tutti gli oggetti allo stesso modo.

opzione 2

Utilizzare un serializzatore (come JSON.Net che è già incluso in ASP.Net 4) che consente la personalizzazione per serializzare gli oggetti.

Le ragioni per cui non raccomando questo approccio sono che alla fine la logica di serializzazione degli oggetti personalizzati sarà necessaria per gli oggetti proxy seriali come altri tipi di oggetti. Ciò significa che hai una dipendenza dalla logica per fornire un risultato a valle. Cambiare l’object significa cambiare la logica, e in un progetto ASP.Net MVC (qualsiasi versione) invece di cambiare solo una vista, c’è qualcos’altro da cambiare che non è facilmente noto al di fuori di chi ha scritto prima la logica.

Opzione 3 (Entity Framework 5.x +)

Utilizzare .AsNoTracking () che disabiliterà gli oggetti proxy nella query specifica. Se è necessario utilizzare il rilevamento delle modifiche, ciò consente di ottenere una soluzione intermedia valida per la soluzione n. 1.

Ho passato innumerevoli ore a provare tutte le varie soluzioni che ho trovato sparse sul web, tra cui:

  • [JsonIgnore]
  • Getter interni
  • Disabilitazione di LazyLoadingEnabled e ProxyCreationEnabled
  • Impostazione di ReferenceLoopHandling su “ignora”
  • Usare con caucanvas caricamento esplicito dove necessario

Tutto ciò alla fine è risultato infruttuoso per me. Ignorare una proprietà ha aiutato una query, ma ha ferito altre 3 persone. Sembrava la programmazione equivalente a whack-a-mole.

Il contesto del mio problema era che i dati in uscita dalla mia applicazione dovevano essere JSON. Assolutamente no. Inserti e aggiornamenti rappresentano ovviamente molto meno problemi. Ma selezionare i dati archiviati in un database normalizzato (e nel mio caso includendo una cronologia delle versioni) da serializzare è un incubo.

La soluzione:

Restituisci i dati (proprietà) di cui hai bisogno come oggetti anonimi.

Un esempio di codice:

In questo caso avevo bisogno dei 3 biglietti più recenti, basati su “Date Scheduled”. Ma aveva anche bisogno di diverse proprietà memorizzate in quadro correlate.

 var tickets = context.TicketDetails .Where(t => t.DateScheduled >= DateTime.Now) .OrderBy(t => t.DateScheduled) .Take(3) .Include(t => t.Ticket) .Include(t => t.Ticket.Feature) .Include(t => t.Ticket.Feature.Property) .AsEnumerable() .Select( t => new { ID = t.Ticket.ID, Address = t.Ticket.Feature.Property.Address, Subject = t.Ticket.Subject, DateScheduled = String.Format("{0:MMMM dd, yyyy}", t.DateScheduled) } ); 

E voilà, nessun loop autoreferenziale.

Mi rendo conto che questa situazione potrebbe non essere adeguata in tutti i casi, dato che quadro e oggetti possono cambiare. Ma vale sicuramente la pena di prenderlo in considerazione se tutto il resto fallisce.

Qualunque class abbia il riferimento di un’altra class, basta aggiungere un attributo come questo

 [Newtonsoft.Json.JsonIgnoreAttribute] public virtual ICollection Lines { get; set; } 

Ora tutto funziona liscio

Nella tua class DbContext , aggiungi questa riga di codice:

 this.Configuration.ProxyCreationEnabled = false; 

Per esempio:

 public partial class EmpDBEntities : DbContext { public EmpDBEntities() : base("name=EmpDBEntities") { this.Configuration.ProxyCreationEnabled = false; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { throw new UnintentionalCodeFirstException(); } public virtual DbSet Departments { get; set; } public virtual DbSet Employees { get; set; } } 

Il riferimento circolare si verifica perché si utilizza il caricamento ansioso sull’object.

Hai 3 metodi:

  • Distriggers il carico di caricamento quando carichi la tua Query (linq o lambda) DbContext.Configuration.ProxyCreationEnabled = false;
  • Scollega gli oggetti (= nessuna funzionalità di caricamento avida e nessun proxy)
    • Repository.Detach (entityObject)
    • DbContext.Entry (entityObject) .EntityState = EntityState.Detached
  • Clona le proprietà
    • È ansible utilizzare qualcosa come AutoMapper per clonare l’object, non utilizzare l’interfaccia ICloneable, perché clona anche ProxyProperties nell’object, in modo che non funzioni.
  • Se stai costruendo un’API, prova a utilizzare un progetto separte con una configurazione diversa (che non restituisce proxy)

PS. Proxy è l’object creato da EF quando lo si carica da Entity Framework. In breve: significa che contiene i valori originali e i valori aggiornati in modo che possano essere aggiornati in seguito. Gestisce altre cose a 😉

Ho avuto lo stesso errore, tuttavia l’ho visto sia sul server di produzione che localmente. Cambiare la configurazione di DbContext non ha risolto il mio problema. Una soluzione diversa mi è stata presentata con il

 [IgnoreDataMember] 

attributo su riferimenti di quadro DB. Vedi il post qui se questo sembra più pertinente al tuo problema.

Errore JSON serializzato API Web ASP.NET: “loop Self Referencing”

Ho avuto lo stesso problema e l’ho risolto deselezionando Json.NET nel progetto Estensioni nel Reference Manager.

(vedi l’immagine http://sofit.miximages.com/serialization/RqbXZ.png )

Ho anche dovuto modificare il file project.csproj per mappare il percorso corretto per la nuova versione:

  ..\packages\Newtonsoft.Json.6.0.5\lib\net45\Newtonsoft.Json.dll  

e doveva ancora configurare web.config

      

Si noti che nel file web.config sono stato costretto a fare riferimento alla versione OLDER (6.0.0.0) sebbene la versione installata fosse 6.0.5.

Spero che sia d’aiuto!

Stavo avendo lo stesso problema, quello che ho fatto è passato solo la colonna necessaria per visualizzare, Nel mio caso. solo 2.

  List lstSubCategory = GetSubCateroy() // list from repo var subCategoryToReturn = lstSubCategory.Select(S => new { Id = S.Id, Name = S.Name }); return this.Json(subCategoryToReturn , JsonRequestBehavior.AllowGet);