Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

Автор вопроса: JonОтветов: 12

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

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

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

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

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

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

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

jsonentity-frameworksubsonic

Ответы

Ответ 1 · рейтинг 181 · Darin Dimitrov

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

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

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

Ответ 2 · рейтинг 115 · ddfnfal

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

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

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

Ответ 3 · рейтинг 58 · ClayKaboom

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

Подводя итог, есть 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);
        }
    }

Ответ 5 · рейтинг 7 · Marc Gravell

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

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

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

Ответ 6 · рейтинг 6 · MorenajeRD

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

Ответ 7 · рейтинг 5 · kravits88

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

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

Ответ 8 · рейтинг 4 · nilesh

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

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

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

    context.Configuration.ProxyCreationEnabled = false;

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

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

Ответ 9 · рейтинг 4 · Alexei - check Codidact

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

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

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

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

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

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

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

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

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

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

Ответ 10 · рейтинг 4 · Unais.N.I

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

Ответ 11 · рейтинг 1 · A.Kosecik

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

Ответ 12 · Bassel

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

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

   return object
}

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы