EF Core 2.0 «Каскадные» вставки для связанных объектов при обновлении основного объекта - PullRequest
0 голосов
/ 26 июня 2018

ASP.NET Core 2 Веб-приложение, использующее REST API. В настоящее время используется sqlite3 для разработки базы данных. (Также попытался перейти на SQL Server и получил те же результаты, что и ниже).

Я отправляю сущность веб-клиенту, клиент вносит изменения в сущность, которые включают добавление новой связанной сущности, и затем эта обновленная принципиальная сущность отправляется обратно в виде json в теле запроса PUT.

Я надеялся, что новая связанная сущность будет создана автоматически, но этого не происходит. Простые свойства основного объекта обновляются должным образом, но не ссылочные свойства. Я не получаю никаких исключений или чего-то еще - просто кажется, что игнорируются ссылочные свойства.

Упрощенные классы (я удалил другие свойства, которые не должны влиять на отношения):

public partial class DashboardItem {
    public int Id { get; set; }
    public int? DataObjectId { get; set; }
    public DataObject DataObject { get; set; }

}

public partial class DataObject {
    public int Id { get; set; }
}

Часть DbContext Fluent API для связанного свойства:

        modelBuilder.Entity<DashboardItem>(entity => {
            entity.HasOne(p => p.DataObject)
            .WithMany()
            .HasForeignKey(p => p.DataObjectId);
        });

Метод контроллера для PUT:

    [HttpPut("{id}")]
    public async Task<IActionResult> PutDashboardItem([FromRoute] int id, [FromBody] DashboardItem entity)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        if (id != entity.Id)
        {
            return BadRequest();
        }

        _context.Entry(entity).State = EntityState.Modified;

        try{
            await _context.SaveChangesAsync();
        }catch (DbUpdateConcurrencyException)
        {
            if (!DashboardItemExists(id)){
                return NotFound();
            }else {
                throw;
            }
        }
        return NoContent();
    }

Упрощенный json (без всех других свойств) будет выглядеть следующим образом (я пробовал разные варианты удаления внешнего ключа «DataObjectId» из json, установки в ноль или в ноль в случае, если это может быть interferring).

{
  Id:1,
  DataObjectId:null,
  DataObject:{
    Id: 0
  }
}

При отладке в методе действия контроллера существующий объект принципа «DashboardItem», созданный из тела запроса, имеет ссылочное свойство «DataObject», заполняемое перед добавлением в DbContext, но новый DataObject никогда не создается в базе данных. Существует только инструкция SQL UPDATE для DashboardItem и нет INSERT для DataObject.

Я также пытался сделать метод контроллера синхронным, а не асинхронным, используя DbContext.SaveChanges () вместо .SaveChangesAsync (), поскольку раньше была проблемой с этим в более ранних версиях EF Ядро связано с созданием связанных сущностей, хотя я использую 2.0, в которой уже есть исправление. Тот же результат.

Этот EFCore Doc звучит так, как будто он должен работать из коробки.

Это сработало для меня в предыдущем проекте. Что мне здесь не хватает?

1 Ответ

0 голосов
/ 27 июня 2018

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

После того, как вырыли намного больше, кажется, что следующая строка в моем методе контроллера для обработки запроса PUT является проблемой:

_context.Entry(entity).State = EntityState.Modified;

Установка состояния входа объекта в значение Modified таким образом приводит к тому, что Entity Framework Core игнорирует ссылочные свойства для связанных объектов - сгенерированный SQL UPDATE будет адресован только столбцам в таблице объектов.

Это простое резюме в конечном итоге привело меня к правильному пути.


Подводя итог тому, что я сейчас узнал:

Этот метод контроллера имеет дело с «отделенным» объектом, который был отредактирован и отправлен обратно от клиента. DbContext еще не отслеживает эту сущность, так как я получаю новый экземпляр контекста с каждым запросом http (следовательно, сущность считается отсоединенной). Поскольку он еще не отслеживается, при его добавлении в DbContext необходимо сообщить контексту, был ли изменен этот объект и как его обрабатывать.

Есть несколько способов сообщить DbContext, как обрабатывать отдельную сущность. Среди них:

(1) установка состояния объекта в EntityState.Modified приведет к тому, что ВСЕ обновления будут включены в обновление SQL (независимо от того, изменились они на самом деле или нет), КРОМЕ для ссылочных свойств для связанных объектов:

      _context.Entry(entity).State = EntityState.Modified;

(2) добавление объекта с вызовом DbContext.Update будет делать то же, что и выше, но будет включать свойства ссылки, а также включать ВСЕ свойства этих объектов в обновлении, независимо от того, изменились они или нет:

       _context.Update(entity) 

Подход № 2 заставил меня работать, и я просто пытался создать новую связанную дочернюю сущность в Обновлении ее родителя.

Помимо этого, DbContext.Attach () и DbContext.TrackGraph звучат как ваш, обеспечивают более детальный контроль над указанием, какие конкретные свойства или связанные объекты должны быть включены в обновление.

...