如何将 TPH(Table 每个层次结构)与 ASP.NET 身份 2 一起使用
How to use TPH (Table Per Hierarchy) with ASP.NET Identity 2
我在一个 MVC5
项目中使用 ASP.NET Identity 2
并且有 2 种类型的用户 class 分别称为 Student
和 Coordinator
,如下所示。另一方面,我尝试遵循 TPH (Table Per Hierarchy) 方法,以便为两种类型的用户使用相同的实体。
public class Student : ApplicationUser
{
public int? Number { get; set; }
}
public class Coordinator : ApplicationUser
{
public string Room { get; set; }
}
public class ApplicationUser : IdentityUser<int, ApplicationUserLogin,
ApplicationUserRole, ApplicationUserClaim>, IUser<int>
{
//Custom Properties
public string Name { get; set; }
public string Surname { get; set; }
//code omitted for brevity
}
由于 ApplicationUser
已经继承自 IdentityUser
,我没有将其创建为 abstract
class 并且 没有 添加到我的DbContext
如下图:
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
另一方面,还有另一个实体将 Student(不是 ApplicationUser)作为导航 属性,如下所示:
public class Grade
{
[Key]
public int Id { get; set; }
public int Grade { get; set; }
//Navigation properties ===========================
public virtual Experiment Experiment { get; set; }
public virtual Student Student { get; set; }
//public virtual ApplicationUser User { get; set; }
//=================================================
}
}
但是,在将DbSet添加到DbContext
时,我遇到了"Multiple object sets per type are not supported. The object sets 'ApplicationUsers' and 'Users' can both contain instances of type 'Xxxx.ApplicationUser'."错误。那么,上面的方法有没有错误呢?我应该在 DbContext
中使用 DbSet 还是应该将 ApplicationUser 作为导航 属性 添加到 Grade 实体?任何帮助将不胜感激...
您应该可以在不向 DbContext
添加 ApplicationUsers
DbSet
的情况下完成此操作,就像这样:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Student> Students { get; set; }
public DbSet<Coordinator> Coordinators { get; set; }
public DbSet<Grade> Grades { get; set; }
...
}
并且可以在 Grade
实体的导航 属性 中使用 Student
类型,您不必使用 ApplicationUser
。
检查您的数据库是否与您的模型同步。你在使用 EF 迁移吗?迁移文件中的 table 应如下所示:(请注意 AspNetUsers
table 中的 Discriminator
字段,以及 Number
和 Room
字段。这些是 TPH 应用于 table 创建的证明。此外,Student
或 Coordinator
的 table 没有包含在迁移中。)
CreateTable(
"dbo.AspNetUsers",
c => new
{
Id = c.String(nullable: false, maxLength: 128),
Name = c.String(),
Surname = c.String(),
Email = c.String(maxLength: 256),
EmailConfirmed = c.Boolean(nullable: false),
PasswordHash = c.String(),
SecurityStamp = c.String(),
PhoneNumber = c.String(),
PhoneNumberConfirmed = c.Boolean(nullable: false),
TwoFactorEnabled = c.Boolean(nullable: false),
LockoutEndDateUtc = c.DateTime(),
LockoutEnabled = c.Boolean(nullable: false),
AccessFailedCount = c.Int(nullable: false),
UserName = c.String(nullable: false, maxLength: 256),
Room = c.String(),
Number = c.Int(),
Discriminator = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => t.Id)
.Index(t => t.UserName, unique: true, name: "UserNameIndex");
CreateTable(
"dbo.Grades",
c => new
{
Id = c.Int(nullable: false, identity: true),
GradeValue = c.Int(nullable: false),
Student_Id = c.String(maxLength: 128),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AspNetUsers", t => t.Student_Id)
.Index(t => t.Student_Id);
如果您的 table 看起来不像这些,请尝试恢复到一个空数据库,删除迁移并重新生成它们,最后用好的迁移更新数据库。
我用你的实体和 DbContext
中的那三个 DbSets
创建了一个虚拟 MVC 项目,生成了迁移并创建了数据库 tables,运行 以下测试代码(来自默认控制器索引方法)。数据已正确保存在数据库中:
public void Test()
{
using (var context = new ApplicationDbContext())
{
var student = new Student
{
Id = "12345",
Name = "John",
Number = 123,
UserName = "Johnny",
Email = "some@email.com"
};
context.Students.Add(student);
context.SaveChanges();
}
using (var context = new ApplicationDbContext())
{
var student = context.Students.Find("12345");
var grade = new Grade { Id = 333, GradeValue = 90, Student = student };
context.Grades.Add(grade);
context.SaveChanges();
}
}
一些问题:
"Multiple object sets" 错误消息说明了有关 "The object sets 'ApplicationUsers' and 'Users'" 的内容。这个 'Users' 对象集来自哪里,你有用户 DbSet
吗?尝试删除它。或者,也许您使用 DbSet
的类型参数进行了复制粘贴错误,例如编写 public DbSet<wrongType> GoodTypeName
(它发生了)。
我对这个声明有疑问:
public class ApplicationUser : IdentityUser<int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>, IUser<int>
{ ... }
所以我用了这个(基础中没有类型参数class):
public class ApplicationUser : IdentityUser
{ ... }
似乎带有 Identity 的 MVC 项目的默认实现期望 ApplicationUser
扩展没有类型参数的 IdentityUser
的实现。否则会在多个地方出现编译错误,例如在声明 ApplicationContext
并尝试使用 ApplicationUser
作为基础 class IdentityDbContext
的类型参数时。
如果你使用带有类型参数的基础class,我认为TKey
类型参数应该是String而不是int,除非你改变了User.Id
属性 输入您的自定义实体。也许您因此而收到错误?在数据库中,这些实体的 Id 字段具有 varchar 类型。
我将 Grade
属性 的名称更改为 GradeValue
(Grade
不是有效的 属性 名称,因为它是class 的名称,我遇到了编译错误)。
如果你需要,我可以发送我用来测试这个的虚拟解决方案的代码。
我在一个 MVC5
项目中使用 ASP.NET Identity 2
并且有 2 种类型的用户 class 分别称为 Student
和 Coordinator
,如下所示。另一方面,我尝试遵循 TPH (Table Per Hierarchy) 方法,以便为两种类型的用户使用相同的实体。
public class Student : ApplicationUser
{
public int? Number { get; set; }
}
public class Coordinator : ApplicationUser
{
public string Room { get; set; }
}
public class ApplicationUser : IdentityUser<int, ApplicationUserLogin,
ApplicationUserRole, ApplicationUserClaim>, IUser<int>
{
//Custom Properties
public string Name { get; set; }
public string Surname { get; set; }
//code omitted for brevity
}
由于 ApplicationUser
已经继承自 IdentityUser
,我没有将其创建为 abstract
class 并且 没有 添加到我的DbContext
如下图:
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
另一方面,还有另一个实体将 Student(不是 ApplicationUser)作为导航 属性,如下所示:
public class Grade
{
[Key]
public int Id { get; set; }
public int Grade { get; set; }
//Navigation properties ===========================
public virtual Experiment Experiment { get; set; }
public virtual Student Student { get; set; }
//public virtual ApplicationUser User { get; set; }
//=================================================
}
}
但是,在将DbSet添加到DbContext
时,我遇到了"Multiple object sets per type are not supported. The object sets 'ApplicationUsers' and 'Users' can both contain instances of type 'Xxxx.ApplicationUser'."错误。那么,上面的方法有没有错误呢?我应该在 DbContext
中使用 DbSet 还是应该将 ApplicationUser 作为导航 属性 添加到 Grade 实体?任何帮助将不胜感激...
您应该可以在不向 DbContext
添加 ApplicationUsers
DbSet
的情况下完成此操作,就像这样:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Student> Students { get; set; }
public DbSet<Coordinator> Coordinators { get; set; }
public DbSet<Grade> Grades { get; set; }
...
}
并且可以在 Grade
实体的导航 属性 中使用 Student
类型,您不必使用 ApplicationUser
。
检查您的数据库是否与您的模型同步。你在使用 EF 迁移吗?迁移文件中的 table 应如下所示:(请注意 AspNetUsers
table 中的 Discriminator
字段,以及 Number
和 Room
字段。这些是 TPH 应用于 table 创建的证明。此外,Student
或 Coordinator
的 table 没有包含在迁移中。)
CreateTable(
"dbo.AspNetUsers",
c => new
{
Id = c.String(nullable: false, maxLength: 128),
Name = c.String(),
Surname = c.String(),
Email = c.String(maxLength: 256),
EmailConfirmed = c.Boolean(nullable: false),
PasswordHash = c.String(),
SecurityStamp = c.String(),
PhoneNumber = c.String(),
PhoneNumberConfirmed = c.Boolean(nullable: false),
TwoFactorEnabled = c.Boolean(nullable: false),
LockoutEndDateUtc = c.DateTime(),
LockoutEnabled = c.Boolean(nullable: false),
AccessFailedCount = c.Int(nullable: false),
UserName = c.String(nullable: false, maxLength: 256),
Room = c.String(),
Number = c.Int(),
Discriminator = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => t.Id)
.Index(t => t.UserName, unique: true, name: "UserNameIndex");
CreateTable(
"dbo.Grades",
c => new
{
Id = c.Int(nullable: false, identity: true),
GradeValue = c.Int(nullable: false),
Student_Id = c.String(maxLength: 128),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AspNetUsers", t => t.Student_Id)
.Index(t => t.Student_Id);
如果您的 table 看起来不像这些,请尝试恢复到一个空数据库,删除迁移并重新生成它们,最后用好的迁移更新数据库。
我用你的实体和 DbContext
中的那三个 DbSets
创建了一个虚拟 MVC 项目,生成了迁移并创建了数据库 tables,运行 以下测试代码(来自默认控制器索引方法)。数据已正确保存在数据库中:
public void Test()
{
using (var context = new ApplicationDbContext())
{
var student = new Student
{
Id = "12345",
Name = "John",
Number = 123,
UserName = "Johnny",
Email = "some@email.com"
};
context.Students.Add(student);
context.SaveChanges();
}
using (var context = new ApplicationDbContext())
{
var student = context.Students.Find("12345");
var grade = new Grade { Id = 333, GradeValue = 90, Student = student };
context.Grades.Add(grade);
context.SaveChanges();
}
}
一些问题:
"Multiple object sets" 错误消息说明了有关 "The object sets 'ApplicationUsers' and 'Users'" 的内容。这个 'Users' 对象集来自哪里,你有用户
DbSet
吗?尝试删除它。或者,也许您使用DbSet
的类型参数进行了复制粘贴错误,例如编写public DbSet<wrongType> GoodTypeName
(它发生了)。我对这个声明有疑问:
public class ApplicationUser : IdentityUser<int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>, IUser<int> { ... }
所以我用了这个(基础中没有类型参数class):
public class ApplicationUser : IdentityUser { ... }
似乎带有 Identity 的 MVC 项目的默认实现期望
ApplicationUser
扩展没有类型参数的IdentityUser
的实现。否则会在多个地方出现编译错误,例如在声明ApplicationContext
并尝试使用ApplicationUser
作为基础 classIdentityDbContext
的类型参数时。如果你使用带有类型参数的基础class,我认为
TKey
类型参数应该是String而不是int,除非你改变了User.Id
属性 输入您的自定义实体。也许您因此而收到错误?在数据库中,这些实体的 Id 字段具有 varchar 类型。我将
Grade
属性 的名称更改为GradeValue
(Grade
不是有效的 属性 名称,因为它是class 的名称,我遇到了编译错误)。
如果你需要,我可以发送我用来测试这个的虚拟解决方案的代码。