【问题标题】:EF composite primary key violation on AddOrUpdateAddOrUpdate 上的 EF 复合主键冲突
【发布时间】:2017-06-22 11:12:23
【问题描述】:

我正在尝试创建 2 个表。一种用于商店,一种用于收银机。收银机有复合键:Id + ShopId

这是一个模型:

public class Shop
{
    [Key]
    public int Id { get; set; }
    public string ShopName { get; set; }
}

public class CashRegister
{
    [Key, Column(Order = 0)]
    public int Id { get; set; }
    public string CashRegisterName { get; set; }

    [ForeignKey("ShopId")]
    public Shop Shop { get; set; }

    [Key, Column(Order = 1)]
    public int ShopId { get; set; }
}

以下是迁移的样子:

        CreateTable(
            "dbo.CashRegisters",
            c => new
                {
                    Id = c.Int(nullable: false),
                    ShopId = c.Int(nullable: false),
                    CashRegisterName = c.String(),
                })
            .PrimaryKey(t => new { t.Id, t.ShopId })
            .ForeignKey("dbo.Shops", t => t.ShopId, cascadeDelete: true)
            .Index(t => t.ShopId);

        CreateTable(
            "dbo.Shops",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    ShopName = c.String(),
                })
            .PrimaryKey(t => t.Id);

这里是抛出主键冲突异常的代码:

        var context = new Model();

        var shops = new List<Shop>
        {
          new Shop() { ShopName = "First shop" },
          new Shop() { ShopName = "Second shop" }
        };

        context.Shops.AddOrUpdate(shops.ToArray());
        context.SaveChanges();

        var cashRegisters = new List<CashRegister>();

        foreach (var shop in shops)
        {
            cashRegisters.Add(new CashRegister()
            {
                CashRegisterName = "First cash register",
                ShopId = shop.Id
            });

            cashRegisters.Add(new CashRegister()
            {
                CashRegisterName = "Second cash register",
                ShopId = shop.Id
            });
        }

        context.CashRegisters.AddOrUpdate(a => new { a.Id, a.ShopId }, cashRegisters.ToArray());
        context.SaveChanges();
        //primary key violation exception on line above

现在显然收银机在创建时具有 0 作为 ID。 我得到的例外是:

"Violation of PRIMARY KEY constraint 'PK_dbo.CashRegisters'. Cannot insert duplicate key in object 'dbo.CashRegisters'. The duplicate key value is (0, 1).\r\nThe statement has been terminated."

我尝试将[DatabaseGenerated(DatabaseGeneratedOption.Identity)] 添加到CashRegister.Id 但后来我得到另一个异常{"Cannot insert the value NULL into column 'Id', table 'TestCompositeKeys.dbo.CashRegisters'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated."},这很奇怪,因为Id 甚至不能为空。

谁能告诉我我做错了什么或者我该如何解决这个问题?谢谢

【问题讨论】:

  • 也许在您的CashRegister 中,您需要分配Shop 属性(对象),而不仅仅是外键。
  • @JoseLuis 感谢您的评论,但没有帮助。我添加了一条我收到的异常消息。我相信第一条记录是用 Id (0, 1) 插入的,然后第二条记录是用 Id (0,1) 插入的,这会导致异常。如果它不是复合键,它会工作得很好
  • 我想了解为什么您使用复合主键。通常,在需要它的极少数情况下,密钥的每个部分都将由其他现有实体确定,因此复合密钥永远不会有自动生成的组件。
  • @grek40 我考虑使用复合键的原因是因为记录是在多台机器上生成的,然后被拉到一个数据库中。例如,销售将具有整数 ID、CashId 和 ShopId。如果会有一个整数 id,那么就会有冲突。我本可以使用Guids,但我不想
  • 这根本不是原因。

标签: c# entity-framework composite-primary-key


【解决方案1】:

CashRegister.Id 设置为自动增量应该确实有效。

public class CashRegister
{
    [Key, Column(Order = 0), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    // ...

要考虑的另一件事是AddOrUpdate 语句。由于Id 只会在实际插入时分配,因此您无法真正按 Id 比较条目。相反,您可以尝试以下方法:

context.CashRegisters.AddOrUpdate(a => new { a.ShopId, a.CashRegisterName  }, cashRegisters.ToArray());

它只会使用初始化的属性进行比较,并且会真正防止重新插入,而使用a.Id, a.ShopId 您可能会在第二次执行种子时得到重复。

【讨论】:

    【解决方案2】:

    这里的代码

    foreach (var shop in shops)
            {
                cashRegisters.Add(new CashRegister()
                {
                    CashRegisterName = "First cash register",
                    ShopId = shop.Id
                });
    
                cashRegisters.Add(new CashRegister()
                {
                    CashRegisterName = "Second cash register",
                    ShopId = shop.Id
                });
            }
    

    将插入 4 个 CashRegister 对象。两个商店各两个。当 ShopId 设置为主键时,CashRegister 表不能有两次相同的 ShopId。

    解决方案是为每个 Shop 对象插入一个 CashRegister 对象,或者从 ShopId 中删除主要约束。

    【讨论】:

      猜你喜欢
      • 2015-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-24
      • 1970-01-01
      • 1970-01-01
      • 2011-08-23
      相关资源
      最近更新 更多