【问题标题】:ASP.NET Identity with EF Database First MVC5带有 EF 数据库优先 MVC5 的 ASP.NET 标识
【发布时间】:2013-11-25 05:47:59
【问题描述】:

是否可以将新的 Asp.net Identity 与 Database First 和 EDMX 一起使用?还是只有代码优先?

这就是我所做的:

1) 我创建了一个新的 MVC5 项目,并让新的身份在我的数据库中创建了新的用户和角色表。

2) 然后我打开我的 Database First EDMX 文件并拖入新的 Identity Users 表,因为我还有其他与之相关的表。

3) 保存 EDMX 后,Database First POCO 生成器将自动创建一个 User 类。但是,UserManager 和 RoleManager 需要一个继承自新 Identity 命名空间 (Microsoft.AspNet.Identity.IUser) 的 User 类,因此无法使用 POCO User 类。

我想一个可能的解决方案是编辑我的 POCO 生成类以让我的用户类继承自 IUser?

还是 ASP.NET 标识仅与代码优先设计兼容?

++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++

更新:按照 Anders Abel 下面的建议,这就是我所做的。它有效,但我想知道是否有更优雅的解决方案。

1) 我通过在与自动生成的实体相同的命名空间中创建部分类来扩展我的实体用户类。

namespace MVC5.DBFirst.Entity
{
    public partial class AspNetUser : IdentityUser
    {
    }
}

2) 我将 DataContext 更改为继承自 IdentityDBContext 而不是 DBContext。请注意,每次更新 EDMX 并重新生成 DBContext 和 Entity 类时,都必须将其设置回 this。

 public partial class MVC5Test_DBEntities : IdentityDbContext<AspNetUser>  //DbContext

3) 在自动生成的 User 实体类中,您必须将 override 关键字添加到以下 4 个字段或将这些字段注释掉,因为它们是从 IdentityUser 继承的(步骤 1)。请注意,每次更新 EDMX 并重新生成 DBContext 和 Entity 类时,都必须将其设置回 this。

    override public string Id { get; set; }
    override public string UserName { get; set; }
    override public string PasswordHash { get; set; }
    override public string SecurityStamp { get; set; }

【问题讨论】:

  • 您有实现的示例代码吗?当我尝试复制上述内容时,当我尝试登录或注册用户时出现错误。“实体类型 AspNetUser 不是当前上下文模型的一部分”其中 AspNetUser 是我的用户实体
  • 您是否将 AspNetUser 表添加到您的 EDMX?另外,请确保您的 AccountController 使用的是 MVC5Test_DBEntities(或任何您的数据库上下文命名)而不是 ApplicationContext。
  • ASP.NET Identity 是一堆热气腾腾的____。对数据库优先的可怕支持,没有文档,引用约束差(在 SQL 服务器上缺少 ON CASCADE DELETE)并使用字符串作为 ID(性能问题和索引碎片)。这是他们对身份框架的第 297 次尝试……
  • @DeepSpace101 Identity 支持 DB-first 和 Code-first 一样。模板设置为代码优先,因此如果您从模板开始,则必须更改一些内容。级联删除工作得很好,您可以轻松地将字符串更改为整数。请参阅下面的答案。
  • @Shoe 我不得不说我认为你可能错了。我还没有找到一个关于如何在 db-first 中实现它的有效的、全面的示例/教程(没有文档)。 API 尝试通过属性 IdentityUser.Roles 引用联结表“IdentityUserRoles”,这会破坏 EF db-first 上的关系,因为联结表没有作为实体公开(参考约束不佳)。我不同意 ID 的字符串,因为可以通过在继承的类中指定类型参数来自定义它。总而言之,在我看来,他们根本没有首先考虑 DB。

标签: asp.net asp.net-mvc asp.net-mvc-5 ef-database-first asp.net-identity


【解决方案1】:

我发现@JoshYates1980 确实有最简单的答案。

经过一系列试验和错误后,我按照 Josh 的建议做了,并用我生成的数据库连接字符串替换了 connectionString。我最初感到困惑的是以下帖子:

How to add ASP.NET MVC5 Identity Authentication to existing database

@Win 接受的答案声明更改ApplicationDbContext() 连接名称。如果您使用的是实体和数据库/模型优先方法,其中生成数据库连接字符串并将其添加到Web.config 文件中,这有点模糊。

ApplicationDbContext() 连接名称​​映射Web.config 文件中的默认连接。因此,Josh 的方法效果最好,但为了使 ApplicationDbContext() 更具可读性,我建议将名称更改为 @Win 最初发布的数据库名称,确保更改 Web.config 中“DefaultConnection”的 connectionString并注释掉和/或删除实体生成的数据库包含。

Code Examples:

【讨论】:

    【解决方案2】:

    好问题。

    我更像是一个数据库优先的人。代码第一范式对我来说似乎很松散,而且“迁移”似乎太容易出错。

    我想自定义 aspnet 身份架构,而不是为迁移而烦恼。我精通 Visual Studio 数据库项目(sqlpackage、data-dude)以及它如何在升级架构方面做得很好。

    我的简单解决方案是:

    1) 创建一个镜像 aspnet 身份架构的数据库项目 2)使用这个项目的输出(.dacpac)作为项目资源 3) 在需要时部署 .dacpac

    对于 MVC5,修改 ApplicationDbContext 类似乎可以解决这个问题...

    1) 实现IDatabaseInitializer

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDatabaseInitializer<ApplicationDbContext> { ... }

    2) 在构造函数中,发出这个类将实现数据库初始化的信号:

    Database.SetInitializer&lt;ApplicationDbContext&gt;(this);

    3) 实现InitializeDatabase:

    在这里,我选择使用 DacFX 并部署我的 .dacpac

        void IDatabaseInitializer<ApplicationDbContext>.InitializeDatabase(ApplicationDbContext context)
        {
            using (var ms = new MemoryStream(Resources.Binaries.MainSchema))
            {
                using (var package = DacPackage.Load(ms, DacSchemaModelStorageType.Memory))
                {
                    DacServices services = new DacServices(Database.Connection.ConnectionString);
                    var options = new DacDeployOptions
                    {
                        VerifyDeployment = true,
                        BackupDatabaseBeforeChanges = true,
                        BlockOnPossibleDataLoss = false,
                        CreateNewDatabase = false,
                        DropIndexesNotInSource = true,
                        IgnoreComments = true,
    
                    };
                    services.Deploy(package, Database.Connection.Database, true, options);
                }
            }
        }
    

    【讨论】:

      【解决方案3】:

      IdentityUser 在这里毫无价值,因为它是 UserStore 用于身份验证的代码优先对象。在定义了我自己的User 对象后,我实现了一个部分类,该类实现了IUserUserManager 类使用。我希望我的Ids 是int 而不是字符串,所以我只返回 UserID 的 toString()。同样,我希望 Username 中的 n 不大写。

      public partial class User : IUser
      {
      
          public string Id
          {
              get { return this.UserID.ToString(); }
          }
      
          public string UserName
          {
              get
              {
                  return this.Username;
              }
              set
              {
                  this.Username = value;
              }
          }
      }
      

      您绝不需要IUser。它只是UserManager 使用的接口。所以如果你想定义一个不同的“IUser”,你就必须重写这个类来使用你自己的实现。

      public class UserManager<TUser> : IDisposable where TUser: IUser
      

      您现在编写自己的UserStore,它处理用户、声明、角色等的所有存储。实现代码优先UserStore所做的一切接口并将where TUser : IdentityUser更改为where TUser : User,其中“用户”是你的实体对象

      public class MyUserStore<TUser> : IUserLoginStore<TUser>, IUserClaimStore<TUser>, IUserRoleStore<TUser>, IUserPasswordStore<TUser>, IUserSecurityStampStore<TUser>, IUserStore<TUser>, IDisposable where TUser : User
      {
          private readonly MyAppEntities _context;
          public MyUserStore(MyAppEntities dbContext)
          { 
              _context = dbContext; 
          }
      
          //Interface definitions
      }
      

      这里有几个关于一些接口实现的例子

      async Task IUserStore<TUser>.CreateAsync(TUser user)
      {
          user.CreatedDate = DateTime.Now;
          _context.Users.Add(user);
          await _context.SaveChangesAsync();
      }
      
      async Task IUserStore<TUser>.DeleteAsync(TUser user)
      {
          _context.Users.Remove(user);
          await _context.SaveChangesAsync();
      }
      

      使用 MVC 5 模板,我将 AccountController 更改为如下所示。

      public AccountController()
              : this(new UserManager<User>(new MyUserStore<User>(new MyAppEntities())))
      {
      }
      

      现在登录应该可以使用您自己的表。

      【讨论】:

      • 我最近有机会实现这个,由于某种原因(我相信是因为 3.0 更新了身份)我无法通过继承 IdentityUser 然后覆盖属性来实现登录;但是编写自定义 UserStore 并继承 IUser 工作正常;只是提供一个更新,也许有人觉得这很有用。
      • 你能给所有接口实现或整个项目的链接吗?
      • 你在这里使用部分类有什么具体原因吗?
      • 如果您使用 edmx 生成模型,则必须使用部分类。如果你先写代码,那么你可以省略这个
      【解决方案4】:

      看看 GitHub 上的这个项目:https://github.com/KriaSoft/AspNet.Identity

      其中包括:

      • ASP.NET Identity 2.0 的 SQL 数据库项目模板
      • 实体框架数据库优先提供者
      • 源代码和示例

      另见How to create Database-First provider for ADO.NET Identity

      【讨论】:

        【解决方案5】:

        我的步骤非常相似,但我想分享一下。

        1) 创建一个新的 MVC5 项目

        2) 创建一个新的 Model.edmx。即使它是一个新数据库并且没有表。

        3) 编辑 web.config 并替换这个生成的连接字符串:

        <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\aspnet-SSFInventory-20140521115734.mdf;Initial Catalog=aspnet-SSFInventory-20140521115734;Integrated Security=True" providerName="System.Data.SqlClient" />
        

        使用此连接字符串:

        <add name="DefaultConnection" connectionString="Data Source=.\SQLExpress;database=SSFInventory;integrated security=true;" providerName="System.Data.SqlClient" />
        

        然后,构建并运行应用程序。注册一个用户,然后创建表。

        【讨论】:

        • 这解决了一个问题,我在数据库中看不到应用程序用户,但是更换默认连接后,它工作了。
        【解决方案6】:

        我花了几个小时解决这个问题,终于找到了一个解决方案,我在我的博客here 上分享了这个解决方案。基本上,您需要完成stink 的回答中所说的所有事情,但还有一件事:确保 Identity Framework 在用于您的应用程序实体的实体框架连接字符串之上具有特定的 SQL-Client 连接字符串。

        总之,您的应用程序将为身份框架使用一个连接字符串,而另一个用于您的应用程序实体。每个连接字符串属于不同的类型。阅读我的博文以获取完整教程。

        【讨论】:

        • 我尝试了您在博客中提到的所有内容两次,但对我来说并不奏效。
        • @Badhon 很抱歉我的博文中的说明对您不适用。我有无数人表示感谢,因为他们在我的文章之后取得了成功。永远记住,如果微软更新了一些东西,它可能会影响结果。我使用 Identity Framework 2.0 为 ASP.NET MVC 5 编写了这篇文章。除此之外的任何事情都可能会遇到问题,但到目前为止,我收到了表明成功的最新 cmets。我很想听听更多关于您的问题的信息。
        • 我会尝试再次关注,我面临的问题是我无法使用我的自定义数据库,它使用了自动构建的数据库。无论如何,我不是故意说你的文章有什么问题。感谢您分享您的知识。
        • @Badhon 我的朋友别担心,我从来没有觉得你说这篇文章有什么问题。我们都有不同的极端情况,所以有时对别人有用的东西不一定对我们有用。我希望你能够解决你的问题。
        【解决方案7】:

        我们有一个实体模型 DLL 项目,我们在其中保存模型类。我们还保留一个包含所有数据库脚本的数据库项目。我的方法如下

        1) 首先使用数据库创建您自己的具有 EDMX 的项目

        2) 为您的数据库中的表编写脚本,我使用 VS2013 连接到 localDB(数据连接)并将脚本复制到数据库项目,添加任何自定义列,例如BirthDate [DATE] 不为空

        3) 部署数据库

        4) 更新模型(EDMX)项目添加到模型项目

        5) 将任何自定义列添加到应用程序类

        public class ApplicationUser : IdentityUser
        {
            public DateTime BirthDate { get; set; }
        }
        

        在 MVC 项目 AccountController 中添加了以下内容:

        身份提供者需要一个 SQL 连接字符串才能工作,为数据库只保留 1 个连接字符串,从 EF 连接字符串中提取提供者字符串

        public AccountController()
        {
           var connection = ConfigurationManager.ConnectionStrings["Entities"];
           var entityConnectionString = new EntityConnectionStringBuilder(connection.ConnectionString);
                UserManager =
                    new UserManager<ApplicationUser>(
                        new UserStore<ApplicationUser>(
                            new ApplicationDbContext(entityConnectionString.ProviderConnectionString)));
        }
        

        【讨论】:

          【解决方案8】:

          编辑: ASP.NET Identity with EF Database First for MVC5 CodePlex Project Template.


          我想使用现有数据库并创建与 ApplicationUser 的关系。这就是我使用 SQL Server 的方式,但同样的想法可能适用于任何数据库。

          1. 创建 MVC 项目
          2. 打开 Web.config 中 DefaultConnection 下列出的 DB。它将被称为 (aspnet-[timestamp] 或类似的东西。)
          3. 为数据库表编写脚本。
          4. 将脚本表插入到 SQL Server Management Studio 中的现有数据库中。
          5. 自定义关系并将其添加到 ApplicationUser(如有必要)。
          6. 创建新的 Web 项目 > MVC > 第一个数据库项目 > 使用 EF 导入数据库...排除您插入的身份类。
          7. IdentityModels.cs 中更改 ApplicationDbContext :base("DefaltConnection") 以使用您项目的 DbContext。

          编辑:Asp.Net 身份类图

          【讨论】:

          • 问题不在于 DBContext,而是 UserManager 和 RoleManager 期望从 Microsoft.AspNet.Identity.EntityFramework.IdentityUser 继承的类
          • 公共类 IdentityDbContext:DbContext 其中 TUser:Microsoft.AspNet.Identity.EntityFramework.IdentityUser。首先使用数据库时,生成的实体类不继承任何基类。
          • 然后在使用实体框架生成类时将它们从数据库中排除。
          • 如果您从 EDMX 中排除身份表,那么您将丢失其他具有您的 UserID 外键的类中的导航属性
          • 我并不不愿意先编写代码...只是在某些场景和公司中,db admin 创建基表,而不是 coder。
          【解决方案9】:

          应该可以将身份系统与 POCO 和 Database First 一起使用,但您必须进行一些调整:

          1. 更新 .tt 文件以生成 POCO 以创建实体类 partial。这样您就可以在单独的文件中提供额外的实现。
          2. 在另一个文件中部分实现User

           

          partial User : IUser
          {
          }
          

          这将使User 类实现正确的接口,而不涉及实际生成的文件(编辑生成的文件总是一个坏主意)。

          【讨论】:

          • 谢谢。如果成功,我将不得不尝试并报告。
          • 我试过你提到的。请参阅我的帖子以获取更新信息...但解决方案不是很优雅:/
          • 我也遇到了同样的问题。似乎关于 DB-first 的文档非常稀少。这是一个很好的建议,但我认为你是对的,它不太管用
          • 目前有什么解决方案不是 hack 吗?
          • 我正在使用 Onion 架构,所有 POCO 都在 Core 中。不建议使用 IUser。那么还有其他解决方案吗?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-12-10
          • 2018-09-05
          • 2020-05-22
          • 2021-10-16
          相关资源
          最近更新 更多