При сериализации объекта типа "дозвуковой" была обнаружена круговая ссылка.Схема.DatabaseColumn'.


Я пытаюсь сделать простой возврат JSON, но у меня возникли проблемы, у меня есть следующее ниже.

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

Я получаю HTTP 500 с исключением, как показано в заголовке этого вопроса. Я тоже пробовал

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

это дало ту же проблему.

это ошибка или моя реализация?

14 156

14 ответов:

Кажется, что в вашей иерархии объектов есть циклические ссылки, которые не поддерживаются сериализатором JSON. Вам нужны все колонки? Вы можете выбрать только те свойства, которые вам нужны в представлении:

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

это сделает ваш объект JSON легче и проще для понимания. Если у вас много свойств,AutoMapper можно использовать для автоматически карта между объектами DTO и объектами просмотра.

у меня была такая же проблема и решена using Newtonsoft.Json;

var list = JsonConvert.SerializeObject(model,
    Formatting.None,
    new JsonSerializerSettings() {
        ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
});

return Content(list, "application/json");

это на самом деле происходит, потому что сложные объекты-это то, что делает результирующий объект json неудачным. И это не удается, потому что, когда объект сопоставляется, он сопоставляет детей, которые сопоставляют своих родителей, делая круговую ссылку на происходящее. JSON займет бесконечное время, чтобы сериализовать его, поэтому он предотвращает проблему с исключением.

сопоставление Entity Framework также приводит к такому же поведению, и решение состоит в том, чтобы отбросить все нежелательные свойства.

просто объясняя окончательный ответ, весь код будет:

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

Это может быть следующее В случае, если вы не хотите, чтобы объекты внутри 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
           );
}

подводя итог, есть 3 решения для этого:

    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, как xml и различные другие форматы, является древовидным форматом сериализации. Он не будет любить вас, если у вас есть круговые ссылки в ваших объектах, так как" дерево " будет:

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

часто существуют способы отключения навигации по определенному пути, например, с XmlSerializer вы можете пометить родительское свойство как XmlIgnore. Я не знаю, возможно ли это с сериализатором json, о котором идет речь, и не DatabaseColumn есть подходящие маркеры (очень маловероятно, так как он должен был бы ссылаться на каждый API сериализации)

Its из-за нового шаблона DbContext T4, который используется для создания сущностей EntityFramework. Для того, чтобы иметь возможность выполнять отслеживание изменений, эти шаблоны используют шаблон Прокси, обертывая ваши хорошие POCOs с ними. Это тогда вызывает проблемы при сериализации с JavaScriptSerializer.

Итак, 2 решения:

  1. либо вы просто сериализуете и возвращаете нужные вам свойства на клиенте
  2. вы можете отключите автоматическую генерацию прокси, установив ее в конфигурации контекста

    контексте.Конфигурация.ProxyCreationEnabled = false;

очень хорошо описано в приведенной ниже статье.

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

С Помощью Newtonsoft.Json: в вашем глобальном.метод Asax Application_Start добавьте эту строку:

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

добавить [JsonIgnore] к свойствам виртуалов в вашей модели.

избегайте прямого преобразования объекта таблицы. Если отношения установлены между другими таблицами, это может вызвать эту ошибку. Вместо этого можно создать класс модели, присвоить значения объекту класса и затем сериализовать его.

при условии, что ответы хороши, но я думаю, что их можно улучшить, добавив "архитектурную" перспективу.

расследование

MVC's Controller.Json функция выполняет эту работу, но она очень плохо обеспечивает соответствующую ошибку в этом случае. С помощью Newtonsoft.Json.JsonConvert.SerializeObject, ошибка точно указывает, что такое свойство, которое вызывает циклическую ссылку. Это особенно полезно при сериализации более сложных иерархий объектов.

правильное архитектура

никогда не следует пытаться сериализовать модели данных (например, модели EF), поскольку навигационные свойства ORM-это путь к гибели, когда речь заходит о сериализации. Поток данных должен быть следующим:

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

модели обслуживания можно получить от моделей данных используя автоматические картографы (например Automapper). Хотя это не гарантирует отсутствие циклических ссылок, правильный дизайн должен это сделать: модели обслуживания должны содержать именно то, что служба потребитель требует (т. е. свойства).

в тех редких случаях, когда клиент запрашивает иерархию, включающую один и тот же тип объекта на разных уровнях, служба может создать линейную структуру с отношением parent->child (используя только идентификаторы, а не ссылки).

современные приложения, как правило, избегают загрузки сложных структур данных сразу и модели обслуживания должны быть тонкими. Например:

  1. доступ к данным заголовка только для события (идентификатор, имя, дата и т. д.) загружается - > модель сервиса (JSON), содержащая только данные заголовка
  2. список управляемых участников-доступ к всплывающему окну и ленивая загрузка списка - > модель обслуживания (JSON), содержащая только список участников

Я использую исправление, потому что использование нокаута в представлениях MVC5.

на действия

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

функции

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

вы можете заметить свойства, которые вызывают циклическую ссылку. Затем вы можете сделать что-то вроде:

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

более простой альтернативой для решения этой проблемы является возврат строки и форматирование этой строки в json с помощью 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 );
}

важно" выбрать " часть, которая выбирает свойства, которые вы хотите в своем представлении. Некоторые объекты имеют ссылку для родителя. Если вы не выберете атрибуты, может появиться циклическая ссылка, Если вы просто возьмете таблицы в целом.

не делайте этого:

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

вместо этого, если вы не хотите всю таблицу:

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

Это помогает визуализировать представление с меньшим количеством данных, только с атрибутами, которые вам нужны, и делает ваш веб-работать быстрее.