I have an ASP.NET MVC 5 project (razor engine) which has Identity 2.0 with Individual User Accounts. I am using Visual Studio Professional 2013
I have not found any clea
I think I can solve the riddle as to WHY the Seed is never triggered: because the Seed is called only when the application is trying to connect to the database, and NOT when the application starts.
I have created examples and I have used your code successfully with and without migrations. But since you want to use it with migrations enabled, below is the sample code.
VERY IMPORTANT: To actually see the breakpoint inside Seed, run the application, press Login and use the credentials from the seed function to get access to the application. In case you get "Invalid username or password", watch out for the manager.PasswordValidator
property in IdentityConfig.cs :: Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context).
Create a new ASP.NET MVC 5 project in VS2013
Update all packages using Update-Package command.
Enable-Migrations
In the Configuration.cs created by migrations add the following code:
internal sealed class Configuration : DbMigrationsConfiguration
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(ApplicationDbContext context)
{
bool itWorks = WriteReferenceData(context);
base.Seed(context);
}
private bool WriteReferenceData(ApplicationDbContext ctx)
{
DbContextTransaction transaction = null;
bool succeeded = false;
try
{
transaction = ctx.Database.BeginTransaction();
CreateRoles(ctx);
CreateUsers(ctx);
ctx.SaveChanges();
transaction.Commit();
succeeded = true;
}
catch (Exception ex)
{
if (transaction != null) { transaction.Rollback(); transaction.Dispose(); }
succeeded = false;
}
return succeeded;
}
private void CreateRoles(ApplicationDbContext ctx)
{
// Out of the box
// ctx.Roles.AddOrUpdate(
// new IdentityRole { Name = "Administrator" },
// new IdentityRole { Name = "Guest" }
// );
// Another approach
var RoleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(ctx));
var roleName = "Administrator";
//Create role if it does not exist
if (!RoleManager.RoleExists(roleName))
{
var roleresult = RoleManager.Create(new IdentityRole(roleName));
}
}
private void CreateUsers(ApplicationDbContext ctx)
{
// Out of the box approach
// ctx.Users.AddOrUpdate(
// new ApplicationUser { Email = "foo@xyz.com", UserName = "foo@xyz.com" }
// );
// Another approach
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(ctx));
var user = new ApplicationUser() { UserName = "foo@xyz.com", Email="foo@xyz.com"};
var password = "Wh@tever777";
var adminresult = UserManager.Create(user, password);
//Add User Admin to Role Administrator
if (adminresult.Succeeded)
{
var result = UserManager.AddToRole(user.Id, "Administrator");
}
}
}
In Global.asax.cs :: Application_Start(), add the following line:
Database.SetInitializer<ApplicationDbContext>(new MigrateDatabaseToLatestVersion<ApplicationDbContext, Configuration>());
Run!
If you also need a sample of code with migrations disabled, let me know.
To complete this question.. As Corneliu Serediuc said all you need to do is simply try to connect to database during application startup, for example, like this (IdentityModel.cs):
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
}
public static ApplicationDbContext Create()
{
var ctx = new ApplicationDbContext();
var runSeed = ctx.Roles.AnyAsync();
return ctx;
}
} By the way, Corneliu thank you for your code examples, they help me a lot.
So we do something similar in our samples package in the following way (To seed the db with our admin user)
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
static ApplicationDbContext()
{
// Set the database intializer which is run once during application start
// This seeds the database with admin user credentials and admin role
Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
// This is useful if you do not want to tear down the database each time you run the application.
// public class ApplicationDbInitializer : DropCreateDatabaseAlways<ApplicationDbContext>
// This example shows you how to create a new database if the Model changes
public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext>
{
protected override void Seed(ApplicationDbContext context) {
DoYourSeedingHere(context);
base.Seed(context);
}
}
EF has two different Seed methods. One that is used with database initializers and another that is used with Migrations. Since you've enabled Migrations, I'll describe how to do this with the Migrations Seed method here...
First of all, even though you've enabled Migrations, by default EF still uses the CreateDatabaseIfNotExists
database initializer. Which means when you run your app, the first time the ApplicationDbContext
is accessed, the initializer is called and it'll create the database tables from your Code First mappings if the tables don't exist already. You're not seeing the schema created because you probably haven't accessed the db context. In a new web app, this is typically triggered the first time you register a user or try to log in.
To seed the ASP.NET Identity tables, there are two things you need to do. The first is to add seed logic to the Seed method in Configuration.cs
. The second is to trigger update-database
... either by running it in the Package Manager Console or by using the MigrateDatabaseToLatestVersion
database initializer.
Here's an example of what you can put into the Seed method to create a role and a user...
public Configuration()
{
AutomaticMigrationsEnabled = true;
// ...
}
protected override void Seed(MyProject.Web.Models.ApplicationDbContext context)
{
if (!context.Roles.Any())
{
var roleStore = new RoleStore<IdentityRole>(context);
var roleManager = new RoleManager<IdentityRole>(roleStore);
var role = new IdentityRole{
Name = "Administrator"
};
roleManager.Create(role);
}
if (!context.Users.Any())
{
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new ApplicationUserManager(userStore);
var user = new ApplicationUser {
Email = "foo@bar.com",
UserName = "SuperUser"
};
userManager.Create(user, "MySecretPassword1234");
userManager.AddToRole(user.Id, "Administrator");
}
}
After this is done, you can run Update-Database
from the Package Manager Console to migrate the database to the latest schema and run the Seed method.
Or you can change EF to use the MigrateDatabaseToLatestVersion
initializer by modifying the context in IdentityModels.cs
...
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
Database.SetInitializer<ApplicationDbContext>(new MigrateDatabaseToLatestVersion<ApplicationDbContext, Configuration>());
}
}
Now when you run the application, the first time the database context is used it'll run update-database
and seed it.
If you want to bulk insert new accounts with a CSV, you can use this project: https://github.com/Stonefinch/AspNetUserMaintenanceAzureSiteExtension
That can also be installed as an Azure Site Extension: https://www.siteextensions.net/packages/AspNetUserMaintenanceAzureSiteExtension/