web-dev-qa-db-fra.com

L'API ASP.NET Core ne renvoie que le premier résultat de la liste

J'ai créé un contrôleur d'API Web d'équipes et j'essaie d'appeler la méthode GET pour obtenir le résultat JSON de toutes les équipes de la base de données. Mais quand je fais l'appel, je ne récupère que la première équipe dans le json mais quand je fixe un point d'arrêt sur la déclaration de retour, elle compte les 254 équipes ainsi que tous les matchs.

Ce sont les deux modèles que je traite:

public class Team
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Icon { get; set; }
    public string Mascot { get; set; }
    public string Conference { get; set; }
    public int NationalRank { get; set; }

    public List<Game> Games { get; set; }
}

public class Game
{
    public string Id { get; set; }
    public string Opponent { get; set; }
    public string OpponentLogo { get; set; }
    public string GameDate { get; set; }
    public string GameTime { get; set; }
    public string TvNetwork { get; set; }
    public string TeamId { get; set; }

    public Team Team { get; set; }
}

Quand je fais ça:

[HttpGet]
public async Task<List<Team>> Get()
{
    var teams = await _context.Teams.ToListAsync();

    return teams;
}

Je reçois les 254 équipes, mais la propriété du jeu est nulle car EF Core ne prend pas en charge le chargement paresseux. Donc, ce que je veux vraiment faire, c'est ajouter le .Include () comme ceci:

[HttpGet]
public async Task<List<Team>> Get()
{
    var teams = await _context.Teams.Include(t => t.Games).ToListAsync();

    return teams;
}

Cela renvoie la première équipe avec le premier match mais rien d'autre. Voici le json:

[
  {
    "id": "007b4f09-d4da-4040-be3a-8e45fc0a572b",
    "name": "New Mexico",
    "icon": "lobos.jpg",
    "mascot": "New Mexico Lobos",
    "conference": "MW - Mountain",
    "nationalRank": null,
    "games": [
      {
        "id": "1198e6b1-e8ab-48ab-a63f-e86421126361",
        "opponent": "vs Air Force*",
        "opponentLogo": "falcons.jpg",
        "gameDate": "Sat, Oct 15",
        "gameTime": "TBD ",
        "tvNetwork": null,
        "teamId": "007b4f09-d4da-4040-be3a-8e45fc0a572b"
      }
    ]
  }
]

Lorsque je fixe un point d'arrêt sur la déclaration de retour, cela montre qu'il y a 254 équipes et que chaque équipe a ses jeux correctement renseignés ... mais le résultat json ne se reflète pas. Voici une image:

enter image description here

J'ai essayé de le faire de manière synchrone et asynchrone, mais en obtenant le même résultat. Savez-vous pourquoi je n'obtiens qu'un seul résultat dans le json mais au point d'arrêt, il a tous les résultats?

33
Aaron

Essaye ça:

services.AddMvc().AddJsonOptions(options => {
            options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
        });

Le problème a été discuté https://github.com/aspnet/Mvc/issues/416 et https://github.com/aspnet/EntityFramework/issues/4646 également voir référence circulaire

50
adem caglin

Il convient de noter que si vous contrôlez la sortie json comme avec l'option inline JsonSerializerSettings,

[HttpGet]
public async Task<IActionResult> Get([FromForm]bool strip_nulls = true)
{
    var teams = await _context.Teams.Include(t => t.Games).ToListAsync();

    return Json(teams, new JsonSerializerSettings() { 
         NullValueHandling = strip_nulls ? NullValueHandling.Ignore : NullValueHandling.Include
    });
}

Mettre simplement la solution suggérée de @ adeam-caglin, qui n'est pas erronée dans l'approche, ne fonctionnera pas. Vous devez également définir le paramètre dans votre retour. Par exemple.

[HttpGet]
public async Task<IActionResult> Get([FromForm]bool strip_nulls = true)
{
    var teams = await _context.Teams.Include(t => t.Games).ToListAsync();

    return Json(teams, new JsonSerializerSettings() { 
         NullValueHandling = strip_nulls ? NullValueHandling.Ignore : NullValueHandling.Include,
         ReferenceLoopHandling = ReferenceLoopHandling.Ignore
    });
}

Il annule essentiellement les paramètres que vous définissez sur le Startup.cs. Cela vous donne également une feuille de route pour ne pas modifier globalement votre sortie mais le faire au cas par cas.

ÉDITER

Je voudrais également prendre un moment et clarifier ce qui se passe lorsque vous utilisez ReferenceLoopHandling.Ignore, Vous demandez à boire du tuyau d'incendie mais en espérant que ce sera un débit contrôlé. Si vous avez une modélisation très développée, vous aurez plus que probablement un ensemble où vous pensez que vous allez obtenir votre entité prévue et sa liste d'enfants, mais si ces éléments de liste ont également des enfants ou d'autres parents, vous les chargerez. Disons que vous avez

Teams>Players>Contacts
Games>Teams

Cela produirait un sacré retour imbriqué json. J'aurais voulu un appartement Game>Teams Mais j'aurais fini avec Games>Teams>Players. Il s'agit d'un exemple simple, mais il est facile de voir comment passer de quelques Ko de données à une boucle sans fin qui étouffe le client qui consomme les résultats.

Cela signifie que vous devrez contrôler ce flux vous-même. Pour obtenir ce retour json plus plat, vous devrez également incorporer .AsNoTracking() sur .Include(x => x.Games)

À titre d'exemple très simple, vous devez faire quelque chose comme:

[HttpGet]
public async Task<IActionResult> Get([FromForm]bool strip_nulls = true)
{
    var teams = _context.Teams.AsQueryable();
    teams = teams.Include(t => t.Games).AsNoTracking();
    Teams _return = await teams.ToListAsync();
    return Json(_return, new JsonSerializerSettings() { 
         NullValueHandling = strip_nulls ? NullValueHandling.Ignore : NullValueHandling.Include,
         ReferenceLoopHandling = ReferenceLoopHandling.Ignore
    });
}
1
jeremy.bass