Entity Framework Code-First Issues (SimpleMembership UserProfile table)

asp.net-mvc-4 c# entity-framework

Question

If you've used ASP.NET MVC 4 you'll notice that the default for an Internet Application is to use the SimpleMembership provider, this is all well and good and works fine.

The issue comes with the default database generation, they have a POCO for UserProfile defined like so:

[Table("UserProfile")]
public class UserProfile
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }
    public string UserName { get; set; }
}

.. which is then generated like this:

using (var context = new UsersContext())
{
    if (!context.Database.Exists())
    {
         // Create the SimpleMembership database without Entity Framework migration schema
         ((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
    }
}

This works fine, the database is generated just fine and works without issue. However, if I am to change the POCO like this and delete the database:

[Table("UserProfile")]
public class UserProfile
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }
    public string EmailAddress { get; set; }

    public string FirstName { get; set; }
    public string Surname { get; set; }

    public string Country { get; set; }

    public string CompanyName { get; set; }
}

Only the first 2 columns are generated, UserId and EmailAddress. It works just fine code-wise (talking login/registration), but obviously none of my other user data is stored.

Am I missing something here? Surely it should generate the database based off the whole UserProfile object.

1
28
9/19/2012 8:01:15 PM

Accepted Answer

It seems I may have finally got this, and it may have just been one giant misunderstanding.

As it turns out, I was expecting ((IObjectContextAdapter)context).ObjectContext.CreateDatabase(); to do what it simply doesn't, which is create all of the tables in the database that don't exist, or simply update them if they do and they're different.

What actually happens is that it literally runs a CREATE DATABASE statement, which to me is the most useless thing ever. Unless you're working in a really strange environment, you will always have a database on the off and so it would always exist (and subsequently the table creation would never happen!), I'd rather not be giving real-world users access to create a database anyway.

Anyway, I solved my specific issue of wanting UserProfile (and related tables) to create the database by using the DropCreateDatabaseIfModelChanges initializer, and forcing an initialization like below:

public SimpleMembershipInitializer()
{
#if DEBUG
    Database.SetInitializer<DataContext>(new DropCreateDatabaseIfModelChanges<DataContext>());
#else
    Database.SetInitializer<DataContext>(null);
#endif

    try
    {
        using (var context = new DataContext())
        {
            if (!context.Database.Exists())
            {
                ((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
            }
            context.Database.Initialize(true);
        }

        WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "EmailAddress", autoCreateTables: true);
    }
    catch (Exception ex)
    {
        throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
    }
}

.. this works and is perfect for development but is pretty useless in practice since it will literally drop the database and recreate it from scratch if the model changes. To me, this makes the whole code-first practice almost useless in it's default form and I'll probably end up reverting back to a from-DB edmx generation.

The "mystery" behind the UserProfile table still being created is that WebSecurity.InitializeDatabaseConnection will initialise the table if it doesn't exist based on the fields you pass into it, which is why the EmailAddress was created instead of UserName, because I had changed it in this.

5
9/23/2012 10:47:51 PM

Popular Answer

1 - You need to enable migrations, prefereably with EntityFramework 5. Use Enable-Migrations in the NuGet package manager.

2 - Move your

WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "EmailAddress", autoCreateTables: true); 

to your Seed method in your YourMvcApp/Migrations/Configuration.cs class

    protected override void Seed(UsersContext context)
    {
        WebSecurity.InitializeDatabaseConnection(
            "DefaultConnection",
            "UserProfile",
            "UserId",
            "UserName", autoCreateTables: true);

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

        if (!WebSecurity.UserExists("lelong37"))
            WebSecurity.CreateUserAndAccount(
                "lelong37",
                "password",
                new {Mobile = "+19725000000", IsSmsVerified = false});

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

Now EF5 will be in charge of creating your UserProfile table, after doing so you will call the WebSecurity.InitializeDatabaseConnection to only register SimpleMembershipProvider with the already created UserProfile table, also tellling SimpleMembershipProvider which column is the UserId and UserName. I am also showing an example of how you can add Users, Roles and associating the two in your Seed method with custom UserProfile properties/fields e.g. a user's Mobile (number).

3 - Now when you run update-database from Package Manager Console, EF5 will provision your table with all your custom properties

For additional references please refer to this article with sourcecode: http://blog.longle.net/2012/09/25/seeding-users-and-roles-with-mvc4-simplemembershipprovider-simpleroleprovider-ef5-codefirst-and-custom-user-properties/



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow