Содержит ссылки на себя петлю обнаружении - возвращается данные WebAPI для браузера
Я использую Entity Framework и возникли проблемы с получением родительских и дочерних данных в браузере. Вот мои занятия:
public class Question
{
public int QuestionId { get; set; }
public string Title { get; set; }
public virtual ICollection<Answer> 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<Question> 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; charset=utf-8'.
петля самоназвания обнаружена для свойства "вопрос" с типом "модели".Ядро.Вопрос".
Это потому, что вопрос имеет ответы и ответы имеют ссылку на вопрос? Все места, которые я искал, предполагают наличие ссылки на родителя в ребенке, поэтому я не уверен, что делать. Может кто-нибудь дать мне несколько советов этот.
13 ответов:
Это потому, что вопрос имеет ответы, а ответы имеют ссылкой на вопрос?
да. Он не может быть сериализован.
EDIT: см. ответ Tallmaris и комментарий Отто, поскольку он проще и может быть установлен глобально.
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
Ответ:
проект объекта EF
Question
для вашего собственного промежуточного или DataTransferObject. Затем этот Dto может быть сериализован успешно.public class QuestionDto { public QuestionDto() { this.Answers = new List<Answer>(); } public int QuestionId { get; set; } ... ... public string Title { get; set; } public List<Answer> Answers { get; set; } }
что-то типа:
public IList<QuestionDto> 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: согласно комментарию Отто ниже, используйте:.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); }
In ASP.NET ядро исправление выглядит следующим образом:
services .AddMvc() .AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
при использовании DNX / MVC 6 / ASP.NET vNext бла-бла, даже
HttpConfiguration
отсутствует. Вы должны конфигурации форматирования с помощью следующих кодов в вашей .public void ConfigureServices(IServiceCollection services) { services.AddMvc().Configure<MvcOptions>(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
не работает для меня. Вместо этого я создал новую, упрощенную версию моего класса модели только для тестирования, и это вернулось нормально. В этой статье рассматриваются некоторые проблемы, которые у меня были в моей модели, которая отлично работала для EF, но не была сериализуемой:
http://www.asp.net/web-api/overview/data/using-web-api-with-entity-framework/part-4
ASP.NET Core Web-API (.NET Core 2.0):
// Startup.cs public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.Configure<MvcJsonOptions>(config => { config.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; }); }
ReferenceLoopHandling.Игнорирование не сработало для меня. Единственный способ обойти это-удалить с помощью кода ссылки обратно к родителю, которого я не хотел, и сохранить те, которые я сделал.
parent.Child.Parent = null;
из-за отложенной загрузки вы получаете эту ошибку. Поэтому мое предложение-удалить виртуальный ключ из свойства. Если вы работаете с API, то ленивая загрузка не подходит для вашего здоровья API.
нет необходимости добавлять дополнительную строку в конфигурационный файл.
public class Question { public int QuestionId { get; set; } public string Title { get; set; } public ICollection<Answer> 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-файл, который определяет концептуальную модель) существующей базы данных, и у него были свойства навигации как к родительской, так и к дочерней таблицам. Я удалил все навигационные ссылки на родительские объекты, а я только хотел перейти к детям, и проблема была решена.
Entities db = new Entities ()
дБ.Конфигурация.ProxyCreationEnabled = false;
дБ.Конфигурация.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<Question> 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; }