EF 4.1 – Code First – Ошибка серийной ссылки JSON

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

Это ошибка:

ошибка

Круговая ссылка была обнаружена при сериализации объекта типа «System.Data.Entity.DynamicProxies.Order_83CECF2AA4DE38232F9077D4B26941AB96BC61230419EA8AC42C9100E6072812». Описание: Необработанное исключение возникло во время выполнения текущего веб-запроса. Просмотрите трассировку стека для получения дополнительной информации об ошибке и ее возникновении в коде.

Сведения об исключении: System.InvalidOperationException: круговая ссылка была обнаружена при сериализации объекта типа «System.Data.Entity.DynamicProxies.Order_83CECF2AA4DE38232F9077D4B26941AB96BC61230419EA8AC42C9100E6072812».

Ошибка источника:

Необработанное исключение было создано во время выполнения текущего веб-запроса. Информация о происхождении и местоположении исключения может быть идентифицирована с использованием следа стека исключений ниже.

Мои classы:

порядок

public class Order { [Key] public int OrderId { get; set; } public int PatientId { get; set; } public virtual Patient Patient { get; set; } public int CertificationPeriodId { get; set; } public virtual CertificationPeriod CertificationPeriod { get; set; } public int AgencyId { get; set; } public virtual Agency Agency { get; set; } public int PrimaryDiagnosisId { get; set; } public virtual Diagnosis PrimaryDiagnosis { get; set; } public int ApprovalStatusId { get; set; } public virtual OrderApprovalStatus ApprovalStatus { get; set; } public int ApproverId { get; set; } public virtual User Approver { get; set; } public int SubmitterId { get; set; } public virtual User Submitter { get; set; } public DateTime ApprovalDate { get; set; } public DateTime SubmittedDate { get; set; } public Boolean IsDeprecated { get; set; } } 

Пациент

 public class Patient { [Key] public int PatientId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string MiddleInitial { get; set; } public bool IsMale; public DateTime DateOfBirth { get; set; } public int PatientAddressId { get; set; } public Address PatientAddress { get; set; } public bool IsDeprecated { get; set; } } 

Период сертификации

 public class CertificationPeriod { [Key] public int CertificationPeriodId { get; set; } public DateTime startDate { get; set; } public DateTime endDate { get; set; } public bool isDeprecated { get; set; } } 

Агентство

 public class Agency { [Key] public int AgencyId { get; set; } public string Name { get; set; } public int PatientAddressId { get; set; } public virtual Address Address { get; set; } } 

диагностика

 public class Diagnosis { [Key] public int DiagnosisId { get; set; } public string Icd9Code { get; set; } public string Description { get; set; } public DateTime DateOfDiagnosis { get; set; } public string Onset { get; set; } public string Details { get; set; } } 

OrderApprovalStatus

 public class OrderApprovalStatus { [Key] public int OrderApprovalStatusId { get; set; } public string Status { get; set; } } 

пользователь

 public class User { [Key] public int UserId { get; set; } public string Login { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string NPI { get; set; } public string Email { get; set; } } 

ПРИМЕЧАНИЕ. АДРЕСНЫЙ КЛАСС – НОВЫЙ ДОПОЛНЕНИЕ ВО ВРЕМЯ РЕДАКТИРОВАНИЯ

Адрес

 public class Address { [Key] public int AddressId { get; set; } public string StreetAddress { get; set; } public string City { get; set; } public string State { get; set; } public string Zip { get; set; } public string Phone { get; set; } public string Title { get; set; } public string Label { get; set; } } 

Код, который выполняет сериализацию, приведен здесь:

Выписка из OrderController

  public ActionResult GetAll() { return Json(ppEFContext.Orders, JsonRequestBehavior.AllowGet); } 

благодаря

Вы можете попытаться удалить ключевое слово virtual из всех свойств навигации, чтобы отключить ленивую загрузку и создание прокси, а затем использовать загрузку с загрузкой вместо этого, чтобы явно загрузить требуемый граф объекта:

 public ActionResult GetAll() { return Json(ppEFContext.Orders .Include(o => o.Patient) .Include(o => o.Patient.PatientAddress) .Include(o => o.CertificationPeriod) .Include(o => o.Agency) .Include(o => o.Agency.Address) .Include(o => o.PrimaryDiagnosis) .Include(o => o.ApprovalStatus) .Include(o => o.Approver) .Include(o => o.Submitter), JsonRequestBehavior.AllowGet); } 

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

редактировать

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

 ppEFContext.Configuration.ProxyCreationEnabled = false; 

Это отключает создание прокси только для конкретного экземпляра контекста ppEFContext .

(Я только что видел, @WillC уже упомянул об этом здесь. Upvote для этого отредактируйте, пожалуйста, к его ответу.)

Когда вы знаете, что вам нужно сериализоваться из определенного контекста, вы можете отключить создание прокси для этого конкретного запроса, как показано ниже. Это сработало для меня и было лучше, чем пересмотр моих моделей.

 using (var context = new MeContext()) { context.Configuration.ProxyCreationEnabled = false; return context.cars.Where(w => w.Brand == "Ferrari") } 

Этот подход отображает тип объекта-посредника для этого конкретного экземпляра контекста, поэтому возвращаемые объекты являются фактическим classом, поэтому сериализация не является проблемой.

то есть:

 {Models.car} 

вместо

 {System.Data.Entity.DynamicProxies.car_231710A36F27E54BC6CE99BB50E0FE3B6BD4462EC‌​A19695CD1BABB79605296EB} 

Проблема в том, что вы фактически сериализуете объект прокси-объекта, созданный сущностью. К сожалению, это связано с некоторыми проблемами при использовании с сериализатором JSON. Вы можете рассмотреть возможность сопоставления своих объектов с особыми classами POCO ради совместимости с JSON.

Существует атрибут для добавления объектов Entity Framework

 [ScriptIgnore] 

Это заставляет код не выполнять Циркулярные ссылки.

Я думаю, они исправили это в последней версии.

Ознакомьтесь с документами справки в разделе « Сериализация и десериализация JSON -> Сериализация и сохранение ссылок на объекты ».

Установите этот параметр при инициализации сериализатора JSON.Net:

 PreserveReferencesHandling = PreserveReferencesHandling.Objects; 

Таким образом, пример будет таким:

 var serializerSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects }; string json = JsonConvert.SerializeObject(people, Formatting.Indented, serializerSettings); 

Я проверил, что это работает с моим первым решением для кода и круговой ссылкой в ​​свойствах навигации. Если вы посмотрите на полученный JSON, он должен иметь свойства «$ id» и «$ ref» во всем мире.

Альтернативным решением будет использование анонимных типов в результате запроса LINQ.

В моем проекте я использую ленивую загрузку, и отключение было неправильным.

Альтернативным решением, если нужны только некоторые значения из объектов, является создание анонимного classа и его возврат, как показано ниже:

 public JsonResult AjaxFindByName(string term) { var customers = context.Customers .Where(c => c.Name.ToUpper().Contains(term.ToUpper())).Take(10) .AsEnumerable() .Select(c => new { value = c.Name, SSN = String.Format(@"{0:000\-00\-0000}", c.SSN), CustomerID = c.CustomerID }); return Json(customers, JsonRequestBehavior.AllowGet); } 

Круговая ссылка происходит, потому что вы используете активную загрузку объекта.

У вас есть несколько способов:

  • Отключайте загрузку при загрузке вашего запроса (linq или lambda) DbContext.Configuration.ProxyCreationEnabled = false;
  • Удалить ключевое слово virtual из Domainmodel
  • Отсоедините объекты (= нет загружаемых функций и без прокси)
    • Repository.Detach (EntityObject)
    • DbContext.Entry (entityObject) .EntityState = EntityState.Detached
  • Клонировать свойства
    • Вы можете использовать что-то вроде AutoMapper для клонирования объекта, не используйте интерфейс ICloneable, потому что он также клонирует ProxyProperties в объекте, так что это не сработает.
  • Если вы создаете API, попробуйте использовать проект separte с другой конфигурацией (которая не возвращает прокси)

PS. Proxies – это объект, созданный EF, когда вы загружаете его из Entity Framework. Короче: это означает, что он содержит исходные значения и обновленные значения, чтобы их можно было обновить позже. Он обрабатывает другие вещи 😉

Для тех, кто использует classы прокси EF / Linq2SQL, моим решением было просто удалить родительскую ссылку для моих дочерних объектов.

Поэтому в моей модели я выбрал отношения и изменил ссылку «Родитель» как «Внутренний», а не «Общественный».

Не может быть идеальным решением для всех, но работал для меня.

Вы можете удалить ключевое слово virtual :

public virtual Patient Patient { get; set; } public virtual Patient Patient { get; set; } -> public Patient Patient { get; set; } public Patient Patient { get; set; }

Имейте в виду, что при удалении ключевого слова virtual, ленивая загрузка будет отключена.

Я смог решить эту проблему, используя описанный здесь метод:

http://mytechworld.officeacuity.com/index.php/2010/02/serializing-entity-framework-objects-into-json-using-asp-net-mvc/

  • Как добавить тип в белый список политики сериализации GWT?
  • Сериализация JSON массива с полиморфными объектами
  • Как превратить объект C # в строку JSON в .NET?
  • Использование JSON.NET для возврата ActionResult
  • Сериализация classа, который содержит std :: string
  • Сериализация типа Json.Net с полиморфным дочерним объектом
  • Каковы различия между XmlSerializer и BinaryFormatter
  • Сохранение / загрузка данных в Unity
  • gwt - Использование списка в вызове RPC?
  • Сериализация / десериализация пользовательской коллекции с дополнительными свойствами с помощью Json.Net
  • Django rest framework, используйте разные сериализаторы в том же ModelViewSet
  • Interesting Posts

    Ошибка «Существует уже открытый DataReader, связанный с этой Командой, который должен быть закрыт первым» при использовании 2 отдельных команд

    Событие JQuery click работает только один раз

    Моя собственная кнопка Like: Django + Ajax – Как?

    Как отправить список объектов для просмотра и возврата к методу Post в controllerе

    Как изменить значок JFrame

    Как предотвратить автоматическое монтирование разделов в Linux (Mint)?

    Есть ли язык запросов для JSON?

    Firefox в Ubuntu: как автоматизировать базовый диалог подтверждения пароля аутентификации

    Объединить два текстовых файла по строкам, используя командный скрипт

    Как показывать вывод на терминал и сохранять в файл одновременно?

    Отказано для отображения в кадре, потому что он установил ‘X-Frame-Options’ в ‘SAMEORIGIN’

    Что такое polymorphism, для чего он используется и как он используется?

    laravel throwing MethodNotAllowedHttpException

    Как выборочно загружать изображения в Chromium?

    PendingIntent работает правильно для первого уведомления, но неправильно для остальных

    Давайте будем гением компьютера.