una serializar referencia objeto detectó c# .net json entity-framework subsonic

c# - Se detectó una referencia circular al serializar un objeto de tipo ''SubSonic.Schema.DatabaseColumn''.



referencia circular sql (14)

Es debido a la nueva plantilla DbContext T4 que se utiliza para generar las entidades EntityFramework. Para poder realizar el seguimiento de cambios, esta plantilla utiliza el patrón de Proxy, envolviendo sus agradables POCOs con ellos. Esto causa los problemas al serializar con el JavaScriptSerializer.

Entonces las 2 soluciones son:

  1. O bien, solo serializa y devuelve las propiedades que necesita en el cliente
  2. Puede desactivar la generación automática de proxies configurándolo en la configuración del contexto

    context.Configuration.ProxyCreationEnabled = false;

Muy bien explicado en el siguiente artículo.

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

Estoy tratando de hacer un retorno JSON simple, pero estoy teniendo problemas. Tengo lo siguiente a continuación.

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

Obtengo un HTTP 500 con la excepción que se muestra en el título de esta pregunta. También intenté

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

Eso dio el mismo problema.

¿Es esto un error o mi implementación?


Esto realmente sucede porque los objetos complejos son los que hacen que el objeto json resultante falle. Y falla porque cuando el objeto se mapea mapea a los niños, que mapea a sus padres, haciendo una referencia circular para que ocurra. Json tardaría un tiempo infinito en serializarlo, por lo que evita el problema con la excepción.

La asignación de Entity Framework también produce el mismo comportamiento, y la solución es descartar todas las propiedades no deseadas.

Solo explicitando la respuesta final, todo el código sería:

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

También podría ser el siguiente en caso de que no desee los objetos dentro de una propiedad 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 ); }


Estoy usando la solución, porque uso Knockout en MVC5 views.

En acción

return Json(ModelHelper.GetJsonModel<Core_User>(viewModel));

función

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


Evite convertir el objeto de tabla directamente. Si las relaciones se establecen entre otras tablas, podría arrojar este error. Por el contrario, puede crear una clase de modelo, asignar valores al objeto de clase y luego serializarlo.


JSON, como xml y varios otros formatos, es un formato de serialización basado en árbol. No te amará si tienes referencias circulares en tus objetos, ya que el "árbol" sería:

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

A menudo hay formas de deshabilitar la navegación a lo largo de una ruta determinada; por ejemplo, con XmlSerializer puede marcar la propiedad principal como XmlIgnore . No sé si esto es posible con el serializador json en cuestión, ni si DatabaseColumn tiene marcadores adecuados ( muy poco probable, ya que necesitaría hacer referencia a cada API de serialización)


Las respuestas proporcionadas son buenas, pero creo que se pueden mejorar agregando una perspectiva "arquitectónica".

Investigación

MVC''s Controller.Json función MVC''s Controller.Json está haciendo el trabajo, pero es muy pobre al proporcionar un error relevante en este caso. Al usar Newtonsoft.Json.JsonConvert.SerializeObject , el error especifica exactamente cuál es la propiedad que desencadena la referencia circular. Esto es particularmente útil al serializar jerarquías de objetos más complejas.

Arquitectura apropiada

Nunca se debe tratar de serializar modelos de datos (por ejemplo, modelos EF), ya que las propiedades de navegación de ORM son el camino a la perdición cuando se trata de la serialización. El flujo de datos debe ser el siguiente:

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

Los modelos de servicio se pueden obtener a partir de modelos de datos que utilizan auto mapeadores (por ejemplo, Automapper ). Si bien esto no garantiza la falta de referencias circulares, el diseño adecuado debería hacerlo: los modelos de servicio deben contener exactamente lo que el consumidor del servicio requiere (es decir, las propiedades).

En esos casos excepcionales, cuando el cliente solicita una jerarquía que involucra el mismo tipo de objeto en diferentes niveles, el servicio puede crear una estructura lineal con relación padre-> hijo (usando solo identificadores, no referencias).

Las aplicaciones modernas tienden a evitar la carga de estructuras de datos complejas a la vez y los modelos de servicio deben ser delgados. P.ej:

  1. acceder a un evento: solo se cargan los datos del encabezado (identificador, nombre, fecha, etc.) -> modelo de servicio (JSON) que contiene solo datos del encabezado
  2. lista de asistentes administrados: acceda a una ventana emergente y cargue la lista pereza -> modelo de servicio (JSON) que contiene solo la lista de asistentes

Para resumir, hay 3 soluciones para esto:

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


Parece que hay referencias circulares en su jerarquía de objetos que no son compatibles con el serializador JSON. ¿Necesitas todas las columnas? Puede recoger solo las propiedades que necesita en la vista:

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

Esto hará que tu objeto JSON sea más ligero y fácil de entender. Si tiene muchas propiedades, AutoMapper podría usarse para mapear automatically entre objetos DTO y objetos View.


Puede observar las propiedades que causan la referencia circular. Entonces puedes hacer algo como:

private Object DeCircular(Object object) { // Set properties that cause the circular reference to null return object }


Tuve el mismo problema y lo resolví al using Newtonsoft.Json;

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


Una alternativa más fácil para resolver este problema es devolver una cadena y formatear esa cadena en json con 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 ); }

Es importante la parte "Seleccionar", que elige las propiedades que desea en su vista. Algunos objetos tienen una referencia para el padre. Si no elige los atributos, puede aparecer la referencia circular, si solo toma las tablas como un todo.

No hagas esto:

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

Haga esto en su lugar si no quiere toda la tabla:

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

Esto ayuda a renderizar una vista con menos datos, solo con los atributos que necesita, y hace que su web funcione más rápido.


Usando Newtonsoft.Json: en su método Global.asax Application_Start agregue esta línea:

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


agregue [JsonIgnore] a las propiedades virtuales en su modelo.


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