Подтвердить что ты не робот

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

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

Это ошибка:

Ошибка

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

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

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

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

Мои классы следующие:

Order

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

}

ПРИМЕЧАНИЕ: ADDRESS CLASS - НОВЫЙ ДОПОЛНЕНИЕ ВО ВРЕМЯ РЕДАКТИРОВАНИЯ

Адрес

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

Спасибо

4b9b3361

Ответ 1

Вы можете попытаться удалить ключевое слово 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 для этого отредактируйте, пожалуйста, на его ответ.)

Ответ 2

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

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} 

Ответ 3

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

Ответ 4

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

[ScriptIgnore]

Это делает код не выполняющим Циркулярные ссылки.

Ответ 5

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

Ознакомьтесь с справочными документами в разделе" Сериализация и десериализация 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

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

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

Ответ 7

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

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

Ответ 8

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

У вас есть несколько методов:

  • Отключить загрузку при загрузке запроса (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. Короче: это означает, что он содержит исходные значения и обновленные значения, чтобы их можно было обновить позже. Он обрабатывает другие вещи: -)

Ответ 9

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

Итак, в моей модели я выбрал связь и изменил ссылку "Родитель" как "Внутренний", а не "Публичный".

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

Ответ 10

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

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

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