为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
慕桂英3389331 2021-12-05 16:55:48
我在我的 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'。为什么我会收到此错误消息?我该如何解决?
查看完整描述

2 回答

?
白衣染霜花

TA贡献1796条经验 获得超10个赞

即使我在没有键和关系的另一个数据库中创建表,我也无法重现该问题。所以我确定你的模型有问题。不幸的是,您没有添加我可以比较的代码,所以我无法说出有什么不同并直接回答问题。我唯一能做的就是展示什么对我有用。不过,首先我要说几点。


我认为你不应该关注这篇文章。因为没有理由将上下文添加到现有数据库中。

就像 Ivan Stoev 提到的,你不应该混合上下文。身份上下文旨在对用户进行身份验证。它存储凭据、用户角色和声明。其中声明旨在添加有关用户的身份信息。

实际上,HometownApplicationUser 模板的默认字段可以删除,因为它是一个身份声明,应该存储在 AspNetUserClaims 表中。不是您需要扩展 ApplicationUser 的东西。实际上,我想不出任何扩展 ApplicationUser 的理由。

关于角色,这些并不是真正的声明,因为它们没有说明身份,而是用于授权。这就是为什么它们可以存储在 AspNetUserRoles 表中的原因。不幸的是,角色作为角色声明被添加到身份中,这让事情变得混乱。

请注意,身份信息存在于声明中。这意味着应用程序不必调用身份上下文。例如 User.IsInRole 检查当前身份的角色声明,而不是存储在表中的角色。

关于不同的上下文,另一个上下文(我通常称之为业务模型)与 Identity 上下文没有任何共同之处。电子邮件和其他领域不是商业模式的一部分,也没有意义。您可能认为这些字段是多余的,但实际上并非如此。我可以使用谷歌帐户登录,但对于业务,请使用我的工作电子邮件地址。

将上下文分开有几个原因。

  • 关注点的分离。假设您将来要与另一个身份验证框架交换身份验证框架。如果您想支持单点登录 (SSO),就像实施 IdentityServer 一样。

  • 如果另一个应用程序需要相同的登录名,则不能将用户表移动到另一个数据库。因此,您最终还要向数据库添加其他上下文。

  • 迁移问题。如果混合上下文,则迁移将失败。

  • 它会让事情变得容易得多。这是您遇到的第一个问题,而不是最后一个。

正如文章中所提到的:

此时,如果您需要将任何关系(例如外键)从您自己的表添加到这些表,欢迎您这样做,但不要直接或稍后在其任何 POCO 类上修改任何实体框架 2.0 表。根据我收到的反馈,这样做会导致错误。

那么,如果您不应该从您的应用程序访问身份上下文,那么如何管理信息呢?

对于当前用户,您不需要访问 users 表。所有信息都存在于身份声明中。访问身份上下文的唯一原因是允许用户登录。除了用户管理。

您可以通过添加对用户的引用(用户 ID)就足够了。如果您需要在报告中显示其他用户的信息(如姓名),则在您的业务上下文中创建一个用户表来存储信息。您可以向该表添加关系,因为它是同一上下文的一部分。

如果您对此方法有任何疑问,请告诉我。


现在对我有用的代码。就像其他人提到的那样,不太可能添加以下行:

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

是原因。如果没有virtual关键字,我认为它甚至会被忽略(保持为空)。

当我按照文章的步骤进行操作时,我最终会得到以下模型:

public class ApplicationUser : IdentityUser

{

    public string Hometown { get; set; }


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


    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> 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;

    }

}


public class ApplicationDbContext : IdentityDbContext<ApplicationUser>

{

    public ApplicationDbContext()

        : base("DefaultConnection", throwIfV1Schema: false)

    {

        // Disable migrations

        //Database.SetInitializer<ApplicationDbContext>(null);

    }


    public static ApplicationDbContext Create()

    {

        return new ApplicationDbContext();

    }

}

然后我添加 Employee 类并取消注释上面 ApplicationUser 类中的行:


public class Employee

{

    public int Id { get; set; }


    public string Name { get; set; }


    //public virtual ApplicationUser ApplicationUser { get; set; }


    public string ApplicationUserId { get; set; }

}

在数据库中,我添加了表:


CREATE TABLE [dbo].[Employees](

    [Id] [int] NOT NULL,

    [Name] [varchar](50) NOT NULL,

    [ApplicationUserId] [nvarchar](128) NOT NULL,

PRIMARY KEY CLUSTERED 

(

    [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]属性来使用不同的字段名称。


您可以尝试这样做或选择将两个上下文分开。


查看完整回答
反对 回复 2021-12-05
?
holdtom

TA贡献1805条经验 获得超10个赞

忧虑:


我确切地知道你在这里的负担是什么。是的,微软,一个深奥的邪教,在为这个与身份(实体框架)建立关系的问题提供信息方面做得很差。

贡献:


Ruard van Elburg 于 8 月 24 日 16:31 发表的帖子对此事给出了很好的见解;但是,我注意到他的代码中缺少一个关键组件,即需要放置在 IdentityModels 的 DBContext 中的 DbSet。

技术栈:


我提供了我的技术堆栈,以便如果这不适用于旧版本的软件,您就会知道我用什么来解决这个问题。


  • Visual Studio 2017 MVC 5。仅供参考,MVC 5 内置于最新的 VS 中。

  • SQL Server 17

  • MS SQL 管理工作室 17


解决方案:



免责声明!!!我知道首先关注的是数据库;但是,此解决方案仅适用于代码优先方法。但是,嘿,它有效!

在这里,我提供了有关如何执行此操作的演练。请确保您在代码的上边距中拥有所有依赖项。

步骤1:添加 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;

    using System.ComponentModel.DataAnnotations.Schema;


namespace AwesomeCode.Models

{

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

    public class ApplicationUser : IdentityUser

    {


        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> 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;

        }

    }


    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>

    {

        public ApplicationDbContext()

            : base("DefaultConnection", throwIfV1Schema: false)

        {

        }

        //A virtul DbSet in order to interact with the autogenerated code the identity framewrok produces.

        public virtual DbSet<ModelNameOfInterest> ModelNameOfInterest { get; set; }


        public static ApplicationDbContext Create()

        {


            return new ApplicationDbContext();

        }




    }

}

第 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

{

    public class WorkExp

    {

        [Key]

        public int Id { get; set; }

        public string JobTitle { get; set; }


        //Create foreign key with reference to ApplicationUser_Id that was auto-generated by entity framework.

        public virtual ApplicationUser ApplicationUser { get; set; }

    }

}

第 3 步:鉴于您为数据库设置了连接字符串,您需要生成迁移。包管理器控制台的路径:工具->NuGet Packer Manager->包管理器控制台

  • 如果根目录中不存在迁移文件夹,则启用迁移:在PM>键入之后,Enable-Migrations您应该会看到一个包含两个文件的迁移文件夹。

  • 启用迁移后: After PM>,请键入Update-DatabaseYou should see tables in your database now。

  • 添加另一个迁移: After PM>,键入Add-Migration After Name:,键入InitialCreateYour model of interest您现在应该会在您的数据库中看到表。您现在应该可以看到数据库中的表。


第 4 步:仔细检查感兴趣的模型的外键是否正确引用到 AspNetUser 表。在 MS Management Studio 中,您可以创建关系图来显示引用。你可以在谷歌上找到如何做到这一点。

第 5 步:一如既往地保持冷静、冷静和镇定。


查看完整回答
反对 回复 2021-12-05
  • 2 回答
  • 0 关注
  • 229 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信