web-dev-qa-db-fra.com

Boucle de référence automatique détectée - Récupération des données de Web Api vers le navigateur

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?.

76
user1943020

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.Re‌​ferenceLoopHandling = 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; 
}
67
Sam Leach

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 .


EDIT: Selon le commentaire d'OttO ci-dessous, utilisez: ReferenceLoopHandling.Ignore à la place.

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

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);
}
21
Bon

Dans ASP.NET Core, le correctif est le suivant:

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

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);
        });
    }
5
Stewart Hou

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;
    });
}
4
René Schindhelm

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

2
Mike

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;
1
Rob Sedgwick

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; }
}
0
chakeda

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; }
}
0
PAWAN RAJ Shakya

Entités db = new Entities ()

db.Configuration.ProxyCreationEnabled = false;

db.Configuration.LazyLoadingEnabled = false;

0
Imran

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 }
        );
    }
}
0
Ogglas

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.

0
Zymotik

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; 
    }
0
spadelives