实体框架在播种数据库上插入重复项[重复]

Posted

技术标签:

【中文标题】实体框架在播种数据库上插入重复项[重复]【英文标题】:Entity Framework inserting duplicates on Seeding database [duplicate] 【发布时间】:2019-05-11 10:20:26 【问题描述】:

编辑----

来自here,我尝试在播种方法中分配 ID,这对于语言来说是可以的,但是当我向客户添加地址并将 ID 也分配给这些地址时,它创建了又被骗了……

地址和语言都在我的上下文中声明为 DbSet<...>

我尝试了什么:

添加 1 个地址(带有 ID)- 将此添加到 1 个客户 => 创建一个 欺骗 添加 1 种语言和 1 个地址(带有 ID) - 将两者都添加到 1 位客户 => 创建一个骗子 添加 1 个只有名称的客户 => 不创建 欺骗 添加 1 种语言(带有 ID) - 将此添加到 1 个客户 => 不 创建一个副本

我的 Customer 上有一个 Override ToString() 方法,它返回它的名称,我可以观察到,当我在调试 1 的名称时查看副本时,另一个是 Customer 类所在的命名空间,它似乎是 loguc,因为在 Dupes 案例中 Name 为 NULL 但我还是想提一下...

----编辑

我正在用一些元数据为我的数据库播种,我发现它有一种我以前从未见过的非常奇怪的行为。我正在插入一个实体“客户”,它插入了这个实体 2 次,第一次插入是正确的并且具有它应该具有的所有内容,另一个具有 NULL 属性(字符串值)但有些(如日期时间)具有值。

我完全不知道为什么会这样,当我调用 base.Seed(ctx);方法,我敢肯定,因为我在此之后停止了 Webapp,然后才到达其他任何地方。

此实体客户具有相关的实体语言以及地址集合。

我有another post open(还没有建议),同样的问题发生在哪里,这突然发生了,我自己没有对我的模型或播种方法做任何改变......

基础实体:

public class BaseEntity

    public int ID  get; set; 

客户:

public class Customer:BaseEntity

    public string Name  get; set; 
    public Language Language  get; set; 
    public ICollection<Address> Addresses  get; set; 

语言:

public class Language : BaseEntity


    public string Name  get; set; 
    public string LanguageCode  get; set; 

    [Required]
    public ICollection<Customer> Customers  get; set; 

地址:

public class Address : BaseEntity

    public Customer Customer  get; set; 

播种方法:

  Language newLanguageNL = new Language("Dutch");
  newLanguageNL.ID = 1;

  Language newLanguageFR = new Language("French");
  newLanguageFR.ID = 2;

  Language newLanguageEN = new Language("English");
  newLanguageEN.ID = 3;

  ctx.Languages.Add(newLanguageNL); 
  ctx.Languages.Add(newLanguageEN); 
  ctx.Languages.Add(newLanguageFR);

  Address addressBE = new Address("informatica laan", "10", "bus nr 1", "8900", "België");
  addressBE.ID = 1;

  Address addressBE2 = new Address("rue de l'informatique", "20", "boite nr 2", "7780", "Belgique");
  addressBE2.ID = 2;

  Address addressEN = new Address("techstreet", "30", "box nr 1", "4000", "Bulgaria");
  addressEN.ID = 3;

  ctx.Addresses.Add(addressEN);
  ctx.Addresses.Add(addressBE);
  ctx.Addresses.Add(addressBE2);

  Customer newCustomer = new Customer("Customer name", newLanguageNL, addressBE);
  // ctx.Customers.AddOrUpdate(c => c.Name, newCustomer);
  ctx.Customers.Add(newCustomer);

  base.Seed(ctx);

OnModelCreating:

     protected override void OnModelCreating(DbModelBuilder modelBuilder)
    
        base.OnModelCreating(modelBuilder);      

        // setting the Product FK relation required + related entity
        modelBuilder.Entity<Entity.ProductSupplierForContract>().HasRequired(psfc => psfc.Product)
                                                            .WithMany(p => p.ProductSupplierForContracts)
                                                            .HasForeignKey(psfc => psfc.Product_Id);

        // setting the Supplier FK relation required + related entity
        modelBuilder.Entity<Entity.ProductSupplierForContract>().HasRequired(psfc => psfc.Supplier)
                                                           .WithMany(s => s.ProductSupplierForContracts)
                                                           .HasForeignKey(psfc => psfc.Supplier_Id);

        // setting the Contract FK relation required + related entity
        modelBuilder.Entity<Entity.ProductSupplierForContract>().HasOptional(psfc => psfc.Contract)
                                                          .WithMany(c => c.ProductSupplierForContracts)
                                                          .HasForeignKey(psfc => psfc.Contract_Id);


        modelBuilder.Entity<Entity.PurchasePrice>()
     .ToTable("PurchasePrices");

        modelBuilder.Entity<Entity.SalesPrice>()
    .ToTable("SalesPrices");

        // Bundle in Bundle
        modelBuilder.Entity<Entity.Bundle>().HasMany(b => b.ChildBundles);                      
    

谁能帮我解决这个问题,提前感谢您的任何反馈。 我试过使用 AddOrUpdate() 没有运气。

【问题讨论】:

缺少您的 BaseEntity 类以及您如何插入客户实体。 @pnet,你是​​对的,对此我很抱歉,我更新了所有内容并添加了更多信息。 再次更新了新结果 是什么让您期望代码每次不会插入新客户? (以及其他一切,就此而言,假设某处 SaveChanges 被调用)。正如种子代码中所预期的那样,我没有看到对现有实体的任何检查,并且AddOrUpdate 被注释掉了。另外,我强烈建议将这两个问题合并为一个minimal reproducible example。到目前为止,经过所有这些编辑,这个问题很难理解,副本的状态也不清楚。另外(2)你的描述让我怀疑你在构造函数中初始化了引用属性。 ***.com/a/20773057/861716 【参考方案1】:

因此,将实体 CustomerAddress 类中的关系更改为 ICollection 而不是 1 Single Customer 不会创建欺骗(并创建一个 CustomerAddress 表,该表我其实也想要)。

从数据库日志 (log4net) 看来,由于关系 EF 首先为客户的地址引用插入客户 (NULL),并插入客户 (NOT NULL) 及其引用......当我比较地址和语言我看到语言也有一个客户集合(地址没有),这解释了为什么地址创建了重复的客户条目。 (如果有人需要对此进行任何澄清,请告诉我我会尽力而为)

此帖HAS MOVED TO HERE

我要感谢所有以任何方式做出贡献的人!

【讨论】:

【参考方案2】:

我发现您的代码存在一些问题。按照惯例,一个名为 ID 的 int 列将是一个标识列,因此您不能在不发出 SET IDENTITY_INSERT Language ON 的情况下明确设置它的 ID(除非您有流畅的代码覆盖它)。

AddOrUpdate 适用于这些情况。您还没有显示该代码。另一种方式如下所示:

...
if (!ctx.Languages.Any(l => l.ID == 1))  // Check if already on file

    ctx.Database.ExecuteSqlCommand("SET IDENTITY_INSERT Language ON");  // Omit if not identity column
    var dutch = new Language 
        ID = 1,
        Name = "Dutch",
        Code = "NL"
    ;
    ctx.Languages.Add(dutch);
    ctx.SaveChanges();
    ctx.Database.ExecuteSqlCommand("SET IDENTITY_INSERT Language OFF"); // Omit if not identity column

... repeat for other languages
... similar code for other seeded tables

【讨论】:

这是正确的,但由于我每次都删除我的数据库(开始清理)我可以自己设置它们(手动自动递增它们),这可以工作,因为我可以看到他们有我分配给他们的 ID .除了 cmets 中的内容(关于 AddOrUpdate),我还应该展示什么?我知道在使用它时我不能使用 ID 属性,但仅此而已......我还尝试使用 ctx.Customers.Local 检查它是否在本地存在......但检查时它不存在所以我会实施您的建议并提供反馈。唯一的问题是添加地址时,其他人工作正常(没有欺骗)【参考方案3】:

您的地址和语言会保留在您向客户提供建议的地方。我认为在您的构造函数中,您会向客户建议收藏品,不是吗? 这是不必要的。您可以在没有明确建议集合的情况下保留客户。 EF 将自行映射集合。

【讨论】:

我确实是通过构造函数添加地址实体,填充 Address = new HashSet() address ;但删除它并没有帮助。在游戏中添加地址时我仍然有一个欺骗......(感谢您的建议) 您的实体映射如何? 我更新了原帖;) 映射与类定义不匹配。你能纠正一下吗? 也许这就是问题所在。将您的映射附加到模型创建映射。我知道选择查询中有一个自动完成功能,其中没有映射。也许这也会发生在这里。

以上是关于实体框架在播种数据库上插入重复项[重复]的主要内容,如果未能解决你的问题,请参考以下文章

将对象插入核心数据实体时处理重复项

实体框架:多对多插入重复

实体框架两次插入相同的实体[重复]

具有过滤索引的实体框架 - “无法在对象中插入重复的键行”

有没有办法在实体框架迁移上播种数据。但我们不需要在播种时提供主键值

实体框架无法在对象中插入重复键