Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initializing and seeding Identity's DbContext in App_Start

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?

like image 724
h bob Avatar asked Sep 07 '14 18:09

h bob


1 Answers

@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.

like image 70
h bob Avatar answered Nov 04 '22 00:11

h bob