代码之家  ›  专栏  ›  技术社区  ›  ataravati

在ASP.NET Identity中向ApplicationUser类添加关系(首先是数据库)

  •  3
  • ataravati  · 技术社区  · 7 年前

    我正在我的ASP.NET MVC应用程序中使用ASP.NET标识(数据库优先)。我遵照指示 here

    我的AspNetUsers表与Employee表有关系(Employee表有一个UserId外键,AspNetUsers实体有一个 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”未定义密钥。定义键 此实体类型。AspNetUserLogins:EntityType:EntitySet “AspNetUserLogins”基于没有 键已定义。

    为什么我会收到这个错误信息?我该怎么解决?

    2 回复  |  直到 7 年前
        1
  •  5
  •   Ruard van Elburg    7 年前

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


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

    事实上,违约 Hometown 可以删除ApplicationUser模板的字段,因为它是一个标识声明,应该存储在AspNetUserClaims表中。不需要扩展应用程序用户。实际上,我想不出任何理由来扩展ApplicationUser。

    关于角色,这些并不是真正的声明,因为它们不告诉任何关于身份的信息,而是用于授权。这就是为什么可以将它们存储在AspNetUserRoles表中。不幸的是,角色作为角色声明添加到标识中,这会使事情变得混乱。

    请注意,索赔中有身份信息。这意味着应用程序不必调用标识上下文。E、 g.User.IsInRole检查当前标识的角色声明,而不是表中存储的角色。

    关于不同的上下文,另一个上下文(我通常称之为业务模型)与标识上下文没有任何共同之处。电子邮件和其他字段不是业务模型的一部分,对业务模型也没有意义。你可能认为这些字段是多余的,但事实上它们不是。我可以使用google帐户登录,但对于企业,请使用我的工作电子邮件地址。

    有几个原因使上下文保持分离。

    • 分离关注点。假设您希望将来与另一个身份验证框架交换身份验证框架。比如实现IdentityServer,以防支持单点登录(SSO)。
    • 如果其他应用程序需要相同的登录名,则不能将users表移动到其他数据库。因此,最终也会将其他上下文添加到数据库中。
    • 移民问题。如果混合上下文,则迁移将失败。

    如本条所述:

    此时,如果需要添加任何关系(例如外键) 从你自己的桌子到这些桌子,欢迎你这么做,但是 不直接或以后修改任何Entity Framework 2.0表 他们的任何一个POCO类。这样做将导致基于 我收到了反馈。

    那么,如果不应该从应用程序访问标识上下文,如何管理信息?

    对于当前用户,不需要访问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] 属性以使用其他字段名。

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

        2
  •  1
  •   ataravati    7 年前

    关注点:




    贡献:
    Ruard van Elburg post于8月24日16:31给出了关于这个问题的很好的见解;但是,我注意到他的代码中缺少一个关键组件,那就是DbSet,需要放在IdentityModels的DBContext中。

    技术堆栈:
    • Visual Studio 2017 MVC 5。仅供参考,MVC5内置于最新的VS中。
    • SQL服务器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 您应该会看到一个包含两个文件的迁移文件夹。
    • 启用迁移后:之后 PM> ,类型 Update-Database 现在应该可以在数据库中看到表了。
    • 添加另一个迁移:之后 PM> ,类型 Add-Migration Name: ,类型 InitialCreate Your model of interest 现在应该可以在数据库中看到表了。现在应该可以在数据库中看到表了。


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

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