EF Core - Почему объекты сохраняются как новые объекты БД, когда они должны быть отношениями внешних ключей? - PullRequest
0 голосов
/ 02 апреля 2019

Отход от первых моделей EF6 DB для наших контекстов - я создаю ASP Core API, который использует модель EF Core Code First для контекстов базы данных. Чтобы сделать это в непрерывном обновлении, мои контексты должны существовать с текущей схемой БД, поэтому я использую EF Fluent API для построения сопоставлений сущностей для моделей Code First, которые не отражают текущую схему базы данных.

Я столкнулся с проблемой со свойствами навигации на объектах во время операций вставки. Получение данных из БД для первичного объекта прекрасно работает с использованием стандартного формата .Include (x => x.OtherEntity), и проблема заключается в сохранении первичного объекта в БД.

Использование Fluent API для отображения сущностей является для меня новым, поэтому, скорее всего, в этом и заключается проблема обучения. Я пытался работать с OwnsOne против HasOne, но документация MS предполагает, что HasOne () является правильным методом для выполнения этого сопоставления.

Моя основная сущность, о которой идет речь, имеет свойства тени для полей внешнего ключа, которые связывают ключ, когда я использую .HasOne () в отображении данных (в коде ниже)

Сообщения об исключениях в этом случае бесполезны, поскольку они не отражают проблему с отображением, они предполагают, что данные не могут быть вставлены, когда таблицы свойств навигации имеют столбцы идентификаторов (IE не может вставить запись с явным идентификатором) -> Это странно, потому что я не пытаюсь вставить данные через эти свойства навигации, я просто пытаюсь связать свою основную сущность с этой вторичной сущностью с помощью внешнего ключа.

Отображение сущностей:

 // WorkOrder Entity Mapping:
 modelBuilder.Entity<WorkOrder>().ToTable("WorkOrder");
 modelBuilder.Entity<WorkOrder>().Property(x => x.Id).HasColumnName("IDWorkOrder");
  modelBuilder.Entity<WorkOrder>().Property(x => x.CreatedBy).HasColumnName("IDUserCreated");
  modelBuilder.Entity<WorkOrder>().Property(x => x.UpdatedBy).HasColumnName("IDUserUpdated");
  modelBuilder.Entity<WorkOrder>().Property<int?>("IDWOCategory");
  modelBuilder.Entity<WorkOrder>().Property<int?>("IDProblem");
  modelBuilder.Entity<WorkOrder>().Property<int?>("IDWOCostCenter");
  modelBuilder.Entity<WorkOrder>().Property<int?>("IDWOLocation");
  modelBuilder.Entity<WorkOrder>().Property<int?>("IDWOPriority");
  modelBuilder.Entity<WorkOrder>().Property<int?>("IDWOStatus");
  modelBuilder.Entity<WorkOrder>().Property<int?>("IDWOTrade");
  modelBuilder.Entity<WorkOrder>().Property<Guid?>("IDUserCompleted");
  modelBuilder.Entity<WorkOrder>().Property<Guid?>("IDParentWO");

  // WO Navigation Properties:
  modelBuilder.Entity<WorkOrder>().HasOne(x => x.Category).WithOne().HasForeignKey<WorkOrder>("IDWOCategory").HasPrincipalKey<Category>(c => c.Id);
  modelBuilder.Entity<WorkOrder>().HasOne(x => x.Problem).WithOne().HasForeignKey<WorkOrder>("IDProblem").HasPrincipalKey<Problem>(c => c.Id);
  modelBuilder.Entity<WorkOrder>().HasOne(x => x.CostCenter).WithOne().HasForeignKey<WorkOrder>("IDWOCostCenter").HasPrincipalKey<CostCenter>(c => c.Id);
  modelBuilder.Entity<WorkOrder>().HasOne(x => x.Location).WithOne().HasForeignKey<WorkOrder>("IDWOLocation").HasPrincipalKey<Location>(c => c.Id);
  modelBuilder.Entity<WorkOrder>().HasOne(x => x.Priority).WithOne().HasForeignKey<WorkOrder>("IDWOPriority").HasPrincipalKey<Priority>(c => c.Id);
  modelBuilder.Entity<WorkOrder>().HasOne(x => x.Status).WithOne().HasForeignKey<WorkOrder>("IDWOStatus").HasPrincipalKey<Status>(c => c.Id);
  modelBuilder.Entity<WorkOrder>().HasOne(x => x.Trade).WithOne().HasForeignKey<WorkOrder>("IDWOTrade").HasPrincipalKey<Trade>(c => c.Id);
  modelBuilder.Entity<WorkOrder>().HasOne(x => x.Requester).WithOne().HasForeignKey<WorkOrder>("IDRequester").HasPrincipalKey<Requester>(c => c.Id);
  modelBuilder.Entity<WorkOrder>().HasOne(x => x.UserCompleted).WithOne().HasForeignKey<WorkOrder>("IDUserCompleted").HasPrincipalKey<User>(c => c.Id);
  modelBuilder.Entity<WorkOrder>().HasOne(x => x.ParentWorkOrder).WithOne().HasForeignKey<WorkOrder>("IDParentWO").HasPrincipalKey<WorkOrder>(c => c.Id);

Получение данных в контроллере: (работает как шарм!)

[HttpGet("{Id}")]
public async Task<ActionResult<List<WorkOrder>>> GetWorkOrders(Guid Id)
{
    var result = await WorkOrdersContext.WorkOrders
        .Include(x => x.Problem)
        .Include(x => x.Status)
        .Include(x => x.Requester)
        .Include(x => x.ParentWorkOrder)
        .Include(x => x.Category)
        .Include(x => x.Trade)
        .Include(x => x.Location)
        .Include(x => x.CostCenter)
        .Include(x => x.Priority)
        .Where(x => x.Id == Id)
        .ToListAsync();

    return Ok(result);
}

Сохранение новых данных в контроллере: (Где они ломаются!)

[HttpPost]
public async Task<ActionResult<WorkOrder>> CreateWorkOrderFromPending([FromBody]WorkOrder call)
{
    // Insert the Work Order to the DB:
    DbContext.WorkOrders.Add(call);
    var saveResult = await DbContext.SaveChangesAsync();

    // Check if any oddities occurred during the save:
    if (saveResult == 0) return BadRequest("An Error occurred during saving and the Call was not saved, please try again.");

    // Return the Inserted Work Order:
    return Ok(call);
}

Здесь, когда он пытается сохранить новый рабочий заказ, он генерирует исключение из-за сопоставленных типов (проблема, категория, CostCenter и т. Д. Из фрагмента кода сопоставлений сущностей). Он пытается сохранить данные как новые сущности, а не построение отношений ФК на существующих предприятиях. Я думаю, что здесь мне не хватает логики с Fluent API!

Любая помощь будет принята с благодарностью, поскольку эту проблему довольно сложно описать несколькими словами для поиска в Google!

1 Ответ

0 голосов
/ 02 апреля 2019

Благодаря @ DavidBrowne-Microsoft, у меня есть ответ, я пытаюсь создать БД. Первые вещи из нашего прошлого сосуществуют с .NET Core - непростая задача и исходят из фона Code First, где .Add () был путем. это сработало, из-за чего было трудно понять, как .Attach () собирается добиться цели.

[HttpPost]
public async Task<ActionResult<WorkOrder>> CreateWorkOrderFromPending([FromBody]WorkOrder call)
{
    // Insert the Work Order to the DB:
    // DbContext.WorkOrders.Add(call);
    var entity = DbContext.WorkOrders.Attach(call);

    entity.State = EntityState.Added;

    var saveResult = await DbContext.SaveChangesAsync();

    // Check if any oddities occurred during the save:
    if (saveResult == 0) return BadRequest("An Error occurred during saving and the Call was not saved, please try again.");

    // Return the Inserted Work Order:
    return Ok(call);
}

Используя .Attach () вместо .Add () добился цели, поместив сущность в и сопоставив FK с соответствующими им свойствами теней из моих отображений Fluent!

...