【问题标题】:How to use TPH (Table Per Hierarchy) with ASP.NET Identity 2如何在 ASP.NET Identity 2 中使用 TPH(按层次结构表)
【发布时间】:2016-09-23 13:56:59
【问题描述】:

我在MVC5 项目中使用ASP.NET Identity 2,有两种类型的用户类,分别称为StudentCoordinator,如下所示。另一方面,我尝试遵循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 类,并且没有将其添加到我的DbContext 中,如下所示:

public DbSet<ApplicationUser> ApplicationUsers { get; set; }

另一方面,还有另一个实体具有 Student (not 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 时遇到 “不支持每种类型的多个对象集。对象集 'ApplicationUsers' 和 'Users' 都可以包含类型的实例'Xxxx.ApplicationUser'。” 错误。那么,关于上述方法是否有任何错误?我应该在DbContext 中使用 DbSet,我应该将 ApplicationUser 作为导航属性添加到 Grade 实体吗?任何帮助将不胜感激...

【问题讨论】:

  • @ChrisPratt 你对这个问题有什么想法吗?
  • @DavidG 您能否看一下这个问题,因为您之前已经对我的类似问题有过意见?谢谢...

标签: asp.net-mvc entity-framework ef-code-first asp.net-identity table-per-hierarchy


【解决方案1】:

您应该可以在不向您的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 迁移吗?迁移文件中的表应如下所示:(注意AspNetUsers 表中的Discriminator 字段,以及NumberRoom 字段。这些是TPH 应用于表创建的证明。另外,没有StudentCoordinator 的表已包含在迁移中。)

        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);

如果您的表看起来不像这些,请尝试恢复到空数据库,删除迁移并再次生成它们,最后使用良好的迁移更新数据库。

我用你的实体和DbContext 中的那三个DbSets 创建了一个虚拟 MVC 项目,生成了迁移并创建了数据库表,并运行以下测试代码(来自默认控制器索引方法)。数据已正确保存在数据库中:

    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();
        }
    }

一些问题:

  • “多个对象集”错误消息说明了“对象集 'ApplicationUsers' 和 'Users'”。这个“用户”对象集来自哪里,你有用户DbSet吗?尝试删除它。或者,您可能在 DbSet 的类型参数上出现了复制粘贴错误,类似于编写 public DbSet&lt;wrongType&gt; GoodTypeName(发生这种情况)。

  • 我对此声明有疑问:

    public class ApplicationUser : IdentityUser<int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>, IUser<int>
    { ... }
    

    所以我用了这个(基类中没有类型参数):

    public class ApplicationUser : IdentityUser
    { ... }
    

    似乎带有 Identity 的 MVC 项目的默认实现期望 ApplicationUser 扩展 IdentityUser 的实现而没有类型参数。否则会在几个地方出现编译错误,例如在声明ApplicationContext 并尝试使用ApplicationUser 作为基类IdentityDbContext 的类型参数时。

  • 如果您使用带有类型参数的基类,我认为TKey 类型参数应该是String 而不是int,除非您在自定义实体中更改了User.Id 属性类型。也许您因此而遇到错误?在数据库中,这些实体的 Id 字段的类型为 varchar。

  • 我将 Grade 属性的名称更改为 GradeValueGrade 不是有效的属性名称,因为它是类的名称,我遇到了编译错误)。

如果您需要,我可以发送我用来测试的虚拟解决方案的代码。

【讨论】:

  • Muchisima gracias Diana,我已经仔细阅读了你所有的 cmets。我将身份类型从 string 更改为 int ,那一侧没有问题。另一方面,数据库表实际上具有相同的鉴别器和自定义列,并且没有额外的表。所以,这意味着一切正常。 Saludos cordiales y muchas gracias otra vez :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-09
  • 2014-10-16
  • 2019-09-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多