我正在处理一个 MVC 项目,该项目需要使用 Identity 创建身份验证和授权,我的项目数据库还包括应该使用该系统的主要实体,处理这种情况的最佳实践是什么。
- 我是否应该根据我的要求自定义标识应用程序用户。
- 创建我自己的表(模型)并将它们与标识表集成,我不知道该怎么做。 或任何其他解决方案。
当然,您应该自定义标识实体,因为这是创建标识的全部原因:允许更大的可扩展性。要拥有不同类型的"用户",您应该从ApplicationUser
继承;重要的是,不是直接来自IdentityUser
。这将确保核心标识关系(角色、声明、登录名等)都绑定到单个"用户"表,同时你可以扩展该表或创建其他表来保存其他用户数据。
public class ApplicationUser : IdentityUser
public class Student : ApplicationUser
public class Instructor : ApplicationUser
默认情况下,此继承将由 TPH(每个层次结构的表)实现,也称为 STI(单表继承)。这意味着所有派生类的所有属性都将由单个数据库表上的列表示。还将添加一个Discriminator
列,该列将保存实际保存的班级的名称,即"应用程序用户"、"学生"或"教师"。EF 在从查询生成对象图以实例化正确的"用户"类型时,将使用此列。
这种方法有利有弊。由于所有内容都存在于单个表中,因此查询既简单又快速。但是,此方法要求每个派生类上的所有属性在数据库级别必须为空。显而易见的原因是,如果Instructor
具有必需的列,您将无法保存Student
,因为Student
没有满足该要求的属性。您仍然可以使用视图模型强制要求在视图级别需要属性。但是,数据库中的实际列必须为空。
另一种方法是使用所谓的 TPT(每个类型的表)。在这个继承策略中,将为基类(ApplicationUser
)创建一个具有所有公共属性的表。然后,将为每个离散派生类创建一个表,仅包含该类上存在的属性。外键将添加到基类的表中,然后 EF 将使用该外键将该表中的公共数据联接到派生类表上的特定数据。此方法允许您在数据库级别强制实施 NOT NULL,但它当然需要联接来引入所有数据,这可能会减慢查询速度。
若要实现 TPT,只需将[Table]
注释添加到派生类:
[Table("Students")]
public class Student : ApplicationUser
[Table("Instructors")]
public class Instructor : ApplicationUser
最后需要注意的一件事是您需要如何利用UserManager
.如果您搭建了基架AccountController
,您会注意到它设置了一个UserManager
控制器属性,然后使用该属性创建用户、查找用户、更改密码等。这实际上是UserManager<ApplicationUser>
的一个实例,因为它是一个泛型类型。如果您需要专门使用Student
或Instructor
,则需要分别实例化UserManager<Student>
和UserManager<Instructor>
。不能使用UserManager<ApplicationUser>
实例,因为它会将派生类型向上转换为ApplicationUser
。例如:
var student = new Student { ... };
await UserManager.CreateAsync(student);
实际上会导致ApplicationUser
被保存到数据库中。特定于学生的数据将被丢弃,Discriminator
列的值将为"应用程序用户"。