Я пытаюсь написать небольшой веб-API, и я впервые использую инфраструктуру сущностей.

Моя проблема в том, что сохраняются только некоторые из моих данных.

У меня есть этот метод GET, который просто должен сохранить модель в базе данных:

[HttpGet]
public async Task<IActionResult> PopulateDB()
{
    var playerOneQuest = new StatusModel
    {
        PlayerId = "1",
        points= 250,
        completedSteps = new List<Status>()
        {
            new Status
            {
                Id = 11,
                Index = 1
            } 
        }
    };
    
    _context.Quest.Add(playerOneQuest);
    await _context.SaveChangesAsync();

    return Ok("Added data"); // Breakpoint here
}

Когда я добавляю точку останова в последней строке, _context отлично выглядит со всеми данными, но как только метод будет выполнен, completedSteps будет нулевым. Остальные данные — PlayerId и points — сохраняются нормально.

Я пытался добавить этот _context.Entry(playerOneQuest).State = EntityState.Added; перед вызовом SaveChangesAsync(), но completedSteps все еще заканчивается нулевым значением после выполнения метода.

_context определяется как:

public class QuestContext : DbContext
{
    public QuestContext(DbContextOptions<QuestContext> options)
        : base(options){  }

    public virtual DbSet<StatusModel> Quest { get; set; } = null!;
}

Используемые 2 модели определяются как:

public class StatusModel
{
    [Key]
    public string? PlayerId {get;set;}
    public long Points{get;set;}
    public List<Status> completedSteps {get;set;} = null!;
}

public class Status
{
    public long Id {get;set;}
    public int Index {get;set;}
}

И на всякий случай вот как контекст базы данных добавляется в мое веб-приложение:

builder.Services.AddDbContext<QuestContext>(opt =>
opt.UseInMemoryDatabase("Quests"));
2
Jeppe 3 Фев 2022 в 16:37
1
Вы пытались добавить общедоступный виртуальный DbSet Status { get; набор; } в QuestContext?
 – 
sam
3 Фев 2022 в 16:48
Да я пробовал это. Это не имело никакого эффекта, поэтому я снова удалил его.
 – 
Jeppe
3 Фев 2022 в 16:53
На самом деле статус требуется добавить в контекст. Согласно правилам навигации EF, если вы определяете/добавляете дочерние объекты в контекст, то родительский объект будет разрешен самим EF. Итак, я бы добавил Status, и наличие StatusModel не является обязательным в этом сценарии, который мы обсуждаем. Не могли бы вы отредактировать свой вопрос, чтобы просто показать нам ту часть, как навигация определяется в ваших объектах/объектах StatusModel и Status?
 – 
sam
3 Фев 2022 в 17:04
1
Как выглядят ваши сущности Status и StatusModel? Вы настроили свойства навигации, внешние ключи и т. д.? Как определяются ПК - вы действительно можете установить идентификаторы или они генерируются БД?
 – 
Maxim Zabolotskikh
3 Фев 2022 в 17:09
Я читал что-то подобное раньше, но я не знаю, добавлю ли я «StatusModel» в базу данных, если контекст определен с помощью «Status».
 – 
Jeppe
3 Фев 2022 в 17:18

3 ответа

В моделях:

 public class StatusModel
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long Id {get;set;}
        public long Points{get;set;}
        public List<Status> completedSteps {get;set;} = new List<Status>();
    }
    
    public class Status
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long Id {get;set;}
        public int Index {get;set;}
        public long StatusModelId {get;set;}
        public StatusModel {get;set;}
    }

В контексте вашего хранилища в OnModelCreating:

modelBuilder.Entity<Status>().HasOne(c => c.StatusModel).WithMany(c => c.completedSteps)
                    .HasForeignKey(c => c.StatusModelId).OnDelete(DeleteBehavior.Cascade);

Вы должны установить отношения между объектами. Вы можете сделать это в своем контексте с помощью свободного API или с помощью атрибутов непосредственно в моделях. Также в вашем контексте вам нужны оба DbSets, для статуса и для statusModel.

Написал ответ без тестирования, хотя должно быть достаточно близко. Вот ссылка на отношения: Отношения в EF

Я пропустил ваш PlayerId - это, я думаю, FK для Player? Затем вы должны установить это отношение. Если вы планировали его как PK, он, вероятно, не должен быть обнуляемым.

1
Maxim Zabolotskikh 3 Фев 2022 в 17:27
Из того, что я читал в другом месте, а также в вашей ссылке, все эти дополнительные шаги не нужны. Я пытался следовать всем вашим советам, и единственное, что внесло изменения, это «новый список ();» Это привело к тому, что «completedSteps» каждый раз был пустым списком, а не просто нулевым.
 – 
Jeppe
3 Фев 2022 в 17:52

Я думаю, вам следует использовать [HttpPost], а не [HttpGet]

0
Daniel.C. A. 3 Фев 2022 в 16:42
1
Это совсем не актуально и не полезно для меня. Кроме того, я добавляю предопределенные данные, поэтому пост будет не лучше
 – 
Jeppe
3 Фев 2022 в 16:45
3
Это не дает ответа на вопрос. Получив достаточную репутацию, вы сможете /comment">прокомментировать любой пост; вместо этого дайте ответы которые не требуют разъяснений от спрашивающего. – Из обзора
 – 
SWilko
3 Фев 2022 в 17:27

Проблема в том, что вы не указываете, что хотите загрузить дополнительные данные.

https://docs.microsoft.com/en-us/ef/core/querying/related-data/

Вы можете включить ленивую загрузку через прокси: https:/ /docs.microsoft.com/en-us/ef/core/querying/related-data/lazy или явно указать .Include() в запросе.

var completeList = await _questContext.Quest.Include(i => i.completedSteps).ToListAsync();

Насколько я могу судить, ваши сущности правильно настроены, следуя соглашениям. Это означает, что вам не нужно ничего дополнительно настраивать в отношении сущностей, когда вы просто хотите иметь простое отношение «один ко многим».

Пример на контроллере, где вводится QuestContext:

[ApiController]
[Route("[controller]")]
public class MyController : ControllerBase
{
    private readonly QuestContext _questContext;

    public MyController(QuestContext questContext)
    {
        _questContext = questContext;
    }

    [HttpGet]
    public async Task<NoContentResult> Get()
    {
        var playerOneQuest = new StatusModel
        {
            PlayerId = "1",
            Points = 250,
            completedSteps = new List<Status>()
            {
                new Status
                {
                    Id = 11,
                    Index = 1
                }
            }
        };

        _questContext.Quest.Add(playerOneQuest);
        await _questContext.SaveChangesAsync();
        return NoContent();
    }

    [HttpGet("check")]
    public async Task<List<StatusModel>> Check()
    {
        var completeList = await _questContext.Quest.Include(i => i.completedSteps).ToListAsync();
        return completeList;
    }
}
0
321X 3 Фев 2022 в 18:35
Ваше предложение кажется отличным, красивым и простым. Мои единственные 2 проблемы: 1. что в примерах из MS используется «использование (var context = new BloggingContext())», и мой контекст вводится с аргументом DbContextOptions, поэтому я не знаю, как это сделать... и 2. вызов моего _context.Quest обычно не содержит ".Include()". Так что я действительно хочу, чтобы вы были правы, но я не знаю, как это сделать.
 – 
Jeppe
3 Фев 2022 в 18:32
1
Я обновил ответ с помощью контроллера, где QuestContext вводится в конструктор.
 – 
321X
3 Фев 2022 в 18:35
Спасибо. Это буквально то, что у меня уже есть в моем коде. _questContext.Quest просто не имеет .Include(). Я понятия не имею, что изменить, чтобы исправить это
 – 
Jeppe
3 Фев 2022 в 18:39
Что ты имеешь в виду? На квестовом объекте его нет??
 – 
321X
3 Фев 2022 в 18:41