Предотвращение исключения StackOverflowException при сериализации графа объектов Entity Framework в Json - PullRequest
4 голосов
/ 05 февраля 2012

Я хочу сериализовать Entity Framework Self-Tracking Entity Framework полный граф объектов (родитель + потомки в отношениях один-ко-многим) в Json.

Для сериализации я использую ServiceStack.JsonSerializer .

Вот так выглядит моя база данных (для простоты я удалил все ненужные поля):

ERD

Я получаю полный профильграфик следующим образом:

public Profile GetUserProfile(Guid userID)
{
    using (var db = new AcmeEntities())
    {
        return db.Profiles.Include("ProfileImages").Single(p => p.UserId == userId);
    }
}

Проблема заключается в том, что при попытке его сериализации:

Profile profile = GetUserProfile(userId);
ServiceStack.JsonSerializer.SerializeToString(profile);

создает StackOverflowException.Я считаю, что это потому, что EF предоставляет бесконечную модель, которая портит сериализатор.То есть я могу технически вызвать: profile.ProfileImages[0].Profile.ProfileImages[0].Profile ... и т. Д.

Как я могу "сгладить" мой граф объектов EF или иным образом предотвратить запуск ServiceStack.JsonSerializer в ситуацию переполнения стека?

Примечание: Я не хочу проецировать свой объект в анонимный тип (например, эти подсказки ), потому что это приведет к оченьдлинный и трудоемкий фрагмент кода).

Ответы [ 3 ]

9 голосов
/ 05 февраля 2012

У вас есть противоречивые опасения, модель EF оптимизирована для хранения вашей модели данных в СУБД, а не для сериализации - вот какую роль будут играть отдельные DTO.В противном случае ваши клиенты будут привязаны к вашей базе данных, где каждое изменение в вашей модели данных может привести к поломке существующих клиентов службы.

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

ServiceStack.Common включает встроенные функции отображения (т.е. TranslateTo / PopulateFrom)это упрощает сопоставление сущностей с DTO и наоборот.Вот пример, показывающий это:

https://groups.google.com/d/msg/servicestack/BF-egdVm3M8/0DXLIeDoVJEJ

Альтернатива состоит в том, чтобы украсить поля, которые вы хотите сериализовать в вашей модели данных, в поля [DataContract] / [DataMember].Любые свойства, не связанные с [DataMember], не будут сериализованы, поэтому вы должны использовать это, чтобы скрыть циклические ссылки, которые вызывают исключение StackOverflowException.

7 голосов
/ 09 февраля 2012

Ради моих коллег из StackOverflowers, которые занимаются этим вопросом, я объясню, что я в итоге сделал:

В случае, который я описал, вы должны использовать стандартный сериализатор .NET (а не ServiceStack's): System.Web.Script.Serialization.JavaScriptSerializer. Причина в том, что вы можете украсить свойства навигации, которые не нужно обрабатывать сериализатором, в атрибуте [ScriptIgnore].

Кстати, вы все еще можете использовать ServiceStack.JsonSerializer для десериализации - это быстрее, чем .NET, и у вас нет проблем с StackOverflowException, о которых я задавал этот вопрос.

Другая проблема заключается в том, как заставить Само-отслеживающиеся объекты украшать соответствующие свойства навигации с помощью [ScriptIgnore].

Объяснение: Без [ScriptIgnore], сериализация (с использованием сериализатора .NET Javascript) также вызовет исключение, о циклическом ссылки (аналогично проблеме, которая вызывает StackOverflowException в ServiceStack). Нам нужно устранить округлость, и это сделано используя [ScriptIgnore].

Поэтому я отредактировал файл .TT, поставляемый с Шаблоном самоконтролируемого объекта ADO.NET , и установил в нем [ScriptIgnore] в соответствующих местах (если кому-то понадобится код diff, напишите мне комментарий). Некоторые говорят, что редактировать эти «внешние», не предназначенные для редактирования файлы - плохая практика, но, черт возьми, это решает проблему, и это единственный способ, который не заставляет меня перестраивать всю мою архитектуру. приложение (используйте POCO вместо STE, используйте DTO для всего и т.д.)

@ mythz: Я не совсем согласен с вашим утверждением об использовании DTO - смотрите комментарии к вашему ответу. Я действительно ценю ваши огромные усилия по созданию ServiceStack (все модули!) И его бесплатному использованию с открытым исходным кодом. Я просто призываю вас либо уважать атрибут [ScriptIgnore] в ваших текстовых сериализаторах, либо придумать свой атрибут. Иначе, даже если на самом деле может использовать DTO, они не смогут добавить свойства навигации из дочернего объекта обратно в родительский, потому что они получат исключение StackOverflowException. Я отмечаю ваш ответ как «принятый», потому что в конце концов он помог мне найти свой путь в этом вопросе.

1 голос
/ 22 февраля 2012

Обязательно отсоединяйте сущность от ObjectContext перед ее сериализацией.

Я также использовал Newton JsonSerializer.

JsonConvert.SerializeObject (EntityObject, Formatting.Indented, new JsonSerializerSettings {PreserveReferencesHandling = PreserveReferencesHandling.Objects});

...