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


13

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

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

class parent
{
   public List<child> 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);

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

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

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

Відповіді:


6

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

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

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

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

Призводить до: TypeError: Converting circular structure to JSON

Єдиний вибір, який ви маєте, - це зберегти лише складову -> складову частину композиції та відкинути компонент "назад навігації" -> композит, таким чином, у вашому прикладі:

class parent
{
    public List<child> 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, ймовірно, використовуватиме динамічні проксі-сервери вашого класу для реалізації поведінки, такої як виявлення змін або ледаче завантаження, ви повинні відключити їх, якщо ви хочете серіалізувати об'єкти EF.

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

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


Чи є химерний спосіб з об'єктно-орієнтованими методами, я можу обійти як круговий посилання, так і питання EF.
DanScan

Чи є химерний спосіб з об'єктно-орієнтованими техніками, які я можу обійти як круговою посиланням, так і питанням EF? Як і BaseObject успадковується суттюObject і viewObject. Таким чином, сутністьObject мала б циркулярну посилання, але viewObject не мала б циркулярної посилання. Я обійшов це, будуючи viewObject від entitObject (viewObject.name = entitetObject.name), але це здається марною витратою часу. Як я можу обійти це питання?
DanScan

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

2

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

Для цього ви можете встановити 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<Employee>(dcs);

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

Дивіться також клас DataContractsSerializer .


1

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

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

Робота з циркулярними посиланнями при серіалізації об'єктів з Джексоном

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

private final Set<ObjectName> 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<MBeanAttributeInfo> ais = this.getAttributeInfos(on);
        for (final MBeanAttributeInfo ai : ais)
        {
            final Object attribute = this.getAttribute(on, ai.getName());
            jgen.writeObjectField(ai.getName(), attribute);
        }
        jgen.writeEndObject();
    }
}

0

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

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


Необхідно більше часу на обробку, коли ви говорите про великі дані, оскільки тепер вам доведеться все перетворити двічі.
Девід ван Дагтерен

-2

.Include(x => x.TableName ) не повертаючи відносини (від головної таблиці до залежної таблиці) або лише повертаючи один рядок даних, ПОПЕРЕДЖУЙТЕ ТУТ

/programming/43127957/include-not-working-in-net-core-returns-one-parent

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

using Microsoft.EntityFrameworkCore; 
using Newtonsoft.Json; 
using Project_Name_Here.Models;

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