Używam Entity Framework i mam problem z pobieraniem danych rodziców i dzieci do przeglądarki. Oto moje zajęcia:
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; }
}
Używam następującego kodu, aby zwrócić dane pytania i odpowiedzi:
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;
}
Po stronie C # wydaje się to działać, jednak zauważam, że obiekty odpowiedzi mają odniesienia z powrotem do pytania. Kiedy korzystam z WebAPI, aby pobrać dane do przeglądarki, otrzymuję następujący komunikat:
Typowi „ObjectContent” 1 nie udało się serializować treści odpowiedzi dla typu zawartości „application / json”; charset = utf-8 '.
Wykryto pętlę samoodniesień dla właściwości „pytanie” o typie „Models.Core.Question”.
Czy to dlatego, że pytanie ma odpowiedzi, a odpowiedzi mają odniesienie z powrotem do pytania? Wszystkie miejsca, które szukałem, sugerują nawiązanie do rodzica w dziecku, więc nie jestem pewien, co robić. Czy ktoś może mi doradzić w tej sprawie.
źródło
Odpowiedzi:
Tak. Nie można go serializować.
EDYCJA: Zobacz odpowiedź Tallmarisa i komentarz OttO, ponieważ jest prostszy i można go ustawić globalnie.
Stara odpowiedź:
Projektuj obiekt EF
Question
na swój własny obiekt pośredni lub DataTransferObject. To Dto można następnie pomyślnie serializować.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; } }
Coś jak:
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; }
źródło
Możesz również spróbować tego w
Application_Start()
:Powinien rozwiązać problem bez przechodzenia przez wiele obręczy.
EDYCJA: Zgodnie z komentarzem OttO poniżej, użyj:
ReferenceLoopHandling.Ignore
zamiast.źródło
Create()
metody statycznej , która akceptuje ustawienie jako parametr. Dokumenty: newtonsoft.com/json/help/html/…Jeśli korzystasz z OWIN, pamiętaj, nigdy więcej GlobalSettings dla Ciebie! Musisz zmodyfikować to samo ustawienie w obiekcie HttpConfiguration, który jest przekazywany do funkcji UseWebApi IAppBuilder (lub dowolnej platformy usługowej, na której jesteś)
Wyglądałoby to mniej więcej tak.
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); }
źródło
W ASP.NET Core poprawka jest następująca:
źródło
Jeśli używasz DNX / MVC 6 / ASP.NET vNext bla bla,
HttpConfiguration
brakuje nawet . Musisz skonfigurować elementy formatujące, używając następujących kodów wStartup.cs
pliku.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); }); }
źródło
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; }); }
źródło
Używając tego:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore
nie działa dla mnie. Zamiast tego utworzyłem nową, uproszczoną wersję mojej klasy modelu tylko do testowania i to wróciło dobrze. Ten artykuł dotyczy niektórych problemów, które miałem w moim modelu, które działały świetnie dla EF, ale nie można ich serializować:
http://www.asp.net/web-api/overview/data/using-web-api-with-entity-framework/part-4
źródło
ReferenceLoopHandling.Ignore nie działa dla mnie. Jedynym sposobem, w jaki mogłem to obejść, było usunięcie za pomocą kodu linków z powrotem do rodzica, którego nie chciałem, i zachowanie tych, które zrobiłem.
parent.Child.Parent = null;
źródło
W przypadku nowej aplikacji sieci Web Asp.Net korzystającej z .Net Framework 4.5:
Web Api: Idź do App_Start -> WebApiConfig.cs:
Powinien wyglądać mniej więcej tak:
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 } ); } }
źródło
W ramach ASP.NET Core 3,0 zespół odszedł od domyślnego uwzględniania Json.NET. Możesz przeczytać więcej na ten temat ogólnie w [Includes Json.Net to netcore 3.x] [1] https://github.com/aspnet/Announcements/issues/325
Błąd mógł być spowodowany przez użycie lazyloading: services.AddDbContext (options => options.UseLazyLoadingProxies () ... lub db.Configuration.LazyLoadingEnabled = true;
poprawka: dodaj do startup.cs
źródło
Z powodu leniwego ładowania otrzymujesz ten błąd. Stąd moja sugestia to usunięcie klucza wirtualnego z właściwości. Jeśli pracujesz z interfejsem API, ładowanie z opóźnieniem nie jest dobre dla stanu interfejsu API.
Nie ma potrzeby dodawania dodatkowej linii w pliku konfiguracyjnym.
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; } }
źródło
Zauważyłem, że ten błąd był spowodowany, gdy wygenerowałem edmx (plik XML, który definiuje model koncepcyjny) istniejącej bazy danych i miał on właściwości nawigacji zarówno dla tabel nadrzędnych, jak i podrzędnych. Usunąłem wszystkie łącza nawigacyjne do obiektów nadrzędnych, ponieważ chciałem tylko nawigować do dzieci, a problem został rozwiązany.
źródło
Jednostki db = nowe jednostki ()
db.Configuration.ProxyCreationEnabled = false;
db.Configuration.LazyLoadingEnabled = false;
źródło
Możesz dynamicznie utworzyć nową kolekcję podrzędną, aby łatwo obejść ten problem.
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; }
źródło
Żadna z konfiguracji w powyższych odpowiedziach nie działała dla mnie w ASP.NET Core 2.2.
Musiałem dodać
JsonIgnore
atrybuty do moich właściwości nawigacji wirtualnej.public class Question { public int QuestionId { get; set; } public string Title { get; set; } [JsonIgnore] public virtual ICollection<Answer> Answers { get; set; } }
źródło