EF Core,我应该在 class 中添加外键 ID 吗?

EF Core, Should I add foreign key Id in class?

我从不同的站点阅读了一些教程,一些添加了外键 ID,一些没有,还有一些添加了虚拟。

例如:

class Class
{
    public int Id { get; set; }
    public string ClassName { get; set; }
    public List<Student> Students { get; set; }
}

class Student
{
    public int Id { get; set; }
    public string StudentName { get; set; }
    public Class class { get; set; }
}

Student class 中,有些在 Class 上使用虚拟,有些在 Student class 中使用 ClassId

这方面的最佳做法是什么?我将 EF Core 3 与 .NET Core 3.1(最新版本)一起使用

在 EF 的上下文中,将 属性 标记为虚拟允许 EF 使用延迟加载来加载它。为了使延迟加载工作,EF 必须创建一个代理对象,该代理对象使用在首次访问时加载引用实体的实现来覆盖您的虚拟属性。如果您不将 属性 标记为虚拟,则延迟加载将无法使用它。

对于 Web 应用程序,也许最好不要使用延迟加载,因为延迟加载会拉取大量数据。

假设您想从 objects/classes 生成数据库 model/relations(使用 EF 核心进行代码优先迁移),最重要的部分是实体的映射配置(在您的模型属性,或使用 Fluent API).

如果您对 "Id" 属性 格式没有任何特定要求,只需将其设置为由数据库生成即可。

对于您的情况,我将按以下方式配置它:

class Class
{
    public int ClassId { get; set; }
    public string ClassName { get; set; }
    public ICollection<Student> Students { get; set; }
}

class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }
    public int ClassId { get; set; }
    public Class class { get; set; }
}

并且,使用 Fluent API 配置映射的一种方法可能类似于:

public class StudentConfiguration : IEntityTypeConfiguration<Student>
{
    public void Configure(EntityTypeBuilder<Student> builder)
    {
        builder
             .HasKey(s => s.StudentId);

        builder
            .Property(s => s.StudentId)
            .UseIdentityColumn();

        builder
            .Property(c => c.StudentName)
            .HasMaxLength(<studentName_maxLength>);

        builder
            .HasOne(s => s.Class)
            .WithMany(c => c.Students)
            .HasForeignKey(s => s.ClassId);

        builder
            .ToTable("Student");
    }
}

public class ClassConfiguration : IEntityTypeConfiguration<Class>
{
    public void Configure(EntityTypeBuilder<Class> builder)
    {
        builder
             .HasKey(c => c.ClassId);

        builder
            .Property(c => c.ClassId)
            .UseIdentityColumn();   

        builder
            .Property(c => c.ClassName)
            .HasMaxLength(<className_maxLength>);

        builder
            .HasMany(c => c.Students)
            .WithOne(s => s.Class);

        builder
            .ToTable("Class");
    }
}

可以找到有关配置 Fluent API 一对多映射的更多详细信息 here

该模式称为“Shadow-property”。假设我们想通过 ClassId.

获得所有 Students

使用 Shadow Property,2 个访问数据的选项:

context.Classes.Include(x => x.Students).FirstOrDefault(x => x.Id == classId)?.Students;

//we have to know the name of foreign key from db.
context.Students.Where(x => EF.Property<int>(x, "ClassId") == classId).ToList(); 

使用显式外键,代码为:

context.Students.Where(x => x.ClassId == classId).ToList();

第二个和第三个代码避免了 join,看起来性能更好。但哪一种是两种风格中最好的?这实际上取决于项目和编码风格偏好。

虽然第二种样式很容易设置:

class MyContext : DbContext
{
    public DbSet<Student> Students { get; set; }
    public DbSet<Class> Classes { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Add the shadow property to the model
        modelBuilder.Entity<Student>()
            .Property<int>("ClassId");

        // Use the shadow property as a foreign key
        modelBuilder.Entity<Student>()
            .HasOne(p => p.Class)
            .WithMany(b => b.Students)
            .HasForeignKey("ClassId");
    }
}

这里有 2 个参考:

  1. https://docs.microsoft.com/en-us/ef/core/modeling/shadow-properties

  2. https://docs.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key