Обнаружен цикл саморегуляции – Возврат данных из WebApi в браузер

Я использую Entity Framework и имею проблему с получением родительских и дочерних данных в браузере. Вот мои classы:

public class Question { public int QuestionId { get; set; } public string Title { get; set; } public virtual ICollection Answers { get; set; } } public class Answer { public int AnswerId { get; set; } public string Text { get; set; } public int QuestionId { get; set; } public virtual Question Question { get; set; } } 

Я использую следующий код для возврата данных вопросов и ответов:

  public IList GetQuestions(int subTopicId, int questionStatusId) { var questions = _questionsRepository.GetAll() .Where(a => a.SubTopicId == subTopicId && (questionStatusId == 99 || a.QuestionStatusId == questionStatusId)) .Include(a => a.Answers) .ToList(); return questions; } 

На стороне C # это, похоже, работает, однако я замечаю, что объекты ответа ссылаются на вопрос. Когда я использую WebAPI для получения данных в браузере, я получаю следующее сообщение:

Тип ObjectContent`1 не смог сериализовать тело ответа для типа контента ‘application / json; кодировка = UTF-8’ .

Цикл саморегуляции обнаружен для свойства «вопрос» с типом «Models.Core.Question».

Это потому, что у Вопроса есть ответы и ответы есть ссылка на Вопрос? Все места, которые я искал, предлагают иметь ссылку на родителя в ребенке, поэтому я не уверен, что делать. Может кто-нибудь дать мне несколько советов по этому поводу.

Это потому, что у Вопроса есть ответы и ответы есть ссылка на Вопрос?

Да. Он не может быть сериализован.

EDIT: см. Ответ Tallmaris и комментарий OttO, поскольку он проще и может быть установлен глобально.

 GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Re‌​ferenceLoopHandling = ReferenceLoopHandling.Ignore; 

Старый ответ:

Проектировать объект EF Question к вашему собственному промежуточному или DataTransferObject. Это Dto может быть сериализовано успешно.

 public class QuestionDto { public QuestionDto() { this.Answers = new List(); } public int QuestionId { get; set; } ... ... public string Title { get; set; } public List Answers { get; set; } } 

Что-то вроде:

 public IList GetQuestions(int subTopicId, int questionStatusId) { var questions = _questionsRepository.GetAll() .Where(a => a.SubTopicId == subTopicId && (questionStatusId == 99 || a.QuestionStatusId == questionStatusId)) .Include(a => a.Answers) .ToList(); var dto = questions.Select(x => new QuestionDto { Title = x.Title ... } ); return dto; } 

Вы также можете попробовать это в своем Application_Start() :

 GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize; 

Он должен исправить вашу проблему, не пройдя много обручей.


EDIT: в соответствии с комментарием OttO ниже, вместо этого используйте: ReferenceLoopHandling.Ignore .

 GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; 

Если вы используете OWIN, помните, больше не GlobalSettings для вас! Вы должны изменить этот же параметр в объекте HttpConfiguration, который передается функции IAppBuilder UseWebApi (или любой другой платформы обслуживания, на которой вы находитесь)

Будет выглядеть примерно так.

  public void Configuration(IAppBuilder app) { //auth config, service registration, etc var config = new HttpConfiguration(); config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; //other config settings, dependency injection/resolver settings, etc app.UseWebApi(config); } 

В ядре ASP.NET исправление выглядит следующим образом:

 services .AddMvc() .AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore); 

При использовании DNX / MVC 6 / ASP.NET vNext blah blah даже HttpConfiguration отсутствует. Вы должны настроить конфигураторы, используя следующие коды в файле Startup.cs .

 public void ConfigureServices(IServiceCollection services) { services.AddMvc().Configure(option => { //Clear all existing output formatters option.OutputFormatters.Clear(); var jsonOutputFormatter = new JsonOutputFormatter(); //Set ReferenceLoopHandling jsonOutputFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; //Insert above jsonOutputFormatter as the first formatter, you can insert other formatters. option.OutputFormatters.Insert(0, jsonOutputFormatter); }); } 

Используя это:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore

не работал для меня. Вместо этого я создал новую, упрощенную версию моего модельного classа, просто чтобы проверить, и это вернулось. Эта статья затрагивает некоторые проблемы, которые я испытывал в своей модели, которая отлично поработала для EF, но не была сериализована:

http://www.asp.net/web-api/overview/data/using-web-api-with-entity-framework/part-4

ReferenceLoopHandling.Ignore не работает для меня. Единственный способ, которым я мог обойти это, – удалить с помощью кода ссылки обратно родителям, которых я не хотел, и сохранить те, которые я сделал.

 parent.Child.Parent = null; 

ASP.NET Core Web-API (.NET Core 2.0):

 // Startup.cs public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.Configure(config => { config.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; }); } 

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

Не нужно добавлять дополнительную строку в свой файл конфигурации.

 public class Question { public int QuestionId { get; set; } public string Title { get; set; } public ICollection Answers { get; set; } } public class Answer { public int AnswerId { get; set; } public string Text { get; set; } public int QuestionId { get; set; } public Question Question { get; set; } } 

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

Объекты db = новые объекты ()

db.Configuration.ProxyCreationEnabled = false;

db.Configuration.LazyLoadingEnabled = false;

Для нового веб-приложения Asp.Net с использованием .Net Framework 4.5:

Web Api: Goto App_Start -> WebApiConfig.cs:

Должен выглядеть примерно так:

 public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API configuration and services // Configure Web API to use only bearer token authentication. config.SuppressDefaultHostAuthentication(); config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType)); // ReferenceLoopHandling.Ignore will solve the Self referencing loop detected error config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; //Will serve json as default instead of XML config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html")); // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } } 

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

 public IList GetQuestions(int subTopicId, int questionStatusId) { var questions = _questionsRepository.GetAll() .Where(a => a.SubTopicId == subTopicId && (questionStatusId == 99 || a.QuestionStatusId == questionStatusId)) .Include(a => a.Answers).Select(b=> new { b.QuestionId, b.Title Answers = b.Answers.Select(c=> new { c.AnswerId, c.Text, c.QuestionId })) .ToList(); return questions; } 
  • Обновление EF 4 EDMX до EF 6
  • Json.Net добавляет $ id к объектам EF, несмотря на то, что PreserveReferencesHandling указывает на "None"
  • Каковы минусы отключения ProxyCreationEnabled для CTP5 кода EF
  • Почему структура Entity Framework не может видеть информацию о столбце хранимой процедуры?
  • Код Entity Framework Сначала поддерживаются хранимые процедуры?
  • Interesting Posts
    Давайте будем гением компьютера.