Entity Framework Code-First问题(SimpleMembership UserProfile表)

28
如果您使用过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; }
}

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

这段代码运行良好,数据库生成成功,且没有问题。但是,如果我将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 对象生成数据库,是吗?


看起来很奇怪。这是可以重复的吗?即清理和重建所有内容,仔细检查结果。您是否正在查看正确的数据库? - H H
1
@HenkHolterman 很不幸,是的,我已经为此疯狂了两天,我甚至尝试为每一列分配自己的名称,但它们根本不存在。更糟糕的是,UserIdEmailAddress(或者无论我将它们重命名为什么)都很好地存在着。 - Rudi Visser
@HenkHolterman 另外值得注意的是,我已经尝试在一个全新的项目中更改此表格作为测试,并且它有相同的问题。我还没有尝试过使用独立的实体框架生成数据库,也许我下次会尝试,但目前最好尽可能保持与默认的ASP.NET MVC4结构对齐。 - Rudi Visser
那么,我被难住了,也没有时间自己尝试这个。 - H H
您需要提供自己的会员提供程序来控制所有字段。 - Persian.
1
@波斯语。这与成员提供程序无关,而是与实体框架的数据库生成有些问题。 - Rudi Visser
4个回答

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属性/字段(例如用户的Mobile号码)相关联的两个内容。
3 - 现在,当您从Package Manager Console运行update-database时,EF5将为您的表提供所有自定义属性。
有关更多参考,请参阅此带有源代码的文章:http://blog.longle.net/2012/09/25/seeding-users-and-roles-with-mvc4-simplemembershipprovider-simpleroleprovider-ef5-codefirst-and-custom-user-properties/

虽然不完全符合我的问题,但对于可能遇到相同问题的其他人来说,这肯定是100%有帮助的!享受奖励吧。 - Rudi Visser
FYI,将WebSecurity.InitializeDatabaseConnection移动到种子方法会在更新开发数据库时崩溃(出现“未启用角色管理器功能”错误)。 - rufo
2
Rufo,你需要确保Web.config已配置使用SimpleRoleProvider的RoleProvider,请参考答案帖子中的链接以获取所需的Web.Config条目。 - LeLong37

5
似乎我终于明白了,这可能只是一个巨大的误解。
事实证明,我原本期望的 ((IObjectContextAdapter)context).ObjectContext.CreateDatabase(); 并不能创建所有不存在的数据库表,或者如果它们存在且不同,则简单地更新它们。
实际发生的是,它会直接运行一个 CREATE DATABASE 语句,对我来说这是最没有用处的。除非你在一个非常奇怪的环境中工作,否则你总是会有一个数据库可供使用,因此它将一直存在(随后表的创建也不会发生!),我也不想让真实用户访问创建数据库的功能。
无论如何,我通过使用 DropCreateDatabaseIfModelChanges 初始化器来解决我想要创建数据库中的 UserProfile(以及相关表)的具体问题,并强制进行初始化,如下所示:
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 而不是 UserName 的原因,因为我已经在这里进行了更改。


你知道EF Migrations是用来做什么的吗?它是无用的,因为那不是从一个版本迁移到另一个版本的方式。EF有一个特定的用例。此外,你的问题说每次都删除了数据库,但实际上你并没有删除它,而是期望EF神奇地为你更改模式。如果你真的给我们正确的信息,我们可以给你一个真正的回答。这就是为什么你得到了很多摸不着头脑的回答,因为你的问题说你正在做一些你没有做的事情。 - Erik Funkenbusch
我不确定为什么你会认为一个名为CreateDatabase()的函数实际上并没有创建数据库。它并不是无用的,因为它的设计初衷就是在数据库不存在的情况下创建它,而这正是你告诉我们的情况。 - Erik Funkenbusch
@MystereMan 是的,我意识到了这一点,这正是我的帖子总结的内容;一个误解!它部分源于其他与类似问题相关的SO帖子,其中被告知“更改类会自动更新表”。我所提供的信息,就“删除数据库”而言,当然是不正确的,因为我实际上是在删除数据库内容。我的困惑来自于我的UserProfile表仍在创建(尽管不正确),但现在我意识到它是InitializeDatabaseConnection。这几周过得非常漫长 :) - Rudi Visser
@Rudi,你的其他字段FirstName、Surname、Country、CompanyName怎么了?答案没有提到它们,你能创建它们吗?! - joedotnot
@joedotnet 当您使用正确的DataContext初始化数据库时,它们仍会被创建。我建议现在使用迁移,虽然在2012年它们是一个相当新的概念,但现在已经很普遍了。请参见最受欢迎的答案 :) 或者只需从包管理器运行Enable-Migrations``Add-Migration Initial,当您的应用程序首次运行时,它将正确地创建数据库。然后,您可以使用MigrateDatabaseToLatestVersion()初始化程序进行所有未来更新。 - Rudi Visser

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

0

如果您在系统上线后没有计划进行更改,而且这仅发生在开发过程中,不想启用迁移,请尝试截断表__MigrationHistory。

truncate table __MigrationHistory

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接