Home > database >  C# & Entity Framework Core: error using a variable for id's when seeding with modelbuilder
C# & Entity Framework Core: error using a variable for id's when seeding with modelbuilder

Time:09-01

I am attempting to seed a database with a lot of example objects... at the moment there are 130 of them, to be exact. After a while I got tired of adding the objects into the .HasData() manually, and put all the objects into an array, and thought I would use a foreach loop instead.

I then quickly realized that this could potentially allow me to get around having to hard-code the object's id's as well, which had quickly turned old (especially since I sometimes have to insert objects between other objects, in an array with 60 objects, which offsets the "flow of sequential id's") ;P

However, when I run Add-Migration [Migration-name] I get an error claiming:

The seed entity for entity type '[MyEntity]' cannot be added because another seed entity with the same key value for {'Id'} has already been added.

My seed method is structured as follows (this is just an example):

public class DbInitializer
{
    private readonly ModelBuilder modelBuilder;

    // This is the id variable that I think should work...
    private int myIdVariable { get; set; }

    public DbInitializer(ModelBuilder modelBuilder)
    {
        this.modelBuilder = modelBuilder;
    }

    public void Seed()
    {
        // First I set the variable to 1 (because [reasons])...
        myIdVariable = 1;

        // And I now give all the objects in the array my id variable as id...
        var Table1Array = new Table1[]
        {
            new Table1() = { Id = myIdVariable, Name = "Example1", AssMode = true },
            new Table1() = { Id = myIdVariable, Name = "Example2", AssMode = true },
            new Table1() = { Id = myIdVariable, Name = "Example3", AssMode = false },
        }

        // And I now loop the array and run .HasData() on all the entries in the array,
        // finishing off with myIdVariable   for incremental increase.
        foreach(table in Table1Array)
        {
            modelBuilder.Entity<Table1>().HasData(table);
            myIdVariable  ;
        }
    }
}

The looping works if the id's are hard-coded, so I know that isn't an issue in and of itself. However, it seems that the variable isn't doing the trick, as the migration-builder insta-fails.

Keep in mind that this is just an example, and I have several arrays of objects, creating a total of 130 objects (with more to come). And one array in particular that currently holds 66 of these objects, where I sometimes have to insert a new object in between others, which offsets the hard-coded id's and is a total STD to fix to be sequential again. Hence this idea of using a variable for id.

Am I braking any rules in EFC by doing this? I mean if running .HasData() in a foreach works, then why doesn't the variable get an incremental increase, and is there any other way to give these objects their id sequentially without hard-coding them?

Thankful for any input.

Love & respect.

CodePudding user response:

So the main issue here is that int is a value type and you changing the value after you assigning the value to your objects ID. This is mentioned above so I'm not going to explain more on that part and just give you some alternatives

  1. If you really want to assign ID's on your c# layer then I would suggest using guids. You can assign them to each table by saying

    Guid.NewGuid();

  2. Best practice and my recommendation is to assign ID's on your your database layer. You can do this by going into your table design and finding the below settings

IdentitySeed

Identity Increment - What you want to increment by Identity Seed - Start value

This way you dont manage unique identifiers and let SQL do that for you. Also Guids are not 100% guaranteed to generate unique values all the time

CodePudding user response:

All your entities are going to have the same ID when you create them. Instead, remove the myIdVariable ; line and do that inline:

var Table1Array = new Table1[]
{
    new Table1() = { Id = myIdVariable  , Name = "Example1", AssMode = true },
    new Table1() = { Id = myIdVariable  , Name = "Example2", AssMode = true },
    new Table1() = { Id = myIdVariable  , Name = "Example3", AssMode = false },
};

And since HasData can take an IEnumerable<T> directly, you can add it in one go like this:

modelBuilder.Entity<Table1>().HasData(Table1Array);

CodePudding user response:

Because the variable myIdVariable is a value type (int), the value of each Id is the value of the variable when you assigned it, even if you incremented the variable afterward.

You can either add the tables to your array inside the loop, when incrementing the value or use something like the following example.

public class Program
{
    public static void Main()
    {
        var Table1Array = new Table1[]
        {
            new Table1() { Id = -1, Name = "Example1", AssMode = true },
            new Table1() { Id = -1, Name = "Example2", AssMode = true },
            new Table1() { Id = -1, Name = "Example3", AssMode = false },
        };

// Initial id value
var currentId = 1;

foreach (var table in Table1Array)
{
   // Update the Id
   table.Id = currentId;
   // Increment the value
   currentId  ;
}

        
    }
}


  • Related