I'm using the latest MVC, Identity, EntityFramework, as well as the official Identity sample solution.
There are lots of ways to run a database initializer in the App_Start()
, (e.g. (DropCreateDatabaseIfModelChanges
, DropCreateDatabaseAlways
).
I tried:
AppDbContext.Instance.Database.Initialize(true); // Instance is static builder
Problem is that with Identity/OWIN, the seeding function pulls manager objects out of the OWIN context (via HttpContext.Current.GetOwinContext()
), which apparently, doesn't exist that early in the lifecycle.
var userManager = HttpContext.Current.GetOwinContext().GetUserManager<UserManager>();
var roleManager = HttpContext.Current.GetOwinContext().Get<RoleManager>();
So I get:
InvalidOperationException: No owin.Environment item was found in the context.
The OWIN context is setup properly, and runs as expected. It's only if I try access it in App_Start
that I get this problem.
Initializing the db in App_Start
is not strictly necessary, but I prefer explicit code, and want various init routines in there, including creation/seeding of the db. How do I do that?
@BrendanGreen's comment gave me an idea, which works on my side. All the stuff below is simply my edits to the Identity sample project.
First edit the DbContextInitializer
:
public class DbContextInitializer : DropCreateDatabaseIfModelChanges<AppDbContext> {
protected override void Seed(AppDbContext context) {
// remove this, because the OWIN context does not yet exist:
//var userManager = HttpContext.Current.GetOwinContext().GetUserManager<AppUserManager>();
//var roleManager = HttpContext.Current.GetOwinContext().Get<AppRoleManager>();
// and replace it with:
var userManager = new AppUserManager<AppUser>(new UserStore<AppUser>(context));
var roleManager = new AppRoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));
// ... etc. ...
base.Seed(context);
}
}
Then edit the AppDbContext
:
public class AppDbContext : IdentityDbContext<AppUser> {
// no change: this static factory method is called in OWIN init
public static AppDbContext Create() { return new AppDbContext(); }
// no change: this static ctor called the first time this type is referenced
static AppDbContext() { Database.SetInitializer<AppDbContext>(new DbContextInitializer()); }
// changed: made ctor private
private AppDbContext() : base("name=DefaultConnection", false) { }
// add this: forces the initializer to run, even if it's already been run
public static void init() { Create().Database.Initialize(true); }
}
And finally edit the Global.asax.cs
:
protected void Application_Start() {
// ... etc.
DbContext.init(); // force initializer to run
// ... etc.
}
The result is that the db is no longer lazy-created / lazy-loaded, but rather initialized in the Application_Start
. Is this necessary? No. But now all my init code occurs in one place and it's easier to keep track of things when debugging.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With