向 ASP.NET Identity 中的 ApplicationUser 类添加关系

我在我的 ASP.NET MVC 应用程序中使用 ASP.NET Identity(数据库优先)。我按照此处的说明使用数据库优先方法设置 ASP.NET Identity。


我的 AspNetUsers 表与 Employee 表有关系(Employee 表有一个 UserId 外键,AspNetUsers 实体有一个ICollection<Employee>属性)。


我想将该ICollection<Employee>属性添加到 ApplicationUser,如下所示:


public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>

{

    public ICollection<Employee> Employees { get; set; }


    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, int> manager)

    {

        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType

        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);

        // Add custom user claims here

        return userIdentity;

    }

}

但是,当我这样做时,我收到以下错误消息:


EntityType 'AspNetUserLogins' 没有定义键。定义此 EntityType 的键。AspNetUserLogins: EntityType: EntitySet 'AspNetUserLogins' 基于未定义键的类型 'AspNetUserLogins'。


为什么我会收到此错误消息?我该如何解决?


慕桂英3389331
浏览 230回答 2
2回答

白衣染霜花

即使我在没有键和关系的另一个数据库中创建表,我也无法重现该问题。所以我确定你的模型有问题。不幸的是,您没有添加我可以比较的代码,所以我无法说出有什么不同并直接回答问题。我唯一能做的就是展示什么对我有用。不过,首先我要说几点。我认为你不应该关注这篇文章。因为没有理由将上下文添加到现有数据库中。就像 Ivan Stoev 提到的,你不应该混合上下文。身份上下文旨在对用户进行身份验证。它存储凭据、用户角色和声明。其中声明旨在添加有关用户的身份信息。实际上,HometownApplicationUser 模板的默认字段可以删除,因为它是一个身份声明,应该存储在 AspNetUserClaims 表中。不是您需要扩展 ApplicationUser 的东西。实际上,我想不出任何扩展 ApplicationUser 的理由。关于角色,这些并不是真正的声明,因为它们没有说明身份,而是用于授权。这就是为什么它们可以存储在 AspNetUserRoles 表中的原因。不幸的是,角色作为角色声明被添加到身份中,这让事情变得混乱。请注意,身份信息存在于声明中。这意味着应用程序不必调用身份上下文。例如 User.IsInRole 检查当前身份的角色声明,而不是存储在表中的角色。关于不同的上下文,另一个上下文(我通常称之为业务模型)与 Identity 上下文没有任何共同之处。电子邮件和其他领域不是商业模式的一部分,也没有意义。您可能认为这些字段是多余的,但实际上并非如此。我可以使用谷歌帐户登录,但对于业务,请使用我的工作电子邮件地址。将上下文分开有几个原因。关注点的分离。假设您将来要与另一个身份验证框架交换身份验证框架。如果您想支持单点登录 (SSO),就像实施 IdentityServer 一样。如果另一个应用程序需要相同的登录名,则不能将用户表移动到另一个数据库。因此,您最终还要向数据库添加其他上下文。迁移问题。如果混合上下文,则迁移将失败。它会让事情变得容易得多。这是您遇到的第一个问题,而不是最后一个。正如文章中所提到的:此时,如果您需要将任何关系(例如外键)从您自己的表添加到这些表,欢迎您这样做,但不要直接或稍后在其任何 POCO 类上修改任何实体框架 2.0 表。根据我收到的反馈,这样做会导致错误。那么,如果您不应该从您的应用程序访问身份上下文,那么如何管理信息呢?对于当前用户,您不需要访问 users 表。所有信息都存在于身份声明中。访问身份上下文的唯一原因是允许用户登录。除了用户管理。您可以通过添加对用户的引用(用户 ID)就足够了。如果您需要在报告中显示其他用户的信息(如姓名),则在您的业务上下文中创建一个用户表来存储信息。您可以向该表添加关系,因为它是同一上下文的一部分。如果您对此方法有任何疑问,请告诉我。现在对我有用的代码。就像其他人提到的那样,不太可能添加以下行:public&nbsp;ICollection<Employee>&nbsp;Employees&nbsp;{&nbsp;get;&nbsp;set;&nbsp;}是原因。如果没有virtual关键字,我认为它甚至会被忽略(保持为空)。当我按照文章的步骤进行操作时,我最终会得到以下模型:public class ApplicationUser : IdentityUser{&nbsp; &nbsp; public string Hometown { get; set; }&nbsp; &nbsp; //public virtual ICollection<Employee> Employees { get; set; }&nbsp; &nbsp; public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType&nbsp; &nbsp; &nbsp; &nbsp; var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);&nbsp; &nbsp; &nbsp; &nbsp; // Add custom user claims here&nbsp; &nbsp; &nbsp; &nbsp; return userIdentity;&nbsp; &nbsp; }}public class ApplicationDbContext : IdentityDbContext<ApplicationUser>{&nbsp; &nbsp; public ApplicationDbContext()&nbsp; &nbsp; &nbsp; &nbsp; : base("DefaultConnection", throwIfV1Schema: false)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; // Disable migrations&nbsp; &nbsp; &nbsp; &nbsp; //Database.SetInitializer<ApplicationDbContext>(null);&nbsp; &nbsp; }&nbsp; &nbsp; public static ApplicationDbContext Create()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; return new ApplicationDbContext();&nbsp; &nbsp; }}然后我添加 Employee 类并取消注释上面 ApplicationUser 类中的行:public class Employee{&nbsp; &nbsp; public int Id { get; set; }&nbsp; &nbsp; public string Name { get; set; }&nbsp; &nbsp; //public virtual ApplicationUser ApplicationUser { get; set; }&nbsp; &nbsp; public string ApplicationUserId { get; set; }}在数据库中,我添加了表:CREATE TABLE [dbo].[Employees](&nbsp; &nbsp; [Id] [int] NOT NULL,&nbsp; &nbsp; [Name] [varchar](50) NOT NULL,&nbsp; &nbsp; [ApplicationUserId] [nvarchar](128) NOT NULL,PRIMARY KEY CLUSTERED&nbsp;(&nbsp; &nbsp; [Id] ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]您可以使用该[ForeignKey]属性来使用不同的字段名称。您可以尝试这样做或选择将两个上下文分开。

holdtom

忧虑:我确切地知道你在这里的负担是什么。是的,微软,一个深奥的邪教,在为这个与身份(实体框架)建立关系的问题提供信息方面做得很差。贡献:Ruard van Elburg 于 8 月 24 日 16:31 发表的帖子对此事给出了很好的见解;但是,我注意到他的代码中缺少一个关键组件,即需要放置在 IdentityModels 的 DBContext 中的 DbSet。技术栈:我提供了我的技术堆栈,以便如果这不适用于旧版本的软件,您就会知道我用什么来解决这个问题。Visual Studio 2017 MVC 5。仅供参考,MVC 5 内置于最新的 VS 中。SQL Server 17MS SQL 管理工作室 17解决方案:免责声明!!!我知道首先关注的是数据库;但是,此解决方案仅适用于代码优先方法。但是,嘿,它有效!在这里,我提供了有关如何执行此操作的演练。请确保您在代码的上边距中拥有所有依赖项。步骤1:添加&nbsp;public virtual DbSet<ModelNameOfInterest> ModelNameOfInterest { get; set; }到public class ApplicationDbContext : IdentityDbContext<ApplicationUser>{}如在下面看到的代码。using System.Data.Entity;using System.Security.Claims;using System.Threading.Tasks;using Microsoft.AspNet.Identity;using Microsoft.AspNet.Identity.EntityFramework;&nbsp; &nbsp; using System.ComponentModel.DataAnnotations.Schema;namespace AwesomeCode.Models{&nbsp; &nbsp; // You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.&nbsp; &nbsp; public class ApplicationUser : IdentityUser&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Add custom user claims here&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return userIdentity;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; public class ApplicationDbContext : IdentityDbContext<ApplicationUser>&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; public ApplicationDbContext()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; : base("DefaultConnection", throwIfV1Schema: false)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; //A virtul DbSet in order to interact with the autogenerated code the identity framewrok produces.&nbsp; &nbsp; &nbsp; &nbsp; public virtual DbSet<ModelNameOfInterest> ModelNameOfInterest { get; set; }&nbsp; &nbsp; &nbsp; &nbsp; public static ApplicationDbContext Create()&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return new ApplicationDbContext();&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}第 2 步:添加public virtual ApplicationUser ApplicationUser { get; set; }到您要与之建立关系的模型中,如下所示。using System;using System.Collections.Generic;using System.ComponentModel.DataAnnotations;using System.ComponentModel.DataAnnotations.Schema;using System.Linq;using System.Web;namespace AwesomeCode.Models{&nbsp; &nbsp; public class WorkExp&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; [Key]&nbsp; &nbsp; &nbsp; &nbsp; public int Id { get; set; }&nbsp; &nbsp; &nbsp; &nbsp; public string JobTitle { get; set; }&nbsp; &nbsp; &nbsp; &nbsp; //Create foreign key with reference to ApplicationUser_Id that was auto-generated by entity framework.&nbsp; &nbsp; &nbsp; &nbsp; public virtual ApplicationUser ApplicationUser { get; set; }&nbsp; &nbsp; }}第 3 步:鉴于您为数据库设置了连接字符串,您需要生成迁移。包管理器控制台的路径:工具->NuGet Packer Manager->包管理器控制台如果根目录中不存在迁移文件夹,则启用迁移:在PM>键入之后,Enable-Migrations您应该会看到一个包含两个文件的迁移文件夹。启用迁移后: After&nbsp;PM>,请键入Update-DatabaseYou should see tables in your database now。添加另一个迁移: After&nbsp;PM>,键入Add-Migration&nbsp;After&nbsp;Name:,键入InitialCreate或Your model of interest您现在应该会在您的数据库中看到表。您现在应该可以看到数据库中的表。第 4 步:仔细检查感兴趣的模型的外键是否正确引用到 AspNetUser 表。在 MS Management Studio 中,您可以创建关系图来显示引用。你可以在谷歌上找到如何做到这一点。第 5 步:一如既往地保持冷静、冷静和镇定。
打开App,查看更多内容
随时随地看视频慕课网APP