#c# #entity-framework-core #ef-fluent-api
#c# #entity-framework-core #ef-fluent-api
Вопрос:
Отказываясь от моделей EF6 DB first для наших контекстов — я создаю ASP Core API, который использует EF Core Code First model для контекстов базы данных. Чтобы сделать это в непрерывном обновлении, мои контексты должны существовать с текущей схемой БД, поэтому я использую EF Fluent API для построения моих сопоставлений объектов для моделей Code First, которые не отражают текущую схему базы данных.
Я сталкиваюсь с проблемой со свойствами навигации по объектам во время операций вставки. Получение данных из БД для основного объекта отлично работает с использованием стандарта.Включить формат (x => x.OtherEntity), сохранение первичной сущности обратно в БД — вот в чем проблема.
Использование Fluent API для сопоставления объектов является новым для меня, поэтому, вероятно, моя проблема заключается в кривой обучения. Я пытался работать с OwnsOne против hasOne но документация MS предполагает, что hasOne () является правильным методом выполнения этого сопоставления.
Мой основной объект, о котором идет речь, имеет теневые свойства для полей внешнего ключа, с которыми связан ключ при использовании.hasOne() в fluent mapping (в коде ниже)
Сообщения об исключениях в этом случае бесполезны, поскольку они не отражают проблему сопоставления, они предполагают, что данные не могут быть вставлены, когда таблицы свойств навигации имеют столбцы идентификаторов (т.Е. не удается вставить запись с явным идентификатором) -> Это странно, потому что я не пытаюсь вставить данные через эти свойства навигации, я просто пытаюсь связать мою основную сущность с этой вторичной сущностью с помощью внешнего ключа.
Сопоставление объектов:
// 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);
}
Здесь, когда он пытается сохранить новый порядок выполнения работ, он выдает исключение из-за сопоставленных типов (Проблема, Категория, Центр затрат и т.д. Из фрагмента кода сопоставления объектов) Он пытается сохранить данные как новые объекты, а не строить отношения FK для существующих объектов. Я думаю, что здесь мне не хватает некоторой логики с Fluent API!
Любая помощь была бы очень признательна, поскольку довольно сложно выразить эту проблему в нескольких словах для поиска в Google!
Комментарии:
1. Ничего общего с fluent API. Вы добавляете () редактируете граф объектов в контекст, и все объекты будут помечены как добавленные. Позже вы можете изменить это с помощью средства отслеживания изменений или использовать Attach () вместо Add () , которое установит для объектов неизмененный, если только у них нет ключа, созданного в хранилище null, в этом случае для них будет установлено значение Added.
2. @DavidBrowne-Microsoft, отлично! Спасибо за информацию, я посмотрю, поможет ли это!
3. @DavidBrowne-Microsoft — Я отказываюсь от этого последнего утверждения! Теперь все работает как ожидалось, большое вам спасибо за ввод! Исходя из фона Code First, с которым мне никогда не приходилось этого делать. Attach () и измените состояние объекта, но я думаю, что попытка заставить ядро EF сначала сосуществовать с DB — это тоже не совсем простой или обычный случай! Еще раз спасибо за помощь!
4. Примечание: Все это
WithOne
выглядит подозрительно и может вызвать у вас проблемы в будущем — маловероятно, что все эти FK будут уникальными внутриWorkOrder
таблицы. Подумайте о том, чтобы изменить их наWithMany()
.
Ответ №1:
Благодаря @DavidBrowne-Microsoft, у меня есть ответ: попытка заставить материал DB First из нашего прошлого сосуществовать с .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() сделал свое дело, поместив объект в и сопоставив FKS с их соответствующими теневыми свойствами из моих Fluent mappings!