EF 4.1 - Code First - Ошибка сериальной ссылки JSON - PullRequest
47 голосов
/ 08 апреля 2011

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

Это ошибка:

Error

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

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

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

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

Мои занятия следующие:

Заказать

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

Спасибо

Ответы [ 11 ]

48 голосов
/ 08 апреля 2011

Вы можете попытаться удалить ключевое слово 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);
}

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

Edit

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

ppEFContext.Configuration.ProxyCreationEnabled = false;

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

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

41 голосов
/ 06 марта 2012

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

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

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

е

{Models.car} 

вместо

{System.Data.Entity.DynamicProxies.car_231710A36F27E54BC6CE99BB50E0FE3B6BD4462EC‌​A19695CD1BABB79605296EB} 
9 голосов
/ 08 апреля 2011

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

8 голосов
/ 09 октября 2011

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

[ScriptIgnore]

В результате код не выполняет циклические ссылки.

7 голосов
/ 10 апреля 2012

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

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

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

PreserveReferencesHandling = PreserveReferencesHandling.Objects;

. Примером может быть такой:

var serializerSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects };

string json = JsonConvert.SerializeObject(people, Formatting.Indented, serializerSettings);

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

6 голосов
/ 18 марта 2012

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

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

3 голосов
/ 02 сентября 2013

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

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);
}
2 голосов
/ 04 марта 2014

Циклическая ссылка возникает из-за того, что вы активно загружаете объект.

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

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

PS.Прокси - это объект, который создается EF при загрузке из Entity Framework.Вкратце: это означает, что он содержит исходные значения и обновленные значения, поэтому они могут быть обновлены позже.Он обращается с другими вещами; -)

0 голосов
/ 22 мая 2013

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

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

0 голосов
/ 18 августа 2012

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...