Циркулярне посилання було виявлено під час серіалізації об’єкта типу "SubSonic.Schema .DatabaseColumn".


170

Я намагаюся зробити просте повернення JSON, але у мене є проблеми, у мене є наступне нижче.

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

Я отримую HTTP 500 за винятком, як показано в назві цього питання. Я також спробував

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

Це дало ту саму проблему.

Це помилка чи моя реалізація?


1
Подивіться на цю. Є рішення за допомогою ScriptIgnoreатрибута. stackoverflow.com/questions/1193857/subsonic-3-0-0-2-structs-tt
freddoo

Це було найкращим рішенням для мене; У мене була гра> Турнір> Гра> Турнір> Гра та ін. Я розмістив ScriptIgnoreатрибут у власності Tournament.Game, і він спрацював чудово :)
eth0

Якщо хтось хоче «автоматизованого» (не найкращого практичного) рішення цієї проблеми, який не потребує додаткового коду, ознайомтеся з цим QA: Не серіалізуйте посилання класу Entity Framework в JSON (бібліотека ServiceStack.Text)
nikib3ro

Відповіді:


175

Здається, у вашій ієрархії об’єктів є круглі посилання, які не підтримуються серіалізатором JSON. Вам потрібні всі стовпці? Ви можете вибрати лише ті властивості, які вам потрібні для перегляду:

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

Це зробить ваш JSON об’єкт легшим і зрозумілим. Якщо у вас є багато властивостей, AutoMapper може використовуватися для автоматичного відображення між об'єктами DTO і Переглядати об'єкти.


Я думаю , що, може бути , вибираючи ті , які я хочу може працювати , я думаю , що циклічна посилання тому , що в подію я маю IQueryable <Категорія> , які в свою чергу , матиме IQueryable <подія>
Jon

7
Automapper не є гарантією, що ви не отримаєте цю проблему. Я прийшов сюди шукати відповідь, і я фактично використовую автомати.
Капітан Кенпачі

1
Дивіться відповідь від @ClayKaboom, оскільки вона пояснює, чому це може бути круговим
PandaWood

106

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

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

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

3
Цей вбудований код добре працював для мене. Ті самі речі в глобальній конфігурації, про які згадував kravits88, не працюють для мене. ТАКОЖ, підпис методу слід оновити, щоб повернути ContentResult для цього коду.
BiLaL

6
Це слід позначати як найкращу відповідь, оскільки воно охоплює випадки, коли ви не можете витрачати години на перетворення об'єктів в інші зображення, як у відповіді, позначеній як прийнято.
Ренан

56

Це насправді відбувається тому, що складні об'єкти - це те, що призводить до відмови отриманого об'єкта 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
           );
}

1
+1 за чіткі та зрозумілі речі, дякую @Clay. Мені подобається ваше пояснення щодо понять, що стоять за помилкою.
Ajay2707

14

Підводячи підсумок, є чотири рішення для цього:

Рішення 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

JSON, як xml та інші інші формати, є деревним форматом серіалізації. Він не сподобається вам, якщо у ваших об’єктах є кругові посилання, як "дерево" було б:

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

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


4

Це завдяки новому шаблону DbContext T4, який використовується для генерації сутностей EntityFramework. Для того, щоб мати можливість виконувати відстеження змін, у цих шаблонах використовується шаблон проксі-сервера, загортаючи свої приємні POCO. Потім це викликає проблеми при серіалізації з JavaScriptSerializer.

Тоді два рішення:

  1. Або просто серіалізуйте та поверніть потрібні властивості клієнту
  2. Ви можете вимкнути автоматичну генерацію проксі, встановивши її в конфігурації контексту

    context.Configuration.ProxyCreationEnabled = false;

Дуже добре пояснено в статті нижче.

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


4

Використання Newtonsoft.Json: У вашому методі Global.asax Application_Start додайте цей рядок:

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

1
Мабуть, виглядає дуже прямо вперед, але не вийшло для мене
BiLaL

4

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


4

Уникайте прямого перетворення об’єкта таблиці. Якщо відносини встановлені між іншими таблицями, це може призвести до помилки. Скоріше, ви можете створити клас моделі, призначити об'єкту класу значення та потім його серіалізувати.


3

Надані відповіді хороші, але я думаю, їх можна вдосконалити, додавши «архітектурну» перспективу.

Розслідування

MVC's Controller.Jsonфункція виконує роботу, але вона дуже погана при наданні відповідної помилки в цьому випадку. Використовуючи Newtonsoft.Json.JsonConvert.SerializeObjectпомилку, вказується саме те, що є властивістю, що викликає циркулярну посилання. Це особливо корисно при серіалізації більш складних ієрархій об'єктів.

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

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

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

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

У тих рідкісних випадках, коли клієнт вимагає ієрархії, що включає один і той же тип об'єкта на різних рівнях, служба може створити лінійну структуру з відносинами батьків-> дочірня (використовуючи просто ідентифікатори, а не посилання).

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

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

1

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

0

Ви можете помітити властивості, які викликають циркулярну відсилку. Тоді ви можете зробити щось на кшталт:

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

   return object
}

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

Це не дає відповіді на запитання
Dane I

-1

Простішою альтернативою для вирішення цієї проблеми є повернення рядка та форматування цієї рядка в 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 );
}

Це допомагає візуалізувати представлення даних із меншою кількістю даних, а саме з необхідними атрибутами та робить ваш Інтернет швидшим.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.