ef4 причиной Циркулярная ссылка в веб-сервисе
У меня есть объект Reason:
public class Reason
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual Company Company {get;set;}
}
Я использую Entity Framework 4, и Компания является собственностью компании.
Я также использую веб-сервисы, чтобы вернуть данные клиенту.
У меня есть веб-метод, который возвращает причины:
[WebMethod]
public Reason[] GetCallReasons()
{
IReasonRepository rep =
ObjectFactory.GetInstance<IReasonRepository>();
return rep.GetReasonsList().ToArray();
}
Из-за ef4 я получаю следующее исключение для выполнения веб-метода:
A circular reference was detected while serializing an object of type 'System.Data.Entity.DynamicProxies.Reason_24A0E4BBE02EE6BC2CF30BB56CFCB670C7D9D96D03D40AF4D174B89C9D3C5537'
Проблема возникает, потому что ef4 добавляет свойство, которое не может быть сериализовано:
Чтобы решить эту проблему и устранить ошибку, я могу отключить свойство навигации, не делая его виртуальным или удалив свойство навигации. Но я хочу это и хочу использовать функцию отложенной загрузки.
Я также могу написать специальный сериализатор для Reason, но у меня есть много классов, которые я использовал в своих веб-сервисах, и написать сериализатор для всех них - большая работа.
Как я могу решить это исключение?..
3 ответа
Существует несколько решений вашей проблемы, и они действительно зависят от типа службы, которую вы используете, и от типа сериализации:
- Чистый подход заключается в использовании DTO (объектов передачи данных), как уже было предложено @Mikael. DTO - это особый объект, который передает именно то, что вам нужно, и ничего более. Вы можете просто создать DTO, чтобы они не содержали циклические ссылки, и использовать AutoMapper для отображения между сущностями и DTO и наоборот. +1 за @Mikael, потому что он первым это упомянул.
Все другие подходы основаны на сериализации твиков, как предложено @Haz:
- WCF и
DataContractSerializer
: явно пометить ваши объекты сDataContract[IsReference=true]
и все свойства с[DataMember]
атрибутов. Это позволит вам использовать циклические ссылки. Если вы используете шаблон T4 для создания сущностей, вы должны изменить его, чтобы добавить эти атрибуты для вас. - WCF и
DataContractSerializer
: неявная сериализация. Отметьте одно из связанных свойств навигации с помощью[IgnoreDataMember]
атрибут, чтобы свойство не сериализовалось. XmlSerializer
: пометить один для связанных свойств навигации с[XmlIgnore]
атрибут- Другие сериализации: пометьте одно из связанных свойств навигации с помощью
[NonSerialized]
(+1 для Хаз, он был первым, кто упомянул об этом) для обычной сериализации или[ScriptIgnore]
для некоторой JSON-связанной сериализации.
Я обычно пишу определенные классы для веб-сервиса. Несмотря на то, что это некоторая дополнительная работа, она имеет преимущество в том, что веб-сервис становится более надежным, поскольку небольшие изменения в ваших сущностях не останутся безнаказанными и молча потерпят неудачу на стороне потребителя /javascript. Например, если я изменю имя свойства.
Есть несколько вещей, которые вы можете сделать, чтобы уменьшить объем работы, и один из них - использовать AutoMapper, который может автоматически сопоставлять объекты.
Вы не предоставили определение для класса вашей компании.... Но я предполагаю, что у вас есть коллекция Reason как собственность.
Ленивая загрузка в среде SOA на самом деле не работает. Вы не можете иметь неограниченную ленивую навигацию по сериализованному классу, после того как вы покинете веб-метод, у вас не будет возможности перезвонить исходному тексту данных от потребителя веб-метода для поиска подходящих объектов... поэтому сериализатор попытается посетить все свойства, в том числе ленивых свойств во время сериализации.
Вам необходимо отключить сериализацию в одной части циклической ссылки, либо в коллекции Reason в классе Company, либо в компании в классе Reason.
Вы можете использовать атрибут NotSerialized, чтобы отключить сериализацию определенного поля.