Search code examples
asp.netmigrationseeding

EF Core - The value of x is unknown when attempting to save changes seeding data


I am trying to seeddata into database. It was working okay on .NET5

Model configuration

builder.Entity<User>()
            .HasMany(ur => ur.UserRoles)
            .WithOne(u => u.User)
            .HasForeignKey(ur => ur.UserId)
            .IsRequired();

        builder.Entity<Role>()
            .HasMany(ur => ur.UserRoles)
            .WithOne(u => u.Role)
            .HasForeignKey(ur => ur.RoleId)
            .IsRequired();

Now in .NET6, this is the error message when I try to run seeding/adding data into database.

Error] An exception occurred in the database while saving changes for context type '"API.Data.DataContext"'."\n""System.InvalidOperationException: The value of 'UserRole.UserId' is unknown when attempting to save changes. This is because the property is also part of a foreign key for which the principal entity in the relationship is not known.\n   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.PrepareToSave()\n   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.GetEntriesToSave(Boolean cascadeChanges)\n   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)\n   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)\n   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)"

my seeding data function;

private void AddUsers()
    {
        Task.Run(async () =>
        {
            string? path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            if (!_context.Users.Any())
                {
                    var userData = File.ReadAllText(path + @"/Data/SeedData/users.json");
                    var users = JsonSerializer.Deserialize<List<User>>(userData);
                    if (users == null) return;

                    foreach (var user in users)
                    {
                        user.UserName = user.UserName.ToLower();
                        user.EmailConfirmed = true;
                        user.PhoneNumberConfirmed = true;
                        user.IsActive = true;
            // ***this below createAsync is producing error.***
                        await _userManager.CreateAsync(user, Users.DefaultPassword);
                        await _userManager.AddToRoleAsync(user, Roles.BasicUser);
                    }
                  
                    _logger.LogInformation(_localizer["Seeded Users."]);

                }
        }).GetAwaiter().GetResult();
    }

Solution

  • I've cloned your repository and tracked down the issue by assigning the _userManager.CreateAsync to a variable.

    var result = await _userManager.CreateAsync(user, Users.DefaultPassword);
    await _userManager.AddToRoleAsync(user, Roles.BasicUser);
    

    now if you debug the program and inspect the result, you'll see the operation has been failed.

    Failed: Invalid Username
    Username 'cobb walters' is invalid, can only contain letters or digits.

    That's caused by those spaces in your seed data. Your username can not contain spaces in Microsoft Identity. Remove them manually or simply by user.UserName = user.UserName.Replace(" ", "_"); and it'll work as expected.