Entity Framework inserting duplicates on Seeding d

2020-05-07 06:06发布

EDIT----

From here i tried assigning Id's in the seeding method and this was OK for Languages but not when i added Address to the customer and assign Id's as well to these addresses, than it created the dupes again...

Both Address & Language are declared as DbSet<...> in my Context

What i tried:

  • Adding 1 Address (with Id) - add this to 1 customer => Creates a dupe
  • Adding 1 language & 1 Address (with Id's) - add both to 1 customer => Creates a dupe
  • Adding 1 Customer with nothing buts its name => Doesn't create a dupe
  • Adding 1 language (with Id) - add this to 1 customer => Doesn't create a dupe

I have a Override ToString() method on my Customer which return its name, I can observe that when i look at the duplicate while debugging 1 is with the name, the other is the Namespace where the Customer class resides in, which seems loguc since the Name is NULL in the Dupes case but i figured to mention it anyway ...

----EDIT

I am seeding my database with some metadata and i see that it has a very strange behavior i never saw before. I am inserting a Entity "Customer" and it inserts this entity 2 times, first insert is correct and has everything it should have, the other one has NULL properties (string values) but some (like datetimes) have values.

I have totally no clue why this is happening, it is occurring when i call the base.Seed(ctx); method, that i am sure since i stopped the Webapp after this before it reached anything else.

This entity Customer has a related Entity Language as well as a Collection of Addresses.

I have another post open (no suggestions yet) where the same issue occurs and this happened suddenly, i did not make any changes myself to my model or seeding methods ...

Base Entity:

public class BaseEntity
{
    public int ID { get; set; }
}

Customer:

public class Customer:BaseEntity
{
    public string Name { get; set; }
    public Language Language { get; set; }
    public ICollection<Address> Addresses { get; set; }
}

Language:

public class Language : BaseEntity

{
    public string Name { get; set; }
    public string LanguageCode { get; set; }

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

Address:

public class Address : BaseEntity
{
    public Customer Customer { get; set; }
}

Seeding method:

  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);                      
    }

Can anyone help me on this one please, thank you in advance for any feedback. I have tried using AddOrUpdate() with no luck.

3条回答
我欲成王,谁敢阻挡
2楼-- · 2020-05-07 06:12

I see a few issues with your code. By convention, an int column called ID is going to be an identity column so you can't set it's ID explicitly without issuing a SET IDENTITY_INSERT Language ON (unless you have fluent code overriding this).

AddOrUpdate is intended for these situations. You have not shown that code. Another way is shown below:

...
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
查看更多
欢心
3楼-- · 2020-05-07 06:25

Your addresses and languages are persisted wheren you advise them to customer. I think in your constructor you advise the collections to customer, din't you? This isn't neccessary. You can persist the customer without an expliciet advise of the collections. EF will map the collections by it self.

查看更多
可以哭但决不认输i
4楼-- · 2020-05-07 06:35

So changing the relation in the Address Class of the Entity Customer to a ICollection instead of 1 Single Customer doesn't create a dupe (and creates a CustomerAddress table which i actually want as well).

Seems from the database logs (log4net) that due to the relation EF is first inserting a Customer (NULL) for the Address Reference of the customer, AND inserts the Customer (NOT NULL) with its references ... When i compare Address & Language I see that Language has a Collection of Customers as well (which Address didn't), this explains why Address was creating the duplicate customer entry. (If anyone needs any clarification on this let me know ill do my best)

This post HAS MOVED TO HERE

I want to thank everyone that has contributed in any way!

查看更多
登录 后发表回答