实体框架代码优先问题(SimpleMembership UserProfile表)

时间:2012-09-19 20:01:15

标签: c# entity-framework asp.net-mvc-4

如果您使用过ASP.NET MVC 4,您会注意到Internet应用程序的默认设置是使用SimpleMembership提供程序,这一切都很好并且运行正常。

问题在于默认数据库生成,他们有UserProfile的POCO定义如下:

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

..然后生成如下:

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

这很好用,生成的数据库很好,工作没有问题。但是,如果我要像这样更改POCO并删除数据库:

[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; }
}

仅生成前两列,UserIdEmailAddress。它工作得很好(代码登录/注册),但显然我的其他用户数据都没有存储。

我在这里遗漏了什么吗?当然它应该基于整个UserProfile对象生成数据库。

4 个答案:

答案 0 :(得分:47)

1 - 您需要启用迁移,优先使用EntityFramework 5.在NuGet包管理器中使用Enable-Migrations

2 - 移动你的

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

到YourMvcApp / Migrations / Configuration.cs类中的Seed方法

    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"});
    }

现在EF5将负责创建UserProfile表,在这样做之后,您将调用WebSecurity.InitializeDatabaseConnection仅使用已创建的UserProfile表注册SimpleMembershipProvider,同时调用SimpleMembershipProvider哪个列是UserId和UserName。我还展示了如何添加用户,角色并将Seed方法中的两者与自定义UserProfile属性/字段相关联的示例,例如用户的手机(号码)。

3 - 现在,当您从Package Manager控制台运行update-database时,EF5将为您的表配置所有自定义属性

有关其他参考资料,请参阅本文的源代码: 的 http://blog.longle.net/2012/09/25/seeding-users-and-roles-with-mvc4-simplemembershipprovider-simpleroleprovider-ef5-codefirst-and-custom-user-properties/

答案 1 :(得分:5)

似乎我可能终于得到了这个,这可能只是一个巨大的误解。

事实证明,我希望((IObjectContextAdapter)context).ObjectContext.CreateDatabase();能够做到它根本不做的事情,即创建数据库中不存在的所有表,或者只是更新它们,如果他们这样做了与众不同。

实际发生的是它实际上运行CREATE DATABASE语句,对我来说这是最无用的事情。除非你在一个非常奇怪的环境中工作,否则你将永远拥有一个数据库,因此它将永远存在(随后表创建永远不会发生!),我宁愿不给真实世界的用户访问无论如何要创建一个数据库。

无论如何,我解决了我想要UserProfile(和相关表)来创建数据库的具体问题,使用DropCreateDatabaseIfModelChanges初始值设定项,并强制进行如下初始化:

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);
    }
}

..这是有效的,非常适合开发,但在实践中相当无用,因为如果模型发生变化,它将从字面上删除数据库并从头开始重新创建它。对我来说,这使得整个代码优先实践在它的默认形式中几乎无用,我可能最终还原为从数据库edmx生成。

仍在创建的UserProfile表背后的“谜”是WebSecurity.InitializeDatabaseConnection如果根据您传入其中的字段不存在则会初始化该表,这就是EmailAddress的原因。创建了1}}而不是UserName,因为我在此更改了它。

答案 2 :(得分:2)

我遇到了同样的问题。我添加了代码,以便在SimpleMembershipInitializer中的“CreateDatabase”之前进行迁移。

这解决了我的问题,但我相信现在我的迁移将在Azure中应用,无论发布配置文件中的设置如何。

  private class SimpleMembershipInitializer
    {
        public SimpleMembershipInitializer()
        {
            Database.SetInitializer<WomContext>(null);

            // forcing the application of the migrations so the users table is modified before
            // the code below tries to create it. 
            var migrations = new MigrateDatabaseToLatestVersion<WomContext, Wom.Migrations.Configuration>();
            var context = new WomContext(); 
            migrations.InitializeDatabase(context); 

            try
            {....

答案 3 :(得分:0)

如果您没有计划在系统生效后进行更改,这只是在开发中发生而不是热衷于启用迁移。尝试截断表__MigrationHistory。

truncate table __MigrationHistory