J'utilise Entity Framework et je ne parviens pas à obtenir les données parent et enfant dans le navigateur. Voici mes cours:
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; }
}
J'utilise le code suivant pour renvoyer la question et les données de réponse:
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;
}
Du côté C #, cela semble fonctionner, mais je remarque que les objets de réponse ont des références à la question. Lorsque j'utilise WebAPI pour transférer les données vers le navigateur, le message suivant s'affiche:
Le type 'ObjectContent`1' n'a pas pu sérialiser le corps de la réponse pour le type de contenu 'application/json; jeu de caractères = utf-8 '.
Boucle d'auto-référencement détectée pour la propriété 'question' avec le type 'Models.Core.Question'.
Est-ce parce que la question a des réponses et que les réponses ont une référence à la question? Tous les endroits que j'ai consultés suggèrent d'avoir une référence au parent de l'enfant, alors je ne sais pas quoi faire. Quelqu'un peut-il me donner des conseils à ce sujet?.
Est-ce parce que la question a des réponses et que les réponses ont un référence à la question?
Oui. Il ne peut pas être sérialisé.
EDIT: Voir la réponse de Tallmaris et le commentaire d'OttO car il est plus simple et peut être réglé globalement
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
Ancienne réponse:
Projetez l'objet EF Question
sur votre propre intermédiaire ou DataTransferObject. Ce Dto peut alors être sérialisé avec succès.
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; }
}
Quelque chose comme:
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;
}
Vous pouvez aussi essayer ceci dans votre Application_Start()
:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
Il convient de résoudre votre problème sans passer par beaucoup de cerceaux .
ReferenceLoopHandling.Ignore
à la place.GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
Si vous utilisez OWIN, rappelez-vous, plus de GlobalSettings à votre place! Vous devez modifier ce même paramètre dans un objet HttpConfiguration qui est transmis à la fonction IAppBuilder UseWebApi (ou à la plate-forme de service sur laquelle vous êtes).
Ressemblerait à quelque chose comme ça.
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);
}
Dans ASP.NET Core, le correctif est le suivant:
services
.AddMvc()
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
Si vous utilisez DNX/MVC 6/ASP.NET vNext blah blah, même la variable HttpConfiguration
est manquante. Vous devez configurer les formateurs en utilisant les codes suivants dans votre fichier Startup.cs
.
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);
});
}
API Web ASP.NET Core (.NET Core 2.0):
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.Configure<MvcJsonOptions>(config =>
{
config.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
}
En utilisant ceci:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore
n'a pas fonctionné pour moi. Au lieu de cela, j'ai créé une nouvelle version simplifiée de ma classe de modèles, juste pour tester, et cela a bien fonctionné. Cet article traite de certains problèmes que je rencontrais dans mon modèle et qui fonctionnaient bien pour EF, mais n'étaient pas sérialisables:
http://www.asp.net/web-api/overview/data/using-web-api-with-entity-framework/part-4
ReferenceLoopHandling.Ignore n'a pas fonctionné pour moi. La seule façon de contourner ce problème était de supprimer par le code les liens renvoyant au parent que je ne voulais pas et de conserver ceux que je gardais.
parent.Child.Parent = null;
Aucune des configurations dans les réponses ci-dessus n'a fonctionné pour moi dans ASP.NET Core 2.2.
J'avais l'ajout des attributs JsonIgnore
sur mes propriétés de navigation virtuelle.
public class Question
{
public int QuestionId { get; set; }
public string Title { get; set; }
[JsonIgnore]
public virtual ICollection<Answer> Answers { get; set; }
}
En raison du chargement paresseux, vous obtenez cette erreur. Par conséquent, ma suggestion est de supprimer la clé virtuelle de la propriété. Si vous travaillez avec une API, le chargement différé n'est pas bon pour la santé de votre API.
Pas besoin d'ajouter de ligne supplémentaire dans votre fichier de configuration.
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; }
}
Entités db = new Entities ()
db.Configuration.ProxyCreationEnabled = false;
db.Configuration.LazyLoadingEnabled = false;
Pour une nouvelle application Web Asp.Net utilisant .Net Framework 4.5:
Web Api: Allez à App_Start -> WebApiConfig.cs:
Devrait ressembler à ceci:
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 }
);
}
}
J'ai constaté que cette erreur était provoquée lorsque j'ai généré un edmx (fichier XML qui définit un modèle conceptuel) d'une base de données existante et que celle-ci possédait des propriétés de navigation pour les tables parent et enfant. J'ai supprimé tous les liens de navigation vers les objets parents, car je voulais uniquement naviguer vers les enfants et le problème a été résolu.
Vous pouvez créer dynamiquement une nouvelle collection enfant pour contourner facilement ce problème.
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;
}