Обнаружена циклическая ссылка при сериализации объекта типа 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()

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

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


person Jon    schedule 20.07.2009    source источник
comment
Посмотри на это. Есть решение, использующее атрибут ScriptIgnore. stackoverflow.com/questions/1193857/subsonic-3-0-0-2- Structs-tt   -  person freddoo    schedule 27.10.2009
comment
Для меня это было лучшим решением; У меня была Game ›Tournament› Game ›Tournament› Game и т. Д. Я поместил атрибут ScriptIgnore в свойство Tournament.Game, и он работал нормально :)   -  person eth0    schedule 15.10.2012
comment
Если кому-то нужно автоматическое (не передовое) решение этой проблемы, не требующее дополнительного кода, ознакомьтесь с этим QA: Не сериализовать ссылки классов Entity Framework в JSON (библиотека ServiceStack.Text)   -  person nikib3ro    schedule 21.02.2013


Ответы (14)


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

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

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

person Darin Dimitrov    schedule 20.07.2009
comment
Я думаю, что, возможно, выбор тех, которые мне нужны, может сработать. Я думаю, что круговая ссылка связана с тем, что в Event у меня есть IQueryable ‹Category›, который, в свою очередь, будет иметь IQueryable ‹Event› - person Jon; 20.07.2009
comment
Automapper не гарантирует, что вы не столкнетесь с этой проблемой. Я пришел сюда в поисках ответа, и на самом деле я использую автомаппер. - person Captain Kenpachi; 14.08.2013
comment
Посмотрите ответ от @ClayKaboom, поскольку он объясняет, почему он может быть круглым - person PandaWood; 04.03.2014

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

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

return Content(list, "application/json");
person ddfnfal    schedule 06.05.2014
comment
Этот встроенный код отлично работал у меня. То же самое в глобальной конфигурации, о котором упоминал kravits88, у меня не работает. ТАКЖЕ, подпись метода должна быть обновлена, чтобы возвращать ContentResult для этого кода. - person BiLaL; 23.04.2015
comment
Этот ответ следует отметить как лучший, поскольку он охватывает случаи, когда вы не можете часами преобразовывать свои объекты в другие представления, как в ответе, отмеченном как принятый. - person Geeky Guy; 27.03.2017
comment
Я сделал это, но теперь я получаю исключение с нулевой ссылкой во время сериализации и не знаю, какое свойство его вызывает. - person Mike Flynn; 25.10.2020

На самом деле это происходит потому, что именно сложные объекты приводят к сбою результирующего объекта 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
           );
}
person ClayKaboom    schedule 07.07.2011
comment
+1 за ясность и легкость понимания, спасибо @Clay. Мне нравится ваше объяснение концепций ошибки. - person Ajay2707; 26.07.2017

Подводя итог, есть 4 решения этой проблемы:

Решение 1. Отключите ProxyCreation для DBContext и в конце восстановите его.

    private DBEntities db = new DBEntities();//dbcontext

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

Решение 2. Использование JsonConvert, установив для ReferenceLoopHandling игнорирование настроек сериализатора.

    //using using Newtonsoft.Json;

    private DBEntities db = new DBEntities();//dbcontext

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

Следующие два решения одинаковы, но лучше использовать модель, потому что она строго типизирована.

Решение 3. Верните модель, которая включает только необходимые свойства.

    private DBEntities db = new DBEntities();//dbcontext

    public class ProductModel
    {
        public int Product_ID { get; set;}

        public string Product_Name { get; set;}

        public double Product_Price { get; set;}
    }

    public ActionResult Index()
    {
        try
        {
            var data = db.Products.Select(p => new ProductModel
                                                {
                                                    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);
        }
    }

Решение 4. Верните новый динамический объект, который включает только необходимые свойства.

    private DBEntities db = new DBEntities();//dbcontext

    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);
        }
    }
person Amro    schedule 16.12.2015

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

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

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

person Marc Gravell    schedule 20.07.2009

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

person MorenajeRD    schedule 03.03.2016

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

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
person kravits88    schedule 14.05.2014
comment
По-видимому, выглядит очень прямолинейно, но у меня не сработало. - person BiLaL; 23.04.2015

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

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

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

    context.Configuration.ProxyCreationEnabled = false;

Очень хорошо объяснено в статье ниже.

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

person nilesh    schedule 30.05.2012

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

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

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

Правильная архитектура

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

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

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

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

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

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

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

person Unais.N.I    schedule 16.01.2014

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

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

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

   return object
}
person Bassel    schedule 21.12.2016

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

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

person Sterling Diaz    schedule 29.08.2017

person    schedule
comment
Это не отвечает на вопрос - person Dane I; 24.02.2015