Asp.net-mvc – Cannot seed Users & Roles

asp.net-mvcc#-4.0entity-frameworkseedsimplemembership

I am trying to seed users and roles into my database.
Currently using Code First Entity Framework with Automatic Migrations in C# MVC4.
Whenever I call

Update-Database -Force

I get the following error:

Running Seed method.
System.InvalidOperationException: You must call the
"WebSecurity.InitializeDatabaseConnection" method before you call any
other method of the "WebSecurity" class. This call should be placed in
an _AppStart.cshtml file in the root of your site.
at WebMatrix.WebData.SimpleRoleProvider.get_PreviousProvider()
at WebMatrix.WebData.SimpleRoleProvider.RoleExists(String roleName)
at System.Web.Security.Roles.RoleExists(String roleName)
at GratifyGaming.Domain.Migrations.Configuration.Seed(GratifyGamingContext
context) in C:\Users\Unreal\Documents\Visual Studio
2010\Projects\GratifyGaming\GratifyGaming.Domain\Migrations\Configuration.cs:line
36
at System.Data.Entity.Migrations.DbMigrationsConfiguration1.OnSeed(DbContext
context)
at System.Data.Entity.Migrations.DbMigrator.SeedDatabase()
at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.SeedDatabase()
at System.Data.Entity.Migrations.DbMigrator.Upgrade(IEnumerable
1
pendingMigrations, String targetMigrationId, String lastMigrationId)
at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.Upgrade(IEnumerable`1
pendingMigrations, String targetMigrationId, String lastMigrationId)
at System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)
at System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(String
targetMigration)
at System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.RunCore()
at System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.Run()
You must call the "WebSecurity.InitializeDatabaseConnection" method before you call any other method of the "WebSecurity" class.
This call should be placed in an _AppStart.cshtml file in the root of
your site.

The offending line of code is the Role.Exists

I have tried putting the WebSecurity.InitializeDatabaseConnection in Global.asax, Seed(), and created an _AppStart.cshtml with no success. I have trawled the internet looking for a possible solution and none of them have worked (including other stack overflow articles). Some notable blog posts are below.

See code below.

[Configuration.cs]

 protected override void Seed(GratifyGaming.Domain.Models.DAL.GratifyGamingContext context)
    {
        var criteria = new List<Criterion>
        {
            new Criterion { ID = 1, IsMandatory=true, Name = "Gameplay", Description="The playability of the games core mechanics" },
            new Criterion { ID = 2, IsMandatory=true, Name = "Art Style", Description="The artistic feel of the game as a whole. Elements such as story, style and originality come into play." },
            new Criterion { ID = 3, IsMandatory=true, Name = "Longevity", Description="How long did this game keep you entertained?" },
            new Criterion { ID = 4, IsMandatory=true, Name = "Graphics", Description="How good does the game look?" }
        };

        criteria.ForEach(s => context.Criterion.AddOrUpdate(s));
        context.SaveChanges();


        if (!Roles.RoleExists("Administrator"))
            Roles.CreateRole("Administrator");

        if (!WebSecurity.UserExists("user"))
            WebSecurity.CreateUserAndAccount(
                "user",
                "password");

        if (!Roles.GetRolesForUser("lelong37").Contains("Administrator"))
            Roles.AddUsersToRoles(new[] { "user" }, new[] { "Administrator" });
    }

The Criterion seed code works without fail.

[_AppStart.cshtml]

@{
 if (!WebSecurity.Initialized)
{
    WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId",
                                             "UserName", autoCreateTables: true);
}
}

Normal login to my site works perfectly with this located here.

[web.config]

 <roleManager enabled="true" defaultProvider="SimpleRoleProvider">
  <providers>
    <clear/>
    <add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/>
  </providers>
</roleManager>
<membership defaultProvider="SimpleMembershipProvider">
  <providers>
    <clear/>
    <add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" />
  </providers>
</membership>

[AccountModel.cs]

    [Table("UserProfile")]
public class UserProfile
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }
    public virtual ICollection<Game> AttachedGames { get; set; }

    public virtual ICollection<UserGratificationRecord> GratificationHistory { get; set; }

    [ForeignKey("UserLevel")]
    public int? AcheivementID { get; set; }
    public virtual Acheivement UserLevel { get; set; }

    public int? NumOfGratifictions { get; set; }

}

UPDATE

I think the WebSecurity.InitializeDatabaseConnection is not even being run – I can place more than one in my Seed method, and not get the 'can only be called once' error that you would normally get.

My seed method is in my domain project along with all the models, while everything else is in a WebUI project. Not sure if this has anything to do with it.

Best Answer

Just put that lazy init into the top of your Seed method

protected override void Seed(GratifyGaming.Domain.Models.DAL.GratifyGamingContext context)
{
    if (!WebSecurity.Initialized)
    {
        WebSecurity.InitializeDatabaseConnection("DefaultConnection",
                                                 "UserProfile",
                                                 "UserId",
                                                 "UserName",
                                                 autoCreateTables: true);
    }
Related Topic