0

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.

Dimitri
  • 1,185
  • 2
  • 15
  • 37
  • 1
    Is missing your BaseEntity class and how you are doing to insert your Customer entity. – pnet Dec 10 '18 at 14:29
  • @pnet, you are correct and i am sorry for this, i update everything and added more info. – Dimitri Dec 10 '18 at 15:04
  • Updated again with new results – Dimitri Dec 10 '18 at 15:21
  • 1
    What made you expect that the code does *not* insert a new customer each time? (And everything else, for that matter, assuming that somewhere `SaveChanges` is called). I don't see any checks on existing entities, as expected in seeding code, and `AddOrUpdate` is commented out. Also, I strongly recommend merging both questions into one [mcve]. By now, after all these edits, the question is very hard to follow and the status of the duplicate isn't clear. Also (2) your description makes me suspect that you initialize reference properties in constructors. – Gert Arnold Dec 10 '18 at 20:35
  • My (custom)Seeding class is inheriting from DropCreateDatabaseAlways so this makes me NOT want to check for existing enities, you are right, i didn't include the "base.Seed(ctx);" (i did in my other post and i forgot here, my bad). So calling this calls the SaveChanges method. AddOrUpdate is behaving the same way (like described in my edit) so i left it there to show how i used it. I can create 1 post with a minimal & complete example that is a good idea. I initialize reference properties and pass them in the constructor of my customer, why shouldn't i do this? (Thank you for your feedback) – Dimitri Dec 11 '18 at 07:40
  • 1
    https://stackoverflow.com/a/20773057/861716 – Gert Arnold Dec 12 '18 at 08:02

3 Answers3

1

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.

Kostarsus
  • 37
  • 5
  • i was indeed adding the Address Entity trough the Constructor, populating Address = new HashSet
    () { address }; but removing this didn't help. I still have a dupe when adding Address into the game ... (Thank you for your suggestion)
    – Dimitri Dec 10 '18 at 15:41
  • How looks your mapping for the entities? – Kostarsus Dec 10 '18 at 15:48
  • I updated the original post ;) – Dimitri Dec 10 '18 at 15:52
  • The mapping don't matchh the class definitions. Can you correct this, please? – Kostarsus Dec 10 '18 at 15:56
  • this is the only mapping done in the method, i do not set anything regarding Language/Address/Customer except in the Class itself where i set the Required attribute. – Dimitri Dec 10 '18 at 15:58
  • 1
    Perhaps this is the problem. Append your mappings to the model creation mapping. I know there is an autocomplete in select queries, where isn't a mapping. Perhapse this will happen here, too. – Kostarsus Dec 10 '18 at 16:05
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/185043/discussion-between-dimitri-and-kostarsus). – Dimitri Dec 11 '18 at 07:47
1

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
Steve Greene
  • 12,029
  • 1
  • 33
  • 54
  • That is correct but since i am Dropping my database each time (starting clean) i can set them myself (auto incrementing them manually), this works as i can see that they have the ID's i assign them. What should i show more than what is in comments (about AddOrUpdate) ? I know i can't use the ID property when using it but that's about it ... I also tried to check if it was existing Locally using ctx.Customers.Local ... but it doesn't exist when checking so i will implement what you are suggesting and give feedback. The only issue is when adding Address, others are working fine (no dupes) – Dimitri Dec 11 '18 at 08:04
0

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!

Dimitri
  • 1,185
  • 2
  • 15
  • 37