【问题标题】:multiple added entities may have the same primary key on database seed多个添加的实体可能在数据库种子上具有相同的主键
【发布时间】:2015-01-03 05:21:11
【问题描述】:

假设我有以下使用实体框架 6 的 asp.net mvc 5 应用程序的模型结构

class Athlete {
 int AthleteID {get; set;}
 List<YearsAsAthlete> YearsAsAthlete {get;set;}
}

class YearsAsAthlete {
 int YearsAsAthleteID {get;set;}
 int AthleteID {get;set;}
 [ForeignKey("AthleteID")]
 Athlete Athlete {get;set;}
 List<ContractRevenue> ContractRevenue {get;set;}
 List<AdvertisementRevenue> AdvertisementRevenue {get;set;}
}

class ContractRevenue {
 int ContractRevenueID {get;set;}
 int YearsAsAthleteID {get;set;}
 [ForeignKey("YearsAsAthleteID")]
 YearsAsAthlete YearsAsAthlete {get;set;}

 List<RevenueAmounts> RevenueAmounts {get;set;}
}

class AdvertisementRevenue {get;set;}
 int AdvertisementRevenueID {get;set;}
 int YearsAsAthleteID {get;set;}
 [ForeignKey("YearsAsAthleteID")]
 YearsAsAthlete YearsAsAthlete {get;set;}

 List<RevenueAmounts> RevenueAmounts {get;set;}
}

class RevenueAmounts {
 int RevenueAmountsID {get;set;}
 int AmountPaid {get;set;}
 date DateOfPayment {get;set;}
}

当我拥有这样的模型时,这些模型工作得很好,他们有关系,而且一切都像热软糖圣代一样美味。当我运行它时,数据库会创建这些表,并且 RevenueAmounts 表会为 ContracRevenue 和 AdvertisementRevenue 获得 2 个自动生成的外键列。

但是,我不想要这些,因为它们的名字很奇怪 (ContractRevenue_ContractRevenueID),我需要一些方法来访问我的后控制器方法中的foreginkey id 属性,以添加与正确类型的收入相关的新值。

当我将 RevenueAmounts 模型更改为以下内容时:

class RevenueAmounts {
 int RevenueAmountsID {get;set;}
 int AmountPaid {get;set;}
 date DateOfPayment {get;set;}

 // ***NOTE adding foreign keys here

 int ContractRevenueID {get;set;}
 [ForeginKey("ContractRevenueID")]
 ContractRevenue ContractRevenue {get;set;}

 int AdvertisementRevenueID {get;set;}
 [ForeignKey("AdvertisementRevenueID")]
 AdvertisementRevenue AdvertisementRevenue {get;set;}
}

我开始遇到异常:

[SqlException (0x80131904):在表“AdvertisementRevenue”上引入 FOREIGN KEY 约束“FK_dbo.AdvertisementRevenue_dbo.YearsAsAthlete_YearsAsAthleteID”可能会导致循环或多个级联路径。指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。

** 编辑 **

我已经使用 fluent API 关闭了级联删除功能,但是现在我遇到了一个不同的异常:

无法确定“GIP.DAL.ContractRevenue_RevenueAmounts”关系的本金端。多个添加的实体可能具有相同的主键。

顺便说一句,当我尝试将一堆信息植入数据库然后在底部执行context.SaveChanges()(最后只执行一次)时抛出异常

【问题讨论】:

标签: c# mysql asp.net-mvc entity-framework


【解决方案1】:

在您的编辑中,您提到“多个添加的实体可能具有相同的主键”。错误。在不了解您在此处所做操作的所有细节的情况下,听起来您正在与一个实体建立关系——在上下文中有两个具有相同 ID 的实体。这些可能是尚未保存的新实体,它们从数据库中获取自动生成的 ID。如果关系基于 ID,则存在一些歧义,因为 Entity Framework 无法确定关系实际指向哪个新实体 - 它们都具有关系指向的 ID。

有两个潜在的修复方法。

  1. 为在上下文中创建的实体生成一个临时的唯一标识符。实体框架将在保存实体时丢弃它,但在此之前,它可以使用它来区分一个新实体与另一个实体。我过去曾为此使用负整数。

  2. 不要使用 ID 而是在实体引用上创建关系。如果Entity Framework直接引用了实体,那么它就不需要经过基于非唯一标识符识别实体的过程,应该不会有这个问题。

【讨论】:

  • 这实际上是数据库的初始种子方法,我添加了一堆“东西”,然后在最后一直执行 context.saveChanges。你能详细说明你提到的第一个修复吗?我怎样才能拥有“临时”唯一标识符?那会是实体类中的属性吗?
  • 临时标识符应分配给指定为实体键的属性。寻找一个用 KeyAttribute 装饰的。 msdn.microsoft.com/en-us/library/…
  • 我很抱歉给你带来了痛苦,但我的理解是,我创建了一个新属性 (int UniqueID{get;set;}) 并用 [keyattribute] 装饰它?但是,如果我们正在查看我的实体类,这是在 Contract/advertisementRevenue 类上还是在 RevenueAmounts 类上?
  • 哦。抱歉,我看到了 [Key] 属性.. 好吧,所以我只是把它放在我的主键之上,但它会为我的主键创建 2 个值,一个作为主键,另一个作为唯一 ID?跨度>
  • 第一种方法对我很有效。我的 EF 类“学生”有一个 Guid 作为 ID(实体键),由数据库在 context.SaveChanges() 上分配。使用代码中的 ID = Guid.NewGuid() 设置它解决了这个问题。这个 ID 不会被使用,但它会解决这个问题。我喜欢这个解决方案。
【解决方案2】:

这是因为 Entity Framework 在启用级联删除时无法确定关系中的哪个对象是父对象。

您是否使用代码优先?生成迁移后,您将在表声明中看到级联删除选项。如果将其设置为 false,则应解决此问题。

但是更大的问题是您正在创建的对象关系没有实现聚合根,而聚合根通常可以避免这个问题。

【讨论】:

  • 您能否详细说明如何使我的模型实现聚合根?在 Visual Studio 服务器资源管理器中,我在哪里可以看到级联删除选项?
  • 让我试着为你找到一些链接。生成迁移时,可以在表级别打开和关闭级联删除。您也可以在 Fluent API 中进行配置
  • 感谢您的跟进,我发现了如何将其关闭,但现在我遇到了一个不同的异常:无法确定“GIP.DAL.ContractRevenue_RevenueAmounts”关系的主体端。多个添加的实体可能具有相同的主键。
  • 您需要删除 YearsAsAthlete 中的运动员声明或将其设为虚拟。如果您声明与关系双方的对象的关系,EF 会抱怨,因为它无法确定父对象。
  • 我明白了,我正在研究这个,我测试后会更新
【解决方案3】:

我遇到了同样的问题,但就我而言,还有另一种解决方案。 在一个循环中,我正在添加父母 对于每个父母,我都在添加孩子。父母和孩子之间是有关系的。当然我用了FK作为虚拟参考列,但问题还是出现了。

foreach (var parent in parents)
{
   var id = parent.Id;
   var parentEntity = new Parent();
   this.Context.Set<Parent>().Add(parentEntity);
   parentEntity.CreatedOn = DateTime.UtcNow;
   ...

   foreach (var child in parents[id].Children)
   {
       var childEntity = new Child();
       //this.Context.Set<Child>().Add(childEntity);
       parent.Children.Add(childEntity);
       childEntity.CreatedOn = DateTime.UtcNow;
       ...
   }
}

我不得不注释掉将子实体添加到 EF 上下文的行,它解决了问题。

【讨论】:

    猜你喜欢
    • 2011-08-27
    • 2018-02-08
    • 2012-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-15
    • 2016-06-14
    相关资源
    最近更新 更多