Круговая ссылка была обнаружена при сериализации объекта типа SubSonic.Schema. DatabaseColumn.

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

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

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

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

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

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

Ответ 1

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

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

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

Ответ 2

У меня была та же проблема и решена с помощью using Newtonsoft.Json;

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

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

Ответ 3

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

Ответ 4

JSON, например xml и другие форматы, представляет собой формат сериализации на основе дерева. Он не будет любить вас, если у вас есть круглые ссылки в ваших объектах, поскольку "дерево" будет:

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

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

Ответ 5

Чтобы подвести итог, есть 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);
        }
    }

Ответ 6

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

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

  • Либо вы просто сериализуете и возвращаете нужные свойства на клиенте
  • Вы можете отключить автоматическое создание прокси, установив его в настройке контекста

    context.Configuration.ProxyCreationEnabled = false;

Очень хорошо объяснено в следующей статье.

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

Ответ 7

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

Ответ 8

Использование Newtonsoft.Json: в свой метод Global.asax Application_Start добавьте эту строку:

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

Ответ 9

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

Ответ 10

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

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

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

Надлежащая архитектура

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

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

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

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

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

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

Ответ 11

Я использую исправление, потому что использование нокаута в представлениях 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_;  
    }

Ответ 12

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

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

   return object
}

Ответ 13

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

Ответ 14

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

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