6

I have .net core application which uses ef core 2.2 code first model , When i run

Add-migration

it always updates "ConcurrencyStamp", "PasswordHash" column values in "AspNetUsers" table.

Auto generated UP method content is

        migrationBuilder.UpdateData(
            table: "AspNetUsers",
            keyColumn: "Id",
            keyValue: new Guid("b0bea27d-5eae-4836-8f85-8bf8f1aed8d5"),
            columns: new[] { "ConcurrencyStamp", "PasswordHash" },
            values: new object[] { "c599b745-8e24-438b-bd62-8264102ac960", "AQAAAAEAACcQAAAAEHjqRSJuPQtzpQR7L7hUNo3vFM8P9dhHkiXQjYRpdgS1Z9I9TXQ2XwhM9CQiE0oVyg==" });

        migrationBuilder.UpdateData(
            table: "AspNetUsers",
            keyColumn: "Id",
            keyValue: new Guid("b0bea27d-5eae-4836-8f85-8bf8f1aed8d6"),
            columns: new[] { "ConcurrencyStamp", "PasswordHash" },
            values: new object[] { "bdbc0bcf-6eaa-489b-b2a2-9c1742a38bf4", "AQAAAAEAACcQAAAAEBpVzPQl74gxL+V93biLDmn4oKNJSZZ5VDUpwSQ/S8h+itGvGbScqG78Wi35bmN4dQ==" });

My OnModelCreating method is as follows

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

    modelBuilder.ApplyConfigurations(
        typeof(SampleDBContext).Assembly,
        typeof(IEntityTypeConfiguration<>));

    DatabaseSeeder.Seed(modelBuilder);
}

Inside seed method i call to a method to seed application user.

    private static void SeedApplicationUser(ModelBuilder modelBuilder)
    {
        var hasher = new PasswordHasher<ApplicationUser>();

        var admin = new ApplicationUser
        {
            Id = Guid.Parse("b0bea27d-5eae-4836-8f85-8bf8f1aed8d4"),
            RoleType = RoleTypes.Admin,
            Email = "admin@gmail.io",
            NormalizedEmail = "ADMIN@GMAIL.IO",
            EmailConfirmed = true,
            UserName = "admin",
            NormalizedUserName = "ADMIN",
        };

        var user1 = new ApplicationUser
        {
            Id = Guid.Parse("b0bea27d-5eae-4836-8f85-8bf8f1aed8d5"),
            RoleType = RoleTypes.User,
            Email = "user1@gmail.io",
            NormalizedEmail = "USER1@GMAIL.IO",
            EmailConfirmed = true,
            UserName = "user1",
            AccessFailedCount = 0,
            NormalizedUserName = "USER1",
        };

        var user2 = new ApplicationUser
        {
            Id = Guid.Parse("b0bea27d-5eae-4836-8f85-8bf8f1aed8d6"),
            RoleType = RoleTypes.User,
            Email = "user2@gmail.io",
            NormalizedEmail = "USER2@GMAIL.IO",
            EmailConfirmed = true,
            UserName = "user2",
            AccessFailedCount = 0,
            NormalizedUserName = "USER2",
        };

        admin.PasswordHash = hasher.HashPassword(admin, "admin@23");
        user1.PasswordHash = hasher.HashPassword(user1, "user1@23");
        user2.PasswordHash = hasher.HashPassword(user2, "user2@23");

        modelBuilder.Entity<ApplicationUser>()
            .HasData(admin, user1, user2);
    }

Is this normal behavior or im i missing something ?

if anyone need more details , comment. i have no idea what to provide.

thanks,

DerStarkeBaer
  • 669
  • 8
  • 28
Roshan
  • 3,236
  • 10
  • 41
  • 63

2 Answers2

0

ConcurrencyStamp is always generated when a new object is created.

public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString();

The same is about PasswordHash. Its value is always re-generated in your seed and is always different (even for the same password).

This is why EF Core sees the difference between the data in the database and the seed data. The values for these two fields are different, every time when you create a new migration.

The quick workaround, but not the best will be to use always the same hardcoded hash and concurrency stamp, but it will be really wrong.

It would be much better for you to use custom initialization logic and this is well described in this question: How to seed an Admin user in EF Core 2.1.0?

Pawel Maga
  • 5,428
  • 3
  • 38
  • 62
0

According to Microsoft docs:

If your scenario includes any of the following it is recommended to use custom initialization logic:

  • Data that requires calls to external API, such as ASP.NET Core Identity roles and users creation

https://learn.microsoft.com/en-us/ef/core/modeling/data-seeding#limitations-of-model-seed-data

But if you must do and needs to stop future migrations from re-instantiating hashes, then you can do:

var admin = new ApplicationUser
{
    ...
    SecurityStamp = "_known_guid_1_",
    ConcurrencyStamp = "_known_guid_2_",
    PasswordHash = "_passwordhash_of_known_password_"
};
dan
  • 1,545
  • 3
  • 20
  • 29