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

Posted

技术标签:

【中文标题】实体框架代码优先问题(SimpleMembership UserProfile 表)【英文标题】:Entity Framework Code-First Issues (SimpleMembership UserProfile table) 【发布时间】:2012-09-12 04:37:08 【问题描述】:

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

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

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

【问题讨论】:

看起来很奇怪。这是可重复的吗? IE。清理并重建一切,仔细检查结果。你在寻找正确的数据库吗? @HenkHolterman 不幸的是,过去 2 天我一直在为此发疯,我什至尝试用它自己的名称来归属每一列,但它们根本不存在。更糟糕的是,UserIdEmailAddress(或者我将它们重命名为) 都很好。 @HenkHolterman 还应该值得注意的是,我已经尝试在一个全新的项目上更改此表作为测试并且它具有相同的问题。我没有尝试过的是使用独立的实体框架来生成数据库,也许我接下来会这样做,但现在我宁愿与默认的 ASP.NET MVC4 结构保持一致.. 好吧,那我就难住了,没时间亲自尝试。 @Persian。这与成员资格提供程序无关,而是与 Entity Framework 的数据库生成由于某种原因而出现问题有关。 【参考方案1】:

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 - 现在,当您从包管理器控制台运行 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%,但对于可能遇到相同问题的其他人来说,它肯定 100% 有帮助!享受赏金 仅供参考,将 WebSecurity.InitializeDatabaseConnection 移动到种子方法在更新开发数据库时崩溃(“角色管理器功能尚未启用”错误)。 rufo,您需要确保 Web.confi 使用 SimpleRoleProvider 配置了 RoleProvider,请参阅答案帖子中的链接以获取您需要的 Web.Config 条目。【参考方案2】:

看来我终于明白了,这可能只是一个巨大的误解。

事实证明,我期待 ((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 迁移的目的,对吗?它没有用,因为这不是您从一个版本迁移到另一个版本的方式。 EF 对此有一个特定的用例。另外,您的问题说您每次都删除了数据库,当看起来您实际上并没有删除它,而是希望 EF 为您神奇地更改架构时。如果你真的给了我们正确的信息,我们本可以给你一个真实的回应。这就是为什么你经常摸不着头脑,因为你的问题是说你在做你没有做的事情。 我不知道为什么你会认为 CreateDatabase() 并没有真正创建数据库。它不是没用的,因为它旨在在数据库不存在时创建数据库,您告诉我们的情况就是这样。 @MystereMan 是的,我知道这一点,这正是我的帖子总结的内容;误会!它部分源于与类似问题相关的其他 SO 帖子,其中被告知“更改课程会自动更新表格”。我提供的“删除数据库”信息当然是不正确的,因为我实际上是在删除数据库contents。我的困惑来自于我的UserProfile 表仍在创建中(尽管不正确),但现在我意识到它是InitializeDatabaseConnection。这是一个非常漫长的几个星期:) @Rudi,您的其他字段 FirstName、Surname、Country、CompanyName 发生了什么?答案没有提到它们,你能创造它们吗?! @joedotnet 当您使用正确的 DataContext 初始化数据库时,它们仍然会被创建。不过,我建议这些天使用迁移,早在 2012 年,它们是一个相当新的概念,现在它很常见。查看最受好评的答案 :) 或者只是从包管理器运行 Enable-Migrations Add-Migration Initial,当您的应用首次运行时,它将正确创建数据库。然后,您可以使用 MigrateDatabaseToLatestVersion() 初始化程序进行所有未来更新。【参考方案3】:

我遇到了同样的问题。我为在 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
            ....

【讨论】:

【参考方案4】:

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

truncate table __MigrationHistory

【讨论】:

以上是关于实体框架代码优先问题(SimpleMembership UserProfile 表)的主要内容,如果未能解决你的问题,请参考以下文章

实体框架4.1代码优先映射问题

实体框架代码优先导航问题

实体框架代码优先:启用迁移错误

实体框架代码优先 - 通过主键将子实体添加到父实体

实体框架代码优先 IQueryable

实体框架代码优先 - 保存实体时设置属性的最佳方法是啥