Внешние ключи, активная загрузка и DTO (. NET Core 3.1 / Entity Framework Core) - PullRequest
0 голосов
/ 19 февраля 2020

Context

Я создаю simple . NET Core API и у меня возникли проблемы с возвратом правильных данных из моих контроллеров. Я предвидел необходимость использовать DTO для управления тем, какие данные отправляются в и из API, но я не уверен, что мои отношения EF Core настроены правильно. Для всего oop меня бросает то, что дочерние объекты в моих моделях возвращают ноль - я объясню дальше. У меня есть две модели домена: Player и Match . В каждом матче четыре игрока, и я думаю, что для одного и того же стола потребуется создать четыре внешних ключа. База данных SQL, которую генерирует EF Core, выглядит так, как я ее представлял - я включил снимок экрана для большего контекста. Я могу просто создавать объекты Player, используя метод API POST. Однако когда я создаю объект Match, используя Player Guids (как показано ниже), запрос вновь созданного объекта Match из базы данных не возвращает объект Player в JSON, как я надеялся; MatchController возвращает Руководства игрока, но без информации об игроке. В конечном счете, я хотел бы последовательно отображать данные об игроках для каждого из четырех игроков в матче , но я не уверен, какие изменения в моих моделях или Fluent API потребовались бы для меня, чтобы добиться этого. Я планирую использовать AutoMapper для сопоставления объектов модели домена с объектами модели DTO в будущем, но это текущее затруднение кажется чем-то, что я должен сгладить в первую очередь. Я был бы более чем рад предоставить больше информации, если есть что-то еще, что позволит лучше помочь! Любая помощь будет искренне и высоко ценится! Заранее спасибо!

Примечание. Меня не волнует наличие коллекции соответствий в моей модели проигрывателя - я включил ее только потому, что считал необходимым создать ограничения внешнего ключа в Entity Framework Core

Модели:

Player.cs (модель домена)

public class Player
{
    [Key]
    public Guid Id { get; set; }

    [Required]
    public string Name { get; set; }

    public double Rating { get; set; }

    public DateTime Created { get; set; }
    public DateTime Updated { get; set; }

    public ICollection<Match> MatchesOne { get; set; }
    public ICollection<Match> MatchesTwo { get; set; }
    public ICollection<Match> MatchesThree { get; set; }
    public ICollection<Match> MatchesFour { get; set; }
}

Match.cs (модель домена)

public class Match
{
    [Key]
    public Guid Id { get; set; }

    [Required]
    public int TeamOneScore { get; set; }
    [Required]
    public int TeamTwoScore { get; set; }

    public DateTime Created { get; set; }
    public DateTime Updated { get; set; }

    public Guid PlayerOneId { get; set; }
    public Guid PlayerTwoId { get; set; }
    public Guid PlayerThreeId { get; set; }
    public Guid PlayerFourId { get; set; }

    public Player PlayerOne { get; set; }
    public Player PlayerTwo { get; set; }
    public Player PlayerThree { get; set; }
    public Player PlayerFour { get; set; }
}

ApplicationDbContext.cs (Свободный API)

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Player>()
        .Property(p => p.Rating)
        .HasDefaultValue(1000);

    modelBuilder.Entity<Player>()
        .Property(p => p.Created)
        .HasDefaultValueSql("GETDATE()");

    modelBuilder.Entity<Player>()
        .Property(p => p.Updated)
        .HasDefaultValueSql("GETDATE()");

    modelBuilder.Entity<Match>()
        .Property(m => m.Created)
        .HasDefaultValueSql("GETDATE()");

    modelBuilder.Entity<Match>()
        .Property(m => m.Updated)
        .HasDefaultValueSql("GETDATE()");

    modelBuilder.Entity<Player>()
        .HasMany(p => p.MatchesOne)
        .WithOne(m => m.PlayerOne)
        .HasForeignKey(m => m.PlayerOneId)
        .OnDelete(DeleteBehavior.NoAction);

    modelBuilder.Entity<Player>()
        .HasMany(p => p.MatchesTwo)
        .WithOne(m => m.PlayerTwo)
        .HasForeignKey(m => m.PlayerTwoId)
        .OnDelete(DeleteBehavior.NoAction);

    modelBuilder.Entity<Player>()
        .HasMany(p => p.MatchesThree)
        .WithOne(m => m.PlayerThree)
        .HasForeignKey(m => m.PlayerThreeId)
        .OnDelete(DeleteBehavior.NoAction);

    modelBuilder.Entity<Player>()
        .HasMany(p => p.MatchesFour)
        .WithOne(m => m.PlayerFour)
        .HasForeignKey(m => m.PlayerFourId)
        .OnDelete(DeleteBehavior.NoAction);
}

Контроллеры:

PlayerController.cs

[HttpGet]
public async Task<ActionResult<IEnumerable<Player>>> GetPlayers()
{
    return await _context.Players.ToListAsync();
}

[HttpPost]
public async Task<ActionResult<Player>> PostPlayer(Player player)
{
    _context.Players.Add(player);
    await _context.SaveChangesAsync();

    return CreatedAtAction(nameof(GetPlayer), new { id = player.Id }, player);
}

MatchController.cs

[HttpGet]
public async Task<ActionResult<IEnumerable<Match>>> GetMatches()
{
    return await _context.Matches.ToListAsync();
}

[HttpPost]
public async Task<ActionResult<Match>> PostMatch(Match match)
{
    _context.Matches.Add(match);
    await _context.SaveChangesAsync();

    return CreatedAtAction(nameof(GetMatch), new { id = match.Id }, match);
}

HTTP-запросы

HTTP POST: PostPlayer (/ api / Player) Запрос:

{
    "Name":"Mike Blart"
}

Ответ:

{
    "id": "d3c022a2-d347-4a9a-d3ec-08d7b5480646",
    "name": "Mike Blart",
    "rating": 1000,
    "created": "2020-02-19T14:32:48.8033333",
    "updated": "2020-02-19T14:32:48.8033333",
    "matchesOne": null,
    "matchesTwo": null,
    "matchesThree": null,
    "matchesFour": null
}

HTTP POST: PostMatch (/ api / Match) Запрос:

{
    "TeamOneScore":21,
    "TeamTwoScore":13,
    "PlayerOneId":"0589867f-590b-4344-d3e9-08d7b5480646",
    "PlayerTwoId":"0f45247b-1fdb-404a-d3ea-08d7b5480646",
    "PlayerThreeId":"f8b4e13d-0dd0-4ef5-d3eb-08d7b5480646",
    "PlayerFourId":"d3c022a2-d347-4a9a-d3ec-08d7b5480646"
}

Ответ:

{
    "id": "dfdc2f23-0786-40df-8aa7-08d7b54fd4a1",
    "teamOneScore": 21,
    "teamTwoScore": 13,
    "created": "2020-02-19T15:24:38.7233333",
    "updated": "2020-02-19T15:24:38.7233333",
    "playerOneId": "0589867f-590b-4344-d3e9-08d7b5480646",
    "playerTwoId": "0f45247b-1fdb-404a-d3ea-08d7b5480646",
    "playerThreeId": "f8b4e13d-0dd0-4ef5-d3eb-08d7b5480646",
    "playerFourId": "d3c022a2-d347-4a9a-d3ec-08d7b5480646",
    "playerOne": null,
    "playerTwo": null,
    "playerThree": null,
    "playerFour": null
}

HTTP GET: GetPlayers (/ api / Player)

[
    {
        "id": "0589867f-590b-4344-d3e9-08d7b5480646",
        "name": "Merwin Dedrick",
        "rating": 1000,
        "created": "2020-02-19T14:28:44.7966667",
        "updated": "2020-02-19T14:28:44.7966667",
        "matchesOne": null,
        "matchesTwo": null,
        "matchesThree": null,
        "matchesFour": null
    },
    {
        "id": "0f45247b-1fdb-404a-d3ea-08d7b5480646",
        "name": "Omar Rupaz",
        "rating": 1000,
        "created": "2020-02-19T14:30:04.4933333",
        "updated": "2020-02-19T14:30:04.4933333",
        "matchesOne": null,
        "matchesTwo": null,
        "matchesThree": null,
        "matchesFour": null
    },
    {
        "id": "f8b4e13d-0dd0-4ef5-d3eb-08d7b5480646",
        "name": "Aaron Randolph",
        "rating": 1000,
        "created": "2020-02-19T14:32:38.7066667",
        "updated": "2020-02-19T14:32:38.7066667",
        "matchesOne": null,
        "matchesTwo": null,
        "matchesThree": null,
        "matchesFour": null
    },
    {
        "id": "d3c022a2-d347-4a9a-d3ec-08d7b5480646",
        "name": "Mike Blart",
        "rating": 1000,
        "created": "2020-02-19T14:32:48.8033333",
        "updated": "2020-02-19T14:32:48.8033333",
        "matchesOne": null,
        "matchesTwo": null,
        "matchesThree": null,
        "matchesFour": null
    }
]

HTTP GET: GetMatches (/ api / Match)

[
    {
        "id": "ce06237b-a137-47bc-e0b7-08d7b5484c68",
        "teamOneScore": 21,
        "teamTwoScore": 13,
        "created": "2020-02-19T14:33:24.1266667",
        "updated": "2020-02-19T14:33:24.1266667",
        "playerOneId": "0589867f-590b-4344-d3e9-08d7b5480646",
        "playerTwoId": "0f45247b-1fdb-404a-d3ea-08d7b5480646",
        "playerThreeId": "f8b4e13d-0dd0-4ef5-d3eb-08d7b5480646",
        "playerFourId": "d3c022a2-d347-4a9a-d3ec-08d7b5480646",
        "playerOne": null,
        "playerTwo": null,
        "playerThree": null,
        "playerFour": null
    }
]

1 Ответ

1 голос
/ 20 февраля 2020

Для отображения четырех players' информации при отображении одного match можно использовать Include:

[HttpGet]
public async Task<ActionResult<IEnumerable<Match>>> GetMatches()
{
    return await _context.Matches.ToListAsync();
}

// GET: api/Matches/5
[HttpGet("{id}")]
public async Task<ActionResult<Match>> GetMatch(Guid id)
{
    var match = await _context.Matches
                    .Include(m => m.PlayerOne)
                    .Include(m => m.PlayerTwo)
                    .Include(m => m.PlayerThree)
                    .Include(m => m.PlayerFour)
                    .Where(m => m.Id == id)
                    .FirstOrDefaultAsync();       
    return match;
}

[HttpPost]
public async Task<ActionResult<Match>> PostMatch(Match match)
{
    _context.Matches.Add(match);
    await _context.SaveChangesAsync();

    //change CreatedAtAction to CreatedAtAction...
    return RedirectToAction("GetMatch", new { id = match.Id }); 
}

Результат:

{
    "id": "e35bff4e-1d3a-40db-6da7-08d7b5c7defc",
    "teamOneScore": 21,
    "teamTwoScore": 13,
    "created": "2020-02-20T13:43:54.1066667",
    "updated": "2020-02-20T13:43:54.1066667",
    "playerOneId": "6a35cfd0-b55f-4151-a75f-08d7b5c79d60",
    "playerTwoId": "caa71d4a-1fe5-488a-a760-08d7b5c79d60",
    "playerThreeId": "4c057f8c-9a05-4e33-a761-08d7b5c79d60",
    "playerFourId": "4dd619f2-1680-4bb6-a762-08d7b5c79d60",
    "playerOne": {
        "id": "6a35cfd0-b55f-4151-a75f-08d7b5c79d60",
        "name": "Mike Blart",
        "rating": 1000.0,
        "created": "2020-02-20T13:42:05.94",
        "updated": "2020-02-20T13:42:05.94",
        "matchesOne": [],
        "matchesTwo": null,
        "matchesThree": null,
        "matchesFour": null
    },
    "playerTwo": {
        "id": "caa71d4a-1fe5-488a-a760-08d7b5c79d60",
        "name": "aaa",
        "rating": 1000.0,
        "created": "2020-02-20T13:42:24.3866667",
        "updated": "2020-02-20T13:42:24.3866667",
        "matchesOne": null,
        "matchesTwo": [],
        "matchesThree": null,
        "matchesFour": null
    },
    "playerThree": {
        "id": "4c057f8c-9a05-4e33-a761-08d7b5c79d60",
        "name": "vvv",
        "rating": 1000.0,
        "created": "2020-02-20T13:42:28.83",
        "updated": "2020-02-20T13:42:28.83",
        "matchesOne": null,
        "matchesTwo": null,
        "matchesThree": [],
        "matchesFour": null
    },
    "playerFour": {
        "id": "4dd619f2-1680-4bb6-a762-08d7b5c79d60",
        "name": "ccc",
        "rating": 1000.0,
        "created": "2020-02-20T13:42:32.86",
        "updated": "2020-02-20T13:42:32.86",
        "matchesOne": null,
        "matchesTwo": null,
        "matchesThree": null,
        "matchesFour": []
    }
}

Примечание : обязательно установите Microsoft.AspNetCore.Mvc.NewtonsoftJson, затем используйте следующий код:

services.AddControllers().AddNewtonsoftJson(x =>
              x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);

Ссылка: Загрузка связанных данных

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...