Uma referência circular foi detectada durante a serialização de um object do tipo ‘SubSonic.Schema .DatabaseColumn’.

Estou tentando fazer um retorno JSON simples, mas estou tendo problemas que tenho abaixo.

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

Eu recebo um HTTP 500 com a exceção mostrada no título desta pergunta. Eu também tentei

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

Isso deu o mesmo problema.

Isso é um bug ou minha implementação?

Parece que existem referências circulares na sua hierarquia de objects que não são suportadas pelo serializador JSON. Você precisa de todas as colunas? Você pode pegar apenas as propriedades necessárias na visualização:

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

Isso tornará seu object JSON mais leve e mais fácil de entender. Se você tiver muitas propriedades, o AutoMapper poderá ser usado para mapear automaticamente entre objects DTO e objects View.

Eu tive o mesmo problema e resolvi using Newtonsoft.Json;

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

Isso realmente acontece porque os objects complexos são o que faz o object json resultante falhar. E falha porque, quando o object é mapeado, mapeia os filhos, que mapeiam seus pais, fazendo uma referência circular para ocorrer. Json levaria tempo infinito para serializá-lo, então evita o problema com a exceção.

O mapeamento do Entity Framework também produz o mesmo comportamento e a solução é descartar todas as propriedades indesejadas.

Apenas explicando a resposta final, todo o código seria:

 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 ); } 

Também pode ser o seguinte caso você não queira os objects dentro de uma propriedade 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 ); } 

Para resumir, existem 3 soluções para isso:

  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, como xml e vários outros formatos, é um formato de serialização baseado em tree. Ele não vai te amar se você tiver referências circulares em seus objects, como a “tree” seria:

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

Existem muitas maneiras de desativar a navegação ao longo de um determinado caminho; por exemplo, com XmlSerializer você pode marcar a propriedade pai como XmlIgnore . Não sei se isso é possível com o serializador json em questão, nem se o DatabaseColumn possui marcadores adequados ( muito improvável, já que seria necessário fazer referência a cada API de serialização)

É devido ao novo modelo DbContext T4 usado para gerar as entidades EntityFramework. Para poder realizar o acompanhamento de alterações, esses modelos usam o padrão Proxy, envolvendo seus POCOs agradáveis. Isso, então, causa os problemas durante a serialização com o JavaScriptSerializer.

Então as 2 soluções são:

  1. Você apenas serializa e retorna as propriedades necessárias no cliente
  2. Você pode desativar a geração automática de proxies definindo-a na configuração do contexto

    context.Configuration.ProxyCreationEnabled = false;

Muito bem explicado no artigo abaixo.

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

Usando Newtonsoft.Json: No seu método Global.asax Application_Start, adicione esta linha:

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

adicione [JsonIgnore] às propriedades [JsonIgnore] em seu modelo.

Evite converter diretamente o object da tabela. Se as relações forem definidas entre outras tabelas, isso poderá causar esse erro. Em vez disso, você pode criar uma class de modelo, atribuir valores ao object de class e, em seguida, serializá-lo.

As respostas fornecidas são boas, mas acho que podem ser melhoradas adicionando uma perspectiva “arquitetônica”.

Investigação

MVC's Controller.Json function MVC's Controller.Json está fazendo o trabalho, mas é muito ruim fornecer um erro relevante nesse caso. Usando Newtonsoft.Json.JsonConvert.SerializeObject , o erro especifica exatamente o que é a propriedade que está triggersndo a referência circular. Isso é particularmente útil ao serializar hierarquias de objects mais complexas.

Arquitetura apropriada

Nunca se deve tentar serializar modelos de dados (por exemplo, modelos EF), já que as propriedades de navegação do ORM são o caminho para a perdição quando se trata de serialização. O stream de dados deve ser o seguinte:

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

Os modelos de serviço podem ser obtidos a partir de modelos de dados usando mapeadores automáticos (por exemplo, Automapper ). Embora isso não garanta a falta de referências circulares, o design adequado deve fazê-lo: os modelos de serviço devem conter exatamente o que o consumidor do serviço exige (ou seja, as propriedades).

Nesses casos raros, quando o cliente solicita uma hierarquia envolvendo o mesmo tipo de object em níveis diferentes, o serviço pode criar uma estrutura linear com relacionamento pai-> filho (usando apenas identificadores, não referências).

Aplicações modernas tendem a evitar o carregamento de estruturas de dados complexas de uma vez e os modelos de serviço devem ser escassos. Por exemplo:

  1. acessar um evento – somente os dados do header (identificador, nome, data, etc.) são carregados -> modelo de serviço (JSON) contendo apenas dados de header
  2. lista de participantes gerenciados – acessar um popup e preguiçoso carregar a lista -> modelo de serviço (JSON) contendo apenas a lista de participantes

Estou usando a correção, porque usando o Knockout em visualizações do MVC5.

Em ação

 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_; } 

Você pode observar as propriedades que causam a referência circular. Então você pode fazer algo como:

 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); } 

Uma alternativa mais fácil de resolver esse problema é retornar uma string e formatá-la para json com 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 ); } 

É importante a parte “Selecionar”, que escolhe as propriedades que você deseja em sua visualização. Alguns objects têm uma referência para o pai. Se você não escolher os atributos, a referência circular pode aparecer, se você simplesmente pegar as tabelas como um todo.

Não faça isso:

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

Faça isso em vez disso, se você não quiser a tabela inteira:

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

Isso ajuda a renderizar uma visualização com menos dados, apenas com os atributos de que você precisa e torna sua Web mais rápida.