EF Core 5 TPT 和身份 (CRUD)

Posted

技术标签:

【中文标题】EF Core 5 TPT 和身份 (CRUD)【英文标题】:EF Core 5 TPT and Identity (CRUD) 【发布时间】:2021-03-25 14:13:22 【问题描述】:

我想使用 EF Core 的 TPT 功能,我想将它与 Identity 集成,但我在添加到数据库时遇到了一些问题。

 public class ApplicationUser : IdentityUser<Guid>
    
        public string FirstName  get; set; 
        public string LastName  get; set; 
        public string NationalCode  get; set; 
        public DateTime BirthDate  get; set; 
        public Gender Gender  get; set; 
    
 public class Student : ApplicationUser
    
        public string FatherName  get; set; 
        public string PlaceOfBirth  get; set; 
        public string Address  get; set; 
        public string HomePhoneNumber  get; set; 
    
 public class Teacher : ApplicationUser
    
        public string FieldOfStudy  get; set; 
        public AcademicDegree AcademicDegree get; set; 
        public int YearsOfExperience  get; set; 
    

这是我的数据库上下文类

 public class SchoolMgmtContext : IdentityDbContext<ApplicationUser,ApplicationRole,Guid>
    


        public SchoolMgmtContext(DbContextOptions<SchoolMgmtContext> dbContextOptions)
            :base(dbContextOptions)
        

        

        public DbSet<Teacher> Teachers  get; set; 
        public DbSet<Student> Students  get; set; 

        protected override void OnModelCreating(ModelBuilder builder)
        
            base.OnModelCreating(builder);

            builder.Entity<ApplicationUser>().ToTable("Users");
            builder.Entity<Student>().ToTable("Students");
            builder.Entity<Teacher>().ToTable("Teachers");

            builder.Entity<ApplicationRole>().ToTable("Roles");
        
    

一切正常。

但我不知道如何插入新教师或新学生。

例如,这是添加新教师的代码。

    public IActionResult CreateTeacher()
        

            ViewBag.Users = new SelectList(_db.Users.Select(u => new  u.Id, FullName = u.FirstName + " " + u.LastName ), "Id", "FullName");
            return View();
        

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> CreateTeacher(TeacherCreationDto teacherCreationDto)
        
            if (ModelState.IsValid)
            
                var newTeacher = new Teacher()
                
                    AcademicDegree = teacherCreationDto.AcademicDegree,
                    FieldOfStudy = teacherCreationDto.FieldOfStudy,
                    YearsOfExperience = teacherCreationDto.YearsOfExperience,
                    Id= teacherCreationDto.UserId
                ;

                 // _db.Teachers.Update(newTeacher); // here I tested Attach - Add - Update, but none of this work.
                await _db.SaveChangesAsync();
                return RedirectToAction(nameof(Index));
            
            else
            
                ViewBag.Users = new SelectList(_db.Users.Select(u => new  u.Id, FullName = u.FirstName + " " + u.LastName ), "Id", "FullName");
                return View(teacherCreationDto);
            
         
        

我应该如何添加新的学生或教师?谢谢,

更新:

【问题讨论】:

您添加新 TPT 记录的方式与添加新 TPH 或常规记录的方式相同 - 通过使用某些 Add 方法,例如对于新的Teacher,其中任何一个都应该这样做-_db.Add(newTeacher)_db.Users.Add(newTeacher)_db.Teachers.Add(newTeacher)。你是什​​么意思“没有这个工作”?您是否遇到异常或? @IvanStoev 感谢您的回答,但这些方法都不起作用,是的,我有 2 个例外,第一个是关于 DbConcurreny,第二个是关于无法插入重复的主键。 这是因为这里 Id= teacherCreationDto.UserId 您正在将现有 ID 分配给新用户(教师)。使用 Id = Guid.NewGuid() 或者不分配它,让 EFC 自动生成它。 @IvanStoev 为什么在 Microsoft docs 或 *** 中没有关于带有 TPT 的 EF Core 5 中的 CRUD 的代码? 嗯,看来你误解了继承的概念。教师用户,学生也是如此。您不会创建单独的用户并将其“连接”到教师或学生 - 您创建的新教师也是新用户。 TPT 和 TPH 只是在数据库中存储此类继承数据的不同方式。如果您想将对象“连接”到 User,则不应使用继承,而应使用 FK 关系。 【参考方案1】:

我同意“Ivan Stoev”的观点:您不能从 User 继承 Student 和 Teacher。 如果需要,那么您的“TeacherCreationDto”应包含以下字段/有效负载:

“学习领域、经验年限、名字、姓氏、国家代码、出生日期、性别、ID、用户名、规范化用户名、电子邮件、规范化电子邮件、电子邮件确认、密码哈希、SecurityStamp、ConcurrencyStamp、电话号码、电话号码确认、TwoFactorEnabled、LockoutEnd、LockoutEnabled、AccessFailedCount” .. . 当我通过使用自动脚手架创建控制器/api时,以下编码对我有用:

    public async Task<IActionResult> Create([Bind("FieldOfStudy,YearsOfExperience,FirstName,LastName,NationalCode,BirthDate,Gender,Id,UserName,NormalizedUserName,Email,NormalizedEmail,EmailConfirmed,PasswordHash,SecurityStamp,ConcurrencyStamp,PhoneNumber,PhoneNumberConfirmed,TwoFactorEnabled,LockoutEnd,LockoutEnabled,AccessFailedCount")] Teacher teacher)
    
        if (ModelState.IsValid)
        
            teacher.Id = Guid.NewGuid();
            _context.Add(teacher);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        
        return View(teacher);
    

【讨论】:

谢谢,这是我在 Ivan github.com/ArminShoeibi/SchoolManagementSystem 的帮助下创建的示例项目 好吧,除非您的用例暗示TeacherStudent 是您系统的Users。但我也同意,通常您将用户与您的应用程序逻辑联系起来,因此在大多数情况下,外键更有可能【参考方案2】:

正如问题作者所说,Microsoft official docs 中没有足够的文档。

与我们对 TPT 的期望相反,当我们想要向派生表中插入新记录时,我们必须提供派生类型(包括父类型)的所有属性,因为 EF Core 会在内部创建两个插入语句,一个用于父表,另一个用于派生表。

因此,如果我们提供现有 id 来创建新记录,则会抛出带有“重复键”消息的 DbUpdateException

我可以说,当我们的继承重叠时,这种方法是不合适的(考虑一个用户既是学生又是教师的情况,甚至想从用户更改为学生)。

最后,我建议您手动创建 TPT 关系。你的模型应该是这样的:

 public class ApplicationUser : IdentityUser<Guid>
    
        public string FirstName  get; set; 
        public string LastName  get; set; 
        public string NationalCode  get; set; 
        public DateTime BirthDate  get; set; 
        public Gender Gender  get; set; 
    
    public class Student 
    
        [ForignKey(nameof(User))]
        public Guid Id get; set;  // Id and forignKey 
        
        /* Student specific fields */

        public ApplicationUser User get; set; 
    
    public class Teacher
    
        [ForignKey(nameof(User))]
        public Guid Id get; set;  // Id and forignKey 

        /* Teacher specific fields */

        public ApplicationUser User get; set; 
    

【讨论】:

以上是关于EF Core 5 TPT 和身份 (CRUD)的主要内容,如果未能解决你的问题,请参考以下文章

EF Core从TPH迁移到TPT

使用 EF Code First 继承 – 每个类型的表 (TPT)

如何在使用 TPT 层次结构时首先在 EF 代码中实现并发

Inheritance with EF Code First: Part 2 – Table per Type (TPT)

如何使用 FreeSql 无缝接替 EF Core ?

代码优先 TPT 和级联删除