10

I am getting a strange behavior of seeded roles on every migration. No matter what changes you have made, migration will delete the seeded roles and will insert them again. Migration given below is created when no modification is done in the project.

All other models are seeded correctly and are considered in migration only if they are modified.

I am using ASP .NET Core 2.1 with Individual Authentication

DbContext Class for seeding

 public class ApplicationDbContext : IdentityDbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        #region Seed-Roles

        modelBuilder.Entity<IdentityRole>().HasData(new IdentityRole { Name = "SuperAdmin", NormalizedName = "SuperAdmin".ToUpper() });
        modelBuilder.Entity<IdentityRole>().HasData(new IdentityRole { Name = "Owner", NormalizedName = "Owner".ToUpper() });
        modelBuilder.Entity<IdentityRole>().HasData(new IdentityRole { Name = "Admin", NormalizedName = "Admin".ToUpper() });
        modelBuilder.Entity<IdentityRole>().HasData(new IdentityRole { Name = "Tester", NormalizedName = "Tester".ToUpper() });
        modelBuilder.Entity<IdentityRole>().HasData(new IdentityRole { Name = "User", NormalizedName = "User".ToUpper() });
        modelBuilder.Entity<IdentityRole>().HasData(new IdentityRole { Name = "Developer", NormalizedName = "Developer".ToUpper() });

        #endregion
    }
}

Migration

protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DeleteData(
            table: "AspNetRoles",
            keyColumns: new[] { "Id", "ConcurrencyStamp" },
            keyValues: new object[] { "4ae1956e-7895-4b0f-a390-22b5c41c1a62", "67b6f36e-5a3c-456b-89ef-c667cf9fe0d3" });

        migrationBuilder.DeleteData(
            table: "AspNetRoles",
            keyColumns: new[] { "Id", "ConcurrencyStamp" },
            keyValues: new object[] { "55ebaa87-e350-4d88-8d6f-2c7d833dd24d", "ed91ae85-918f-4651-b7f9-42f6dd90d9b2" });

        migrationBuilder.DeleteData(
            table: "AspNetRoles",
            keyColumns: new[] { "Id", "ConcurrencyStamp" },
            keyValues: new object[] { "6ae683e2-5df3-425f-8df7-66581ce56259", "6a4ff0dc-f82f-4c8d-85a1-0258bbf905d7" });

        migrationBuilder.DeleteData(
            table: "AspNetRoles",
            keyColumns: new[] { "Id", "ConcurrencyStamp" },
            keyValues: new object[] { "b06b021d-b369-44b3-a5d9-eeb3ef8e245d", "1363d06f-a0cb-4d10-8495-866c219f5560" });

        migrationBuilder.DeleteData(
            table: "AspNetRoles",
            keyColumns: new[] { "Id", "ConcurrencyStamp" },
            keyValues: new object[] { "b5331aad-70ec-47a2-8dd0-bc2508bdc353", "ae98d41a-e8fa-46dd-b081-96f9a1934e1e" });

        migrationBuilder.DeleteData(
            table: "AspNetRoles",
            keyColumns: new[] { "Id", "ConcurrencyStamp" },
            keyValues: new object[] { "b9fec2b5-6fd2-46a5-a960-a8d26f16d269", "413922ea-76f0-4d2d-8f1a-d9157fb31df0" });

        migrationBuilder.InsertData(
            table: "AspNetRoles",
            columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
            values: new object[,]
            {
                { "34e62595-2afc-43f9-bbcd-267773129d69", "c47b3378-ef47-4661-8a01-ceb2fbe34d7c", "SuperAdmin", "SUPERADMIN" },
                { "bc020d73-e415-4d4e-8dfe-577d81755f80", "77d4f05b-6677-4e99-9ada-3f6ec083c14b", "Owner", "OWNER" },
                { "cbfdbeb0-f800-4b42-b735-db1449fcc4e4", "2b8f6650-e2ee-46c1-a70f-36725fb893b3", "Admin", "ADMIN" },
                { "cd0e178d-f9cb-448e-8ecc-49914aa63c5d", "23ee6cfe-4bc2-4c6c-847a-d03aa0087e1f", "Tester", "TESTER" },
                { "4572a259-0d7c-4d1c-ad1d-e0230b7dd1fb", "bcc79860-9207-42bd-8a9c-8aeef0b5fe56", "User", "USER" },
                { "334fd762-7f37-48aa-afdc-a87ef8d0593e", "d929467e-44be-4a94-912f-071702316c85", "Developer", "DEVELOPER" }
            });
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DeleteData(
            table: "AspNetRoles",
            keyColumns: new[] { "Id", "ConcurrencyStamp" },
            keyValues: new object[] { "334fd762-7f37-48aa-afdc-a87ef8d0593e", "d929467e-44be-4a94-912f-071702316c85" });

        migrationBuilder.DeleteData(
            table: "AspNetRoles",
            keyColumns: new[] { "Id", "ConcurrencyStamp" },
            keyValues: new object[] { "34e62595-2afc-43f9-bbcd-267773129d69", "c47b3378-ef47-4661-8a01-ceb2fbe34d7c" });

        migrationBuilder.DeleteData(
            table: "AspNetRoles",
            keyColumns: new[] { "Id", "ConcurrencyStamp" },
            keyValues: new object[] { "4572a259-0d7c-4d1c-ad1d-e0230b7dd1fb", "bcc79860-9207-42bd-8a9c-8aeef0b5fe56" });

        migrationBuilder.DeleteData(
            table: "AspNetRoles",
            keyColumns: new[] { "Id", "ConcurrencyStamp" },
            keyValues: new object[] { "bc020d73-e415-4d4e-8dfe-577d81755f80", "77d4f05b-6677-4e99-9ada-3f6ec083c14b" });

        migrationBuilder.DeleteData(
            table: "AspNetRoles",
            keyColumns: new[] { "Id", "ConcurrencyStamp" },
            keyValues: new object[] { "cbfdbeb0-f800-4b42-b735-db1449fcc4e4", "2b8f6650-e2ee-46c1-a70f-36725fb893b3" });

        migrationBuilder.DeleteData(
            table: "AspNetRoles",
            keyColumns: new[] { "Id", "ConcurrencyStamp" },
            keyValues: new object[] { "cd0e178d-f9cb-448e-8ecc-49914aa63c5d", "23ee6cfe-4bc2-4c6c-847a-d03aa0087e1f" });

        migrationBuilder.InsertData(
            table: "AspNetRoles",
            columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
            values: new object[,]
            {
                { "6ae683e2-5df3-425f-8df7-66581ce56259", "6a4ff0dc-f82f-4c8d-85a1-0258bbf905d7", "SuperAdmin", "SUPERADMIN" },
                { "55ebaa87-e350-4d88-8d6f-2c7d833dd24d", "ed91ae85-918f-4651-b7f9-42f6dd90d9b2", "Owner", "OWNER" },
                { "4ae1956e-7895-4b0f-a390-22b5c41c1a62", "67b6f36e-5a3c-456b-89ef-c667cf9fe0d3", "Admin", "ADMIN" },
                { "b5331aad-70ec-47a2-8dd0-bc2508bdc353", "ae98d41a-e8fa-46dd-b081-96f9a1934e1e", "Tester", "TESTER" },
                { "b9fec2b5-6fd2-46a5-a960-a8d26f16d269", "413922ea-76f0-4d2d-8f1a-d9157fb31df0", "User", "USER" },
                { "b06b021d-b369-44b3-a5d9-eeb3ef8e245d", "1363d06f-a0cb-4d10-8495-866c219f5560", "Developer", "DEVELOPER" }
            });
    }

If i'm doing it wrong please update me what is the correct behavior of seeding roles.

Zubair Rana
  • 2,006
  • 2
  • 15
  • 38
  • How are you seeding this data in app? – Vivek Nuna Sep 26 '18 at 12:23
  • code is given above => using OnModelCreating – Zubair Rana Sep 26 '18 at 12:25
  • Do you have `context.Database.EnsureCreated()` in the code? I have one more query ... After adding this seed data, are you adding migration and running update-database command before running the app? – Vivek Nuna Sep 26 '18 at 12:27
  • no i haven't used it. – Zubair Rana Sep 26 '18 at 12:31
  • @camilo HasData() is introduced in ef core 2.1 so please tell me how removing tag of ef-core-2.1 will be helpful ? – Zubair Rana Sep 26 '18 at 12:34
  • @ZubairRana Didn't mean to remove it, it's just that the actual tag is [tag:entity-framework-core-2.1] rather than [tag:ef-core-2.1] – Camilo Terevinto Sep 26 '18 at 12:38
  • thank for adding it back but what if would say that entity-framework-core-2.1 is tagged in 64 questions only with 7 watchers only and ef-core-2.1 has been tagged in 175 questiones with 17 watchers ? Don't you think i will get more audience with ef-core-2.1 to answer this question ? consider it my suggestion not a proof to make you wrong because you are a senior i can't do that. – Zubair Rana Sep 26 '18 at 12:46
  • @ZubairRana try that and then report – Vivek Nuna Sep 26 '18 at 12:49
  • @viveknuna where should i use it ? – Zubair Rana Sep 26 '18 at 12:51
  • @ZubairRana run from package manager console if you have visual studio – Vivek Nuna Sep 26 '18 at 12:52
  • are you saying that i should run context.Database.EnsureCreated() command in package manager console ? – Zubair Rana Sep 26 '18 at 12:53
  • @viveknuna can you please explain what are saying as i don't think context.Database.EnsureCreated() is console command but an ef method for ensuring the creation of database. I'm facing issue with seeded data of role table only my database and tables are working and created good. – Zubair Rana Sep 26 '18 at 12:59
  • https://stackoverflow.com/questions/52067079/ef-core-2-1-hasdata-creating-deletes-and-re-inserts-for-unchanged-entities-on Follow up on this question instead. Contains a link to .NET's github regarding this issue and is currently being worked on. Also contains the workarounds. – Nicholas May 20 '19 at 15:58

3 Answers3

10

As Zubair Rana mentioned, if you want to use this method of seeding data in OnModelCreating method for Entity Framework .net Core then you have to seed complete object fields! not just the key field, here is my code example for seeding IdentityRole:

modelBuilder.Entity<IdentityRole>().HasData(new IdentityRole { Id = "117d1b41-6753-4622-89e6-8126a3b7d3f0", ConcurrencyStamp = "da7a4f42-ff3c-42a8-935a-62af68f978b0", Name = "Admin", NormalizedName = "Admin".ToUpper() });

if you do it like below:

 modelBuilder.Entity<IdentityRole>().HasData(new IdentityRole { Name = "Admin", NormalizedName = "Admin".ToUpper() });

then at each migration the ef will keep delete old objects and fill it again with same "Name" & "NormalizedName" but the "Id" & "ConcurrencyStamp" will generated again with new values.

  • Even if you create the IDs like string adminId = Guid.NewGuid().ToString(), it is still going to drop and create the records. So it is better to hardcode those IDs so that IDs are not new for every migration. Refer to the link https://learn.microsoft.com/en-us/ef/core/modeling/data-seeding for limitations of seeding data – dotcoder Dec 14 '21 at 11:55
2

Rather than hardcoding the Id and ConcurrencyStamp to work around this issue, you can follow the following steps:

  1. Create an initial migration which will seed the roles

  2. Remove the seeding logic for Roles from your OnModelCreating code (i.e. the HasData)

  3. Create another migration. This will have logic to delete the Roles on Up and add the Roles on Down that looks like this:

    migrationBuilder.DeleteData(
        table: "AspNetRoles",
        keyColumn: "Id",
        keyValue: "1");
    
    ...
    
    migrationBuilder.InsertData(
        table: "AspNetRoles",
        columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
        values: new object[,]
        {
            { "1", "...GUID...", "MyRole", "MYROLE" },
        });
    

Remove this code inside the migration. The migration will still exist, but will do nothing.

Basically your initial migration will create the data (and delete it on down) and then migrations moving forward don't muck around with the seeding.

ThisGuy
  • 2,335
  • 1
  • 28
  • 34
0

After reading many questions and answers and documentations i just came to know that if seeded values are without Primary key then they will be deleted and recreated on each migration.

Normally we cannot seed model without giving primary key value but in case of Microsoft Identity Models they can be seeded without primary keys as primary keys are created by identity methods itself. So on each migration primary keys of Seeded roles are changed which results in recreation.

Zubair Rana
  • 2,006
  • 2
  • 15
  • 38