【问题标题】:EF Core 5 TPT and Identity (CRUD)EF Core 5 TPT 和身份 (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 或 StackOverflow 中没有关于带有 TPT 的 EF Core 5 中的 CRUD 的代码?
  • 嗯,看来你误解了继承的概念。教师是用户,学生也是如此。您不会创建单独的用户并将其“连接”到教师或学生 - 您创建的新教师也是新用户。 TPT 和 TPH 只是在数据库中存储此类继承数据的不同方式。如果您想将对象“连接”到 User,则不应使用继承,而应使用 FK 关系。

标签: asp.net-core entity-framework-core table-per-type ef-core-5.0


【解决方案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 的帮助下创建的示例项目
  • 好吧,除非您的用例暗示Teacher 和Student 是您系统的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; }
    }

【讨论】:

    猜你喜欢
    • 2021-07-27
    • 1970-01-01
    • 2017-06-16
    • 2021-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多