[英]Foreign key not populating in Entity Framework
我无法正确更新应该链接我的两个实体的表。 更详细地解释......我有两个实体Class和Teacher ,它们的关系为:
下面是这两个实体。
public class Teacher
{
[Required, Key]
public Guid Id { get; private set; }
[StringLength(50)]
public string Name { get; set; }
public string Email { get; set; }
public List<Class> Classes = new List<Class>();
public Teacher()
{
Id = new Guid();
}
public Teacher(Guid id)
{
Id = id;
}
public void AssignClass(Class newClass)
{
Classes.Add(newClass);
}
}
public class Class
{
[Required, Key]
public Guid Id { get; private set; }
[Required, StringLength(20)]
public string Name { get; set; }
[Required, Range(5, 30)]
public int Capacity { get; set; }
public Teacher Teacher { get; set; }
public IEnumerable<StudentClass> StudentClasses { get; set; }
public Class()
{
Id = new Guid();
}
public Class(Guid id)
{
Id = id;
}
}
当我生成迁移时,我按预期在 Classes 表中得到了一个外键TeacherId 。 这是 SQL:
CREATE TABLE [dbo].[Classes] (
[Id] UNIQUEIDENTIFIER NOT NULL,
[Name] NVARCHAR (20) NOT NULL,
[Capacity] INT NOT NULL,
[TeacherId] UNIQUEIDENTIFIER NULL,
CONSTRAINT [PK_Classes] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_Classes_Teachers_TeacherId] FOREIGN KEY ([TeacherId]) REFERENCES [dbo].[Teachers] ([Id])
);
GO
CREATE NONCLUSTERED INDEX [IX_Classes_TeacherId]
ON [dbo].[Classes]([TeacherId] ASC);
我的DBContext派生类如下所示:
public class SchoolDatabaseContext : DbContext
{
public DbSet<Student> Students { get; private set; }
public DbSet<Class> Classes { get; private set; }
public DbSet<Teacher> Teachers { get; private set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
public SchoolDatabaseContext(DbContextOptions<SchoolDatabaseContext> options) : base(options)
{
}
}
这些实体还没有配置。 我使用 DI 将DbContext 提供给控制器,一切看起来都很好。
我的目标是 DDD 类型的结构,但为了使这个问题更容易调试,我已经将所有内容都剥离回控制器,因此它基本上是... controller => DbContext 。
这是我在控制器中的代码:
[HttpPost]
[Route("assign-teacher-to-class")]
public async Task<IActionResult> AssignClass([FromBody] AssignTeacherToClass assignTeacherToClass)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var teacher = await schoolDatabaseContext.Teachers.FindAsync(assignTeacherToClass.TeacherId);
var classToAssign = await schoolDatabaseContext.Classes.FindAsync(assignTeacherToClass.ClassId);
teacher.AssignClass(classToAssign);
schoolDatabaseContext.Entry(teacher).State = EntityState.Modified;
await schoolDatabaseContext.SaveChangesAsync();
return Ok(teacher);
}
当我通过帖子正文中的 id 进行调试时,它们被正确分配给 DTO AssignClass并且调用DbContext以查找每种类型(教师和班级)的数据都很好。 然后我打电话的方法在我的老师类型的类添加到列表类财产(见老师实体代码在开始供参考),然后我保存用的DbContext方法和问题定义这里的变化:在任何阶段做的TeacherId在调试/完成时数据库更新。 我已经尝试了所有我能想到的方法,例如以不同的方式实例化集合、更改集合类型、寻找可能有助于以这种方式映射这些实体的配置、剥离所有额外层、更改属性和类的可访问性等等。
任何帮助都将不胜感激,因为我在这个问题上有点失败,我觉得这种关系应该是相当直接的。 我实际上能够让我的多对多工作与桥梁课程一起工作,所以我很惊讶被困在这个课程上:(
谢谢
尝试这个:
var teacher = await schoolDatabaseContext.Teachers.Include(x => x.Classes).SingleOrDefaultAsync(x => x.Id == assignTeacherToClass.TeacherId);
否则我不认为teacher.Classes
会被DbContext
跟踪。
使用 EF Core 有多种方法可以实现这一点。 如果您称其为文档所称的“相关数据”,则最容易找到。
这是父文档:相关数据
特别是@Y Stroli 已经说明了Eager Loading方法。
下面的示例显示在预先加载参考中以加载多个级别的相关数据:
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ThenInclude(author => author.Photo)
.ToList();
}
从 EF Core 5.0 开始,您还可以过滤包含:
using (var context = new BloggingContext())
{
var filteredBlogs = context.Blogs
.Include(blog => blog.Posts
.Where(post => post.BlogId == 1)
.OrderByDescending(post => post.Title)
.Take(5))
.ToList();
}
根据 lvan 的建议,您应该更改public List<Class> Classes = new List<Class>();
到public List<Class> Classes { get; set; } = new List<Class>();
public List<Class> Classes { get; set; } = new List<Class>();
.
对于您当前的代码,您似乎想添加Class
并返回teacher
,如果是这样,则需要将现有的classes
包含到teacher
中,如下所示,否则,它只会返回新添加的课程。
public async Task<IActionResult> AssignClass()
{
var assignTeacherToClass = new AssignTeacherToClass {
TeacherId = new Guid("52abe5e0-bcd4-4827-893a-26b24ca7b1c4"),
ClassId =new Guid("50354c76-c9e8-4fc3-a7c9-7644d47a6854")
};
var teacher = await _context.Teachers.Include(t => t.Classes).FirstOrDefaultAsync(t => t.Id == assignTeacherToClass.TeacherId);
var classToAssign = await _context.Classes.FindAsync(assignTeacherToClass.ClassId);
teacher.AssignClass(classToAssign);
_context.Entry(teacher).State = EntityState.Modified;
await _context.SaveChangesAsync();
return Ok(teacher);
}
再注意一点,你需要配置SerializerSettings.ReferenceLoopHandling
类的
services.AddMvc()
.AddJsonOptions(opt => {
opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
您需要定义教师和班级之间的连接。
protected override void OnModelCreating(Modelbuilder modelBuilder)
{
modelBuilder.Entity<Class>()
.HasOne<Teacher>(p => p.Teacher)
.WithMany(q => q.Classes)
.HasForeignKey(r => r.TeacherId);
}
还将 TeacherId 道具添加到 Class。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.