EF Core-为什么当实体应为外键关系时将其另存为新的数据库实体?

时间:2019-04-01 22:01:04

标签: c# entity-framework-core ef-fluent-api

针对我们的上下文,从EF6 DB First模型移开-我正在构建一个将EF Core Code First模型用于数据库上下文的ASP Core API。为了在滚动更新中做到这一点,我的上下文必须与当前的数据库架构存在,因此我使用EF Fluent API为不反映当前数据库架构的Code First模型构建实体映射。

在插入操作期间,我在实体上遇到导航属性问题。使用标准的.include(x => x.OtherEntity)格式,可以从数据库中获取主要实体的数据,将问题保存到数据库中即可。

使用Fluent API映射实体对我来说是新手,因此学习的曲线很可能是我的问题所在。我曾尝试使用OwnsOne与HasOne,但MS文档建议HasOne()是执行此映射的正确方法。

有问题的我的主要实体具有“外键”字段的“阴影属性”,当我在流畅的映射中使用.HasOne()时(在下面的代码中),这些键已链接起来

在这种情况下,异常消息没有用处,因为它们没有反映出映射问题,它们建议当导航属性的表具有“标识列”时,不能插入数据(IE无法插入记录带有显式ID)->这很奇怪,因为我没有尝试通过这些导航属性插入数据,而是试图通过外键将我的主要实体链接到该次要实体。

实体映射:

 // 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逻辑的地方!

任何帮助将不胜感激,因为要用谷歌搜索将这个问题用几句话说是很难的!

1 个答案:

答案 0 :(得分:0)

感谢@ 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()可以解决问题,将实体放置在FK中并从我的Fluent映射向上映射到它们各自的阴影属性!