При сериализации объекта типа SubSonic.Schema .DatabaseColumn была обнаружена циклическая ссылка. - PullRequest
166 голосов
/ 20 июля 2009

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

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

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

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

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

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

Ответы [ 14 ]

170 голосов
/ 20 июля 2009

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

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

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

94 голосов
/ 06 мая 2014

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

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

return Content(list, "application/json");
54 голосов
/ 07 июля 2011

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

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

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

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

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

4 голосов
/ 03 марта 2016

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

4 голосов
/ 14 мая 2014

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

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
4 голосов
/ 16 января 2014

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

4 голосов
/ 30 мая 2012

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

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

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

    context.Configuration.ProxyCreationEnabled = false;

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

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

2 голосов
/ 06 марта 2016

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

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

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

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

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

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

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

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

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

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