实体框架中未填充外键

时间:2019-08-29 22:39:45

标签: c# asp.net-core .net-core entity-framework-core

我无法正确连接应该连接两个实体的表。为了更详细地说明...我有两个实体,老师,它们之间的关系形式为:

  1. 老师可以分配给许多班级
  2. 班上只能有一位老师。

下面是这两个实体。

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 均不会更新。我已经尝试过所有可以想到的方法,例如以不同的方式实例化集合,更改集合类型,寻找可能有助于以这种方式映射这些实体的配置,剥离所有额外的层,更改属性和类的可访问性等等。

任何帮助都将不胜感激,因为我在这一方面有些失败,我觉得这种关系应该很简单。实际上,我能够使很多人参加桥梁课程,所以我很惊讶地被困在这个:(

谢谢

4 个答案:

答案 0 :(得分:1)

有多种方法可以使用EF Core实现。最容易找到您是否称其为“文档相关数据”。

这是父文档:Related Data

特别是@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();
}

答案 1 :(得分:0)

尝试一下:

 var teacher = await schoolDatabaseContext.Teachers.Include(x => x.Classes).SingleOrDefaultAsync(x => x.Id == assignTeacherToClass.TeacherId);

我认为teacher.Classes不会被DbContext跟踪。

答案 2 :(得分:0)

根据lvan的建议,您应该将public List<Class> Classes = 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);

答案 3 :(得分:0)

您需要定义教师与班级之间的联系。

<soap:Body>
    <ProcessRequest>
        <request xsi:type="GetNotification">
            <node1>val1</node2>
            <node2>val2</node2>
        </request>
    </ProcessRequest>
</soap:Body>

还向班级添加TeacherId属性。