Seeding identity data in EF core 6 - c#

I'm trying to seed identity roles and users. I have the following:
public class AppDbContext : IdentityDbContext<User, Role, int>
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Role>().HasData(new Role
{
Id = 1,
Name = "SystemAdmin",
NormalizedName = "SYSTEMADMIN"
});
base.OnModelCreating(builder);
}
}
This creates the role in the DB as expected, however the NormalizedName is left null, regardless of whether I specify a value or not. The same thing happens when trying to create a user with normalized properties.
How do I go about seeding identity data (roles, users) with normalized data in the relevant properties?
Worth noting I'm using:
Microsoft.AspNetCore.Identity.EntityFrameworkCore v6.0.8
Microsoft.EntityFrameworkCore v6.0.8

This was my mistake... I had created the first migration without specifying the NormalizedName value, then given it a value but not created a new migration to apply this change. So when I ran dotnet ef migrate script, it showed me the script for the first migration, and when I ran dotnet ef database update, it updated the database to apply only the first migration.
So the obvious fix to this was to create a new migration:
dotnet ef migrations add NormalizedSeedData
Then update the database with the latest migration:
dotnet ef database update

Related

EF Migrations - Delete Database schema before migration

I am trying to create a migration, that would purge data for the whole schema and then run migrations.
I have found a way to run the migrations for the context set only.
I cannot find a way to delete the data in the given context, and not in the whole database.
Current code for migrations only:
var context = scope.ServiceProvider.GetRequiredService<TContext>();
await context.Database.MigrateAsync();
context.Database.EnsureDeleted(); has allowed me to delete the database before running migrations, but it seems to ignore the context I provide (as it says in the documentation).
Any ideas on how to delete data in the context?
Thanks!
What I would suggest is to create an empty migration, then add the raw SQL in order to drop the schema:
public partial class DropSchemaMigration : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("DROP SCHEMA MY_SCHEMA;");
}
}
Then do your other migrations. Assuming that this is a code-first approach.
You can stash all your changes, do the empty migration, pop stash and generate other migrations.
Or, if it feels more natural, do your migration, and on the first line in the Up method you can set the SQL command
public partial class MyMigration: Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("DROP SCHEMA MY_SCHEMA;");
//auto-generated mappings
}
}

How to change the type of the user's id from string to int in Asp.NET Core 2.2?

I have an application written using C# on the top on Asp.Net Core 2.2. The application was written on the top of Core 2.1 but then upgraded to Asp.net Core 2.2.
Now, I decided to change the Id type of the User model from string to an integer.
To override the string/default type, I created a User class like so
public class User : IdentityUser<int>
{
}
Then I created a Role class like so
public class Role : IdentityRole<int>
{
}
Finally, I updated the ApplicationDbContext class like so
public class ApplicationDbContext : IdentityDbContext<User, Role, int>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
Then I deleted all the database tables that start with AspNet and the _Migrations table.
Now, every time I run my app I get the following error
InvalidOperationException: No service for type
'Microsoft.AspNetCore.Identity.UserManager`1[Microsoft.AspNetCore.Identity.IdentityUser]'
has been registered.
Here is my Startup.cs class
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<User>().AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication()
.AddFacebook(facebookOptions =>
{
facebookOptions.AppId = Configuration["Authentication:Facebook:AppId"];
facebookOptions.AppSecret = Configuration["Authentication:Facebook:AppSecret"];
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
I also tried to so the following code in my Startsup file
services.AddScoped<UserManager<User>>();
Here is a screenshot of the stack trace
The call to AddDefaultIdentity<TUser>() registers UserManager<TUser> with the Dependency Injection container. Before you created your own custom TUser type, you would have been using IdentityUser here, which would have registered UserManager<IdentityUser>.
With the change from using IdentityUser to using User, you now register UserManager<User> instead of UserManager<IdentityUser>. The error message from your question shows that a partial view within your project is attempting to receive an instance of UserManager<IdentityUser> from the DI container, but it should be requesting an instance of UserManager<User> due to your change. That means the following change would be needed in the partial view:
#inject UserManager<IdentityUser> UserManager
-becomes-
#inject UserManager<User> UserManager
This will fix the specific error you've shown, but you'll also need to scan through the rest of the project to see if there are any other references to UserManager<IdentityUser> that also need to be changed.
You need to Add Migration and Update Database for creating schemas:
Define or update a data model in code. done
Add a Migration to translate this model into changes that can be applied to the database.
Check that the Migration correctly represents your intentions.
Apply the Migration to update the database to be in sync with the model.
Repeat steps 1 through 4 to further refine the model and keep the database in sync.
For Creating Migrations
dotnet ef migrations add InitialCreate
or
Add-Migration InitialCreate
For Updating Database
dotnet ef database update
or
Update-Database
You can get further details about schema customization and migration from Identity model customization in ASP.NET Core
Did you update your db?
If this done, then just change this
services.AddDefaultIdentity<User>().AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
To
services.AddIdentity<User, Role>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultUI()
.AddDefaultTokenProviders();
And this is unnecessary.
services.AddScoped<UserManager<User>>();
Just remove it.

Change Id type of asp.net core 2.2 IdentityUser

I'm new to dot-net core 2.x, so...
I would like to change the type of the Id in asp.net core 2.2 IdentityUser from string to int.
All the examples I've found via google (and the stackoverflow search facility) are giving me examples of asp.net core 2.0 which provides an ApplicationUser when you scaffold Identity (which 2.2 did not provide).
SO, I'm at a loss.. The first thing I tried (which I had high hopes for) was:
services.AddDefaultIdentity<IdentityUser<int>>()
.AddRoles<IdentityRole>()
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<ApplicationDbContext>();
But, I get the following error when I try to Add-Migration InitialCreate -Context ApplicationDbContext:
An error occurred while accessing the IWebHost on class 'Program'. Continuing without the application service provider. Error: GenericArguments[0], 'Microsoft.AspNetCore.Identity.IdentityUser`1[System.Int32]', on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9[TUser,TRole,TContext,TKey,TUserClaim,TUserRole,TUserLogin,TUserToken,TRoleClaim]' violates the constraint of type 'TUser'
Thoughts? Ideas? Documentation I can read?
For changing IdentityUser key type from string to int, you also need to change the IdentityDbContext to IdentityDbContext<IdentityUser<int>,IdentityRole<int>,int>.
Startup.cs
services.AddDefaultIdentity<IdentityUser<int>>()
.AddDefaultUI(UIFramework.Bootstrap4)
.AddEntityFrameworkStores<ApplicationDbContext>();
ApplicationDbContext
public class ApplicationDbContext : IdentityDbContext<IdentityUser<int>,IdentityRole<int>,int>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
Remove Migrations folder and delete the existing database
Run command to add and update the database
You can write your own implementation that derives from IdentityUser
public class AppUser : IdentityUser<int>
{
}
Then register it:
services.AddDefaultIdentity<AppUser>()
This is also useful if you want to add additional properties to your user model.
Remember that because you are changing primary key of the tables, so you will need to recreate these tables instead of updating them.

Migration from two WebApp with use MigrateDatabaseToLatestVersion

I have two WebApp.
Both refer to the same database, and the need to organize the migration of both App.
Both application setup to MigrateDatabaseToLatestVersion.
When you start with a blank database application first comes the creation of scheme, in table __MigrationHistory create record about this, and everything is OK.
But when you start the second application should make changes to existing database tables, but the application crashes with an error - "... This table already exists"
How to solve this problem?
Example of code second app
Global.asax
Database.SetInitializer(new SyncContextInitializer());
using (var context = new SyncDataContext())
{
context.Database.Initialize(force: true);
}
public class SyncContextInitializer : MigrateDatabaseToLatestVersion<SyncDataContext, SyncConfiguration>
{ }
public class SyncDataContext : DataContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new IdentityUserLoginMap());
.......
base.OnModelCreating(modelBuilder);
}
}
public sealed class SyncConfiguration : DbMigrationsConfiguration<SyncDataContext>
{
private readonly bool _pendingMigrations;
public SyncConfiguration()
{
AutomaticMigrationDataLossAllowed = true;
AutomaticMigrationsEnabled = true;
var migrator = new DbMigrator(this);
_pendingMigrations = migrator.GetDatabaseMigrations().Any();
}
......
.....
}
That's not going to work. When you modify the model in one app, it looks at the prior migration (in code) to compare. It will try to recreate the changes made in the other app or you will get a model mismatch error.
The best solution is to maintain the models in one application. Otherwise you would have to create a migration in the other application to keep it in sync:
// Application 1:
add-migration MyChanges
update-database
// Application 2:
add-migration SyncMyChanges -IgnoreChanges // Just update meta model
update-database
You would need to take care that there were no pending changes in App 2. See Entity Framework Under the Hood
If the models are not shared, you may just need to create an initial migration starting point in both apps:
add-migration Initial -IgnoreChanges

Entity Framework using IdentityDbContext with Code First Automatic Migrations table location and schema?

I am trying to setup automatic migration updates using the IdentityDbContext class and propagating changes to the actual DbContext for the entire database.
Before I get into the code, on my implementation of the IdentityDbContext with automatic migrations I get this error:
Automatic migrations that affect the location of the migrations history system table (such as default schema changes) are not
supported. Please use code-based migrations for operations that affect
the location of the migrations history system table.
I am not going to post the models that are associated with the migrations and context code unless someone finds them of use.
Implemenation of the IdentityDbContext:
public class SecurityContext: IdentityDbContext<User>
{
public SecurityContext() : base("MyActualContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//removing this line I do not get an error, but everything gets placed into the dbo schema. Keeping this line, i get the above error.
modelBuilder.HasDefaultSchema("ft");
}
}
So I tried adding this class to place the migrations history into the correct schema. This, in fact, does move the migrations history into the correct schema, but everything else remains in the dbo schema.
public class MyHistoryContext : HistoryContext
{
public MyHistoryContext(DbConnection dbConnection, string defaultSchema)
: base(dbConnection, defaultSchema)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("ft");
}
}
public class SecurityContextMigrations : DbMigrationsConfiguration<SecurityContext>
{
public SecurityContextMigrations()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
//When the migrations get set, I use the new class created to move the migrations to the correct schema.
SetHistoryContextFactory("System.Data.SqlClient", (c, s) => new MyHistoryContext(c, s));
}
protected override void Seed(SecurityContext context)
{
...
}
}
Ideally, I'd like everything to be in the ft schema. I don't think the migrations are that complex that I need to manually setup the migrations. I was hoping for simplicity sake, I could use automatic migrations for this. I am wondering if this approach is impossible and what I need to do to make this happen and any changes made to the models do get propagated.
I have a similar issue with Oracle 12c and EF6: I cannot get automatic migrations to work. I found, however, the following partial success factors: - I needed to set
modelBuilder.HasDefaultSchema("")
on my DbContext in order to get the runtime see the tables in the logon schema of the particular user - For the update-database it was necessary to set the MyHistoryContext parameters like that:
public class MyHistoryContext : HistoryContext
{
public MyHistoryContext(DbConnection dbConnection, string defaultSchema)
: base(dbConnection, defaultSchema)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("L2SRVRIZ");
}
}
NOTE: You need to hard-code the schema name there. In this way, update-database does not try to use dbo as schema (but still no automatic migrations are possible, they will drop your MigrationHistory table and mess up everything). This is in my opinion a nasty bug inside either EF6 or the Oracle custom class. As I have no maintenance contract with them, I cannot file a ticket.
For your case, I think its not possible somehow by design to avoid the error message with automatic migrations. EF6 thinks, for some reason, that if you use a custom schema name, that you actually moving the __MigrationHistory table from the default dbo schema, which is of course not true.
Or, did you find a solution for that?
BR Florian

Categories

Resources