我在ASP.NET MVC应用程序中使用ASP.NET Identity(数据库优先)。我按照说明here使用数据库优先方法设置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”,没有 键已定义。
为什么会收到此错误消息?我该如何解决?
答案 0 :(得分:5)
即使我在没有键和关系的另一个数据库中创建表,也无法重现该问题。因此,我确定您的模型有问题。不幸的是,您没有添加我可以比较的代码,因此我无法分辨出不同之处并直接回答问题。我唯一能做的就是展示对我有用的东西。但是,首先我要说一些话。
我认为您不应该关注这篇文章。由于没有理由将上下文添加到现有数据库中。
就像伊万·斯托耶夫(Ivan Stoev)提到的那样,您不应混合使用上下文。身份上下文旨在对用户进行身份验证。它存储凭据,用户角色和声明。声明旨在添加有关用户的身份信息。
实际上,可以删除ApplicationUser模板的默认Hometown
字段,因为它是一个身份声明,应存储在AspNetUserClaims表中。不需要为ApplicationUser扩展它。实际上,我想不出任何理由来扩展ApplicationUser。
关于角色,这些并不是真正的要求,因为它们不提供任何有关身份的信息,而是用于授权。这就是将它们存储在AspNetUserRoles表中的原因。不幸的是,角色作为角色声明被添加到身份中,这使事情变得混乱。
请注意,身份信息存在于权利要求中。这意味着应用程序不必调用Identity上下文。例如。 User.IsInRole检查当前身份的角色声明,而不是表中存储的角色。
关于不同的上下文,另一个上下文(我通常将其称为业务模型)与Identity上下文没有任何共同之处。电子邮件和其他字段不属于业务模式,也没有意义。您可能会认为这些字段是多余的,但实际上并非如此。我可以使用Google帐户登录,但对于公司,请使用我的工作电子邮件地址。
有几个原因可以使上下文分开。
如文章中所述:
此时,如果您需要添加任何关系(例如外键) 从您自己的表到这些表,欢迎您这样做,但是 不要直接或稍后修改任何Entity Framework 2.0表 他们的任何POCO课程。这样做会基于以下原因导致错误 我收到的反馈。
如果您不应该从应用程序访问身份上下文,那么如何管理信息?
对于当前用户,您不需要访问users表。所有信息都存在于身份声明中。访问身份上下文的唯一原因是允许用户登录。除了用户管理。
您只需添加对用户的引用(用户ID)即可。如果您需要在报表中显示其他用户的信息(例如姓名),请在您的业务上下文中创建一个用户表来存储信息。您可以向该表添加关系,因为它是同一上下文的一部分。
如果您对此方法有疑问,请告诉我。
现在适合我的代码。像其他人提到的那样,不太可能添加以下行:
public ICollection<Employee> Employees { get; set; }
是原因。如果没有virtual
关键字,我认为它甚至会被忽略(仍然为null)。
当我按照本文的步骤进行操作时,我将得到以下模型:
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]
属性使用其他字段名称。
您可以尝试这样做,也可以选择将两个上下文分开。
答案 1 :(得分:0)
关注点:
解决方案:
免责声明!!!!我了解到首先要关注数据库。但是,此解决方案仅适用于代码优先方法。但是,嘿,它有效!
在这里,我提供了有关如何执行此操作的演练。请确保所有依赖项都在代码的顶部。
步骤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管理器->软件包管理器控制台
PM>
之后,键入Enable-Migrations
,您将看到一个包含两个文件的迁移文件夹。 PM>
之后,键入Update-Database
,您现在应该在数据库中看到表。 PM>
之后,键入Add-Migration
在Name:
之后,键入InitialCreate
或Your model of interest
您现在应该在数据库中看到表。您现在应该在数据库中看到表。
步骤4:仔细检查感兴趣的模型的外键是否正确引用了AspNetUser表。在MS Management Studio中,您可以创建一个关系图以显示参考。您可以在Google上找到操作方法。
第5步:一如既往地保持冷静,镇定并保持冷静。