Як обійти проблему циркулярного посилання з JSON і Entity

Я експериментував зі створенням веб-сайту, який використовує MVC з JSON для мого презентаційного рівня та рамки Entity для моделі даних/бази даних. Моя проблема вступає в гру з серіалізацією об'єктів Модель в JSON.

Я використовую перший метод коду для створення своєї бази даних. При виконанні першого методу коду відносини один до багатьох (батько/дитина) вимагають, щоб дитина мав посилання на батька. (Приклад коду мій бути помилкою, але ви отримаєте зображення)

class parent
{
   public List Children{get;set;}
   public int Id{get;set;}

}
class child
{
    public int ParentId{get;set;}
    [ForeignKey("ParentId")]
    public parent MyParent{get;set;}
    public string name{get;set;}
 }

При поверненні "батьківського" об'єкта через JsonResult виникає кругова помилка посилання, оскільки "дочірня" має властивість батьківського класу.

Я спробував атрибут ScriptIgnore, але я втрачаю можливість дивитися на дочірні об'єкти. Мені потрібно буде відображати інформацію у вигляді батьківської дитини в певний момент.

Я намагався зробити базові класи як для батьків, так і для дитини, які не мають кругової посилання. На жаль, коли я намагаюся відправити baseParent і baseChild, вони читаються JSON Parser як їхні похідні класи (я впевнений, що ця концепція мене уникає).

Base.baseParent basep = (Base.baseParent)parent;
return Json(basep, JsonRequestBehavior.AllowGet);

Одним з рішень, які я придумав, є створення моделей "View". Створюються прості версії моделей баз даних, які не містять посилання на батьківський клас. Ці моделі перегляду мають метод повернення версії бази даних та конструктора, який приймає модель бази даних як параметр (viewmodel.name = databasemodel.name). Цей метод здається змушеним, хоча він працює.

ПРИМІТКА: Я розміщую тут, тому що я думаю, що це більше обговорення, гідне. Я міг би використовувати інший шаблон дизайну для того, щоб вийти з цього питання, або він може бути таким же простим, як використання іншого атрибута на моїй моделі. У своєму пошуку я не бачив хорошого методу подолання цієї проблеми.

Моєю кінцевою метою було б мати приємне додаток MVC, що сильно використовує JSON для спілкування з сервером і відображення даних. При збереженні послідовної моделі через шари (або якнайкраще, як я можу придумати).

11

5 Відповіді

Я бачу два різні теми у вашому питанні:

  • Як керувати циркулярними посиланнями при серіалізації до JSON?
  • Наскільки безпечно використовувати об'єкти EF як об'єкти моделі у ваших переглядах?

Що стосується циркулярних посилань, мені дуже шкода сказати, що простого рішення немає. По-перше, тому що JSON не можна використовувати для представлення кругових посилань, такий код:

var aParent = {Children : []}, aChild  = {Parent : aParent};
aParent.Children.push(aChild);
JSON.stringify(aParent);

Results in: TypeError: Converting circular structure to JSON

The only choice you have is to keep only the composite --> component part of the composition and discard the "back navigation" component --> composite, thus in you example:

class parent
{
    public List Children{get;set;}
    public int Id{get;set;}
}
class child
{
    public int ParentId{get;set;}
    [ForeignKey("ParentId"), ScriptIgnore]
    public parent MyParent{get;set;}
    public string name{get;set;}
}

Ніщо не заважає вам повторно складати цю властивість навігації на стороні клієнта, використовуючи тут jQuery:

$.each(parent.Children, function(i, child) {
  child.Parent = parent;  
})

Але тоді вам доведеться знову відкинути його, перш ніж відправити його на сервер, оскільки JSON.stringify не зможе серіалізувати циркулярне посилання:

$.each(parent.Children, function(i, child) {
  delete child.Parent;  
})

Тепер існує проблема використання об'єктів EF як об'єктів моделі перегляду.

Перший EF, ймовірно, використовує Dynamic Proxies вашого класу для реалізації таких поведінок, як виявлення змін або ліниве завантаження, ви повинні відключити їх, якщо ви хочете серіалізувати об'єкти EF.

Більш того, використання об'єктів EF в інтерфейсі користувача може бути під загрозою, оскільки всі підшивки за замовчуванням будуть відображати кожне поле з запиту на поля об'єктів, включаючи ті, які ви не бажали встановлювати користувачем.

Таким чином, якщо ви хочете, щоб програма MVC була правильно розроблена, я б рекомендував використовувати спеціальну модель перегляду, щоб запобігти виникненню клієнту внутрішньої бізнес-моделі.

5
додано
Чи є химерний спосіб з об'єктно-орієнтованими методами, я можу обійти як циркулярний виклик, так і випуск EF.
додано Автор user120557, джерело
Чи є химерний спосіб з об'єктно-орієнтованими методами, я можу обійти як циркулярний виклик, так і випуск EF? Подібно BaseObject успадковується entityObject і viewObject. Отже, entityObject мав би циркулярне посилання, але viewObject не мав би циркулярного посилання. Я обминувся цим, створивши viewObject з entityObject (viewObject.name = entityObject.name), але це здається марною тратою часу. Як мені обійти цю проблему?
додано Автор user120557, джерело
Вони вам дуже багато. Ваше пояснення було дуже зрозумілим і зрозумілим.
додано Автор user139195, джерело

.Include(x => x.TableName ) not returning relationships (from principal table to dependent table), or only returning one row of data, FIX HERE:

https://stackoverflow.com/questions/43127957/include-not-working-in-net-core-returns-one-parent

Також у Startup.cs переконайтеся, що ви маєте це вгорі

using Microsoft.EntityFrameworkCore; 
using Newtonsoft.Json; 
using Project_Name_Here.Models;
2
додано
син Ват? erm .. wat?
додано Автор Pradi KL, джерело

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

Для цього можна налаштувати DataContractSerializer і встановити DataContractSerializer.PreserveObjectReferences властивість" false "у конструкторі класу моделі даних. Це вказує, що посилання на об'єкт не повинні зберігатися при серіалізації відповідей HTTP.

Приклади:

Формат Json:

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = 
    Newtonsoft.Json.PreserveReferencesHandling.None;

Формат XML:

var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
var dcs = new DataContractSerializer(typeof(Employee), null, int.MaxValue, 
    false, /* preserveObjectReferences: */ false, null);
xml.SetSerializer(dcs);

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

Див. Також

JSON серіалізатор, що має справу з циркулярними посиланнями

Нижче наведено приклад користувальницького Джексона JSONSerializer , який має справу з циркулярними посиланнями шляхом серіалізації першого входження і збереження * посилання до першого входження на всіх наступних входженнях.

Робота з циркулярними посиланнями при серіалізації об'єктів за допомогою Jackson

Відповідний фрагмент із наведеної вище статті:

private final Set seen;

/**
 * Serialize an ObjectName with all its attributes or only its String representation if it is a circular reference.
 * @param on ObjectName to serialize
 * @param jgen JsonGenerator to build the output
 * @param provider SerializerProvider
 * @throws IOException
 * @throws JsonProcessingException
 */
@Override
public void serialize(@Nonnull final ObjectName on, @Nonnull final JsonGenerator jgen, @Nonnull final SerializerProvider provider) throws IOException, JsonProcessingException
{
    if (this.seen.contains(on))
    {
        jgen.writeString(on.toString());
    }
    else
    {
        this.seen.add(on);
        jgen.writeStartObject();
        final List ais = this.getAttributeInfos(on);
        for (final MBeanAttributeInfo ai : ais)
        {
            final Object attribute = this.getAttribute(on, ai.getName());
            jgen.writeObjectField(ai.getName(), attribute);
        }
        jgen.writeEndObject();
    }
}
1
додано

Єдине рішення, яке я придумав, - це створити моделі "Перегляд". I   створювати прості версії моделей баз даних, які не включають   посилання на батьківський клас. У цих моделях перегляду кожен має метод   повернути Версію Бази даних і конструктор, який бере базу даних   модель як параметр (viewmodel.name = databasemodel.name). Це   здається вимушеним, хоча він працює.

Надсилання мінімуму даних є єдиною правильною відповіддю. Коли ви надсилаєте дані з бази даних, зазвичай немає сенсу розсилати кожен стовпець з усіма асоціаціями. Споживачам не потрібно мати справу з асоціаціями та структурами баз даних, тобто для баз даних. Це не тільки збереже пропускну здатність, але й набагато простіше підтримувати, читати та споживати. Запитуйте дані, а потім моделюйте їх для того, що вам дійсно потрібно відправити eq. мінімум.

0
додано
Чим більше часу на обробку потрібно, коли ви говорите про великі дані, так як тепер вам доведеться перетворити все двічі.
додано Автор Ne Mo, джерело
var chat = new Chat();
var chat = new Chat();
642 учасників

Обсуждение вопросов по C# / .NET / .NET Core / .NET Standard / Azure Сообщества-организаторы: — @itkpi — @dncuug

ІТ КПІ - JavaScript
ІТ КПІ - JavaScript
504 учасників

співтовариство javascript розробників в Telegram