2
votes

API Web ASP.NET Core et modèles EF Core avec relation de clé étrangère

Je développe un projet d'API Web de base à des fins éducatives et j'ai des problèmes avec mes relations avec le modèle EF. J'ai 2 modèles. Message et MessageBoard.

[Route("api/[controller]")]
[ApiController]
public class MessageBoardsController : ControllerBase
{
        private readonly MessageBoardContext _context;

        public MessageBoardsController(MessageBoardContext context)
        {
            _context = context;
        }

        // GET: api/MessageBoards/5
        [HttpGet("{id}")]
        public async Task<ActionResult<MessageBoard>> GetMessageBoard(long id)
        {
            var messageBoard = await _context.MessageBoards.FindAsync(id);

            if (messageBoard == null)
            {
                return NotFound();
            }

            return messageBoard;
        }


        // POST: api/MessageBoards
        [HttpPost]
        public async Task<ActionResult<MessageBoard>> PostMessageBoard(MessageBoard messageBoard)
        {
            _context.MessageBoards.Add(messageBoard);
            await _context.SaveChangesAsync();

            return CreatedAtAction("GetMessageBoard", new { id = messageBoard.Id }, messageBoard);
        }
}


[Route("api/[controller]")]
[ApiController]
public class MessagesController : ControllerBase
{
        private readonly MessageBoardContext _context;

        public MessagesController(MessageBoardContext context)
        {
            _context = context;
        }

        // GET: api/Messages/5
        [HttpGet("{id}")]
        public async Task<ActionResult<Message>> GetMessage(long id)
        {
            var message = await _context.Messages.FindAsync(id);

            if (message == null)
            {
                return NotFound();
            }

            return message;
        }


        // POST: api/Messages
        [HttpPost]
        public async Task<ActionResult<Message>> PostMessage(Message message)
        {
            _context.Messages.Add(message);
            await _context.SaveChangesAsync();

            return CreatedAtAction("GetMessage", new { id = message.Id }, message);
        }
}

J'ai configuré mon DBContext et créé une migration pour configurer la base de données. J'ai généré deux contrôleurs d'API Web à l'aide d'EF Scaffolding. La migration semble détecter correctement la relation entre les deux modèles:

{
    "id": 2,
    "text": "Posting my first message!",
    "user": "Jesse",
    "postedDate": "2019-01-01T00:00:00",
    "messageBoardId": 4,
    "messageBoard": null
}

Mais, lorsque je crée un MessageBoard puis que je crée un Message avec l'ID du MessageBoard, ils ne le font pas. t semblent se lier correctement. Dans PostMan, je fais ce qui suit:

1) Publier un nouveau MessageBoard

POST - https: // localhost: 44384 / api / MessageBoards /

Body - Raw - Json

{
    "Text":"Posting my first message!",
    "User":"Jesse",
    "PostedDate":"1/1/2019",

    "MessageBoardId":4
}

Renvoie

{
    "id": 4,
    "name": "Test Board",
    "description": "A Message board for testing purposes.",
    "messages": null
}

2) Publier un nouveau message

POST - https: // localhost: 44384 / api / Messages

Body - Raw - JSON code>

{
 "Name":"Test Board",
 "Description":"A Message board for testing purposes."
}

Renvoie

modelBuilder.Entity("Asp.net_Core_Web_Api.Models.Message", b =>
{
   b.HasOne("Asp.net_Core_Web_Api.Models.MessageBoard", "MessageBoard")
   .WithMany("Messages")
   .HasForeignKey("MessageBoardId")
   .OnDelete(DeleteBehavior.Cascade);
});

Je m'attendrais à ce que le messageBoard ne soit pas nul, et qu'il renvoie plutôt le JSON pour le messageBoard qui a été créé précédemment. Si je passe à une méthode GET, elle est également nulle. Pourquoi est-il nul?

EDIT: Voici mes contrôleurs. J'ai supprimé des actions à l'exception de GET et POST.

public class Message
    {
        public long Id { get; set; }
        public string Text { get; set; }
        public string User { get; set; }
        public DateTime PostedDate { get; set; }

        public long MessageBoardId { get; set; }
        [ForeignKey("MessageBoardId")]
        public MessageBoard MessageBoard { get; set; }
    }

public class MessageBoard
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }

        public ICollection<Message> Messages { get; set; }
    }


3 commentaires

Veuillez publier le code de vos méthodes d'action GET / PUT (où vous interrogez la base de données)


Sûr. Il est inchangé par rapport à l'échafaudage, donc je ne pensais pas que ce serait super utile. Une seconde. EDIT: ajouté.


@Jack Vérifiez votre question et ce que vous avez accepté comme réponse. Avez-vous posé la question correctement. Si c'est le cas, la réponse acceptée n'est certainement pas la solution exacte à votre question. Soit dit en passant, lisez ma réponse puis donnez votre avis.


3 Réponses :


0
votes

Vous avez donné la sortie de l'application et à quoi ressemble la base de données, mais pas le bit du milieu sur la façon dont elle enregistre / récupère les données. Sans savoir ce qui se passe au milieu, mon meilleur coup dans l'obscurité est que vous n'avez ni défini correctement votre chargement différé ni utilisé Inclure pour inclure l'entité MessageBoard . < / p>

Plus d'informations ici sur ce qu'ils sont .


0 commentaires

5
votes

Vous devez charger les données associées

Par exemple, pour votre MessageBoard GET - // GET: api / MessageBoards / 5

Changer de:

var messageBoard = await _context.MessageBoards
                        .Include(i=>i.Messages)
                        .FirstOrDefaultAsync(i => i.Id == id);

À

var messageBoard = await _context.MessageBoards.FindAsync(id);


3 commentaires

C'était ça. Je suis habitué à EF6 Database First où cela est automatiquement configuré, donc je n'ai pas pensé à le faire sur EF Core. J'ai tout mis à jour pour utiliser Eager Loading et cela a été corrigé. Je vous remercie!


@Jack pas de problème, veuillez marquer comme réponse si cela a aidé


J'attends juste que le chronomètre soit écoulé pour que je puisse le marquer :)



2
votes

Je m'attendrais à ce que le messageBoard ne soit pas nul, et qu'il renvoie plutôt le JSON pour le messageBoard qui a été créé précédemment. Si je passe à une méthode GET, elle est également nulle. Pourquoi est-il nul?

Ceci est dû au fait que vous renvoyez le message nouvellement créé, ici seul MessageBoadId existe, pas l'objet MessageBoad . Vous devez donc charger le MessageBoad associé à partir de la base de données en utilisant Inclure pour le message nouvellement créé.

Votre méthode PostMessage doit être la suivante:

// POST: api/Messages
[HttpPost]
public async Task<ActionResult<Message>> PostMessage(Message message)
{
    _context.Messages.Add(message);
    await _context.SaveChangesAsync();

    var message = await _context.Messages
                    .Include(i=>i.MessageBoard)
                    .FirstOrDefaultAsync(i => i.Id == message.Id);

    return Json(message);
}

0 commentaires