-
EF Core基础映射——C#编写实体与表关联
第一部分:C#基础入门
EF Core基础映射——实体与表关联
实例介绍
EF Core(Entity Framework Core)是.NET生态中最流行的ORM框架(对象关系映射),它能将C#实体类与数据库表无缝关联,让开发者用面向对象的方式操作数据库,无需手写复杂SQL。实体与表的关联是EF Core的核心能力,包括类→表、属性→列、主键/外键配置、导航属性关联等关键环节。
本节通过“学生选课系统”场景,带你掌握EF Core实体与表关联的核心配置:数据vb.net教程C#教程python教程SQL教程access 2010教程注解、Fluent API、导航属性(一对多/多对多),让你快速上手EF Core的基础映射。
需求分析
设计一个学生选课系统的实体映射方案,核心需求:
1.实体→表映射:将Student类映射到Students表,Course类映射到Courses表;
2.属性→列映射:指定列名、数据类型、长度(如Student.Name映射到StudentName列,类型为nvarchar(100));
3.主键配置:设置Student.Id和Course.Id为主键;
4.关联配置:实现学生与课程的多对多关联(一个学生可选多门课,一门课可被多个学生选);
5.导航属性:通过导航属性Student.Courses和Course.Students查询关联数据;
6.测试验证:添加数据并查询关联关系,验证映射正确性。
代码实现
前置条件:
安装EF Core包:Microsoft.EntityFrameworkCore、Microsoft.EntityFrameworkCore.SqlServer;
创建数据库SchoolDB(或复用之前的数据库);
-
实体类定义(数据注解配置)
csharp
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
// 学生实体:映射到Students表
[Table("Students")] // 指定表名(若类名与表名一致可省略)
public class Student {
[Key] // 主键注解
public int Id { get; set; }
[Column("StudentName", TypeName = "nvarchar(100)")] // 列名+类型
[Required] // 非空约束
public string Name { get; set; }
public int Age { get; set; }
// 导航属性:学生选的课程(多对多)
public ICollection<StudentCourse> StudentCourses { get; set; } = new List<StudentCourse>();
}
// 课程实体:映射到Courses表
[Table("Courses")]
public class Course {
[Key]
public int Id { get; set; }
[Column("CourseName", TypeName = "nvarchar(50)")]
[Required]
public string Name { get; set; }
public int Credits { get; set; }
// 导航属性:选这门课的学生(多对多)
public ICollection<StudentCourse> StudentCourses { get; set; } = new List<StudentCourse>();
}
// 中间实体:学生-课程关联表(EF Core 3.x需显式定义,5+可省略)
[Table("StudentCourses")]
public class StudentCourse {
[Key]
public int Id { get; set; }
[ForeignKey("Student")] // 外键:关联Student表的Id
public int StudentId { get; set; }
[ForeignKey("Course")] // 外键:关联Course表的Id
public int CourseId { get; set; }
// 引用导航属性:关联的学生和课程
public Student Student { get; set; }
public Course Course { get; set; }
}
-
DbContext配置(Fluent API增强映射)
csharp
using Microsoft.EntityFrameworkCore;
public class SchoolDbContext : DbContext {
// 数据库连接字符串
private const string ConnStr = @"Data Source=(localdb)MSSQLLocalDB;Initial Catalog=SchoolDB;Integrated Security=True;";
// DbSet:对应数据库表
public DbSet<Student> Students { get; set; }
public DbSet<Course> Courses { get; set; }
public DbSet<StudentCourse> StudentCourses { get; set; }
// 配置数据库连接
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
optionsBuilder.UseSqlServer(ConnStr);
}
// Fluent API:更灵活的映射配置(优先级高于数据注解)
protected override void OnModelCreating(ModelBuilder modelBuilder) {
// 配置Student实体
modelBuilder.Entity<Student>()
.ToTable("Students") // 确认表名
.Property(s => s.Name)
.HasMaxLength(100) // 最大长度
.IsRequired(); // 非空
// 配置Course实体
modelBuilder.Entity<Course>()
.ToTable("Courses")
.Property(c => c.Name)
.HasMaxLength(50)
.IsRequired();
// 配置StudentCourse中间表(多对多关联)
modelBuilder.Entity<StudentCourse>()
.HasKey(sc => sc.Id); // 复合主键可选:sc => new { sc.StudentId, sc.CourseId }
// 多对多关联:Student ↔ Course
modelBuilder.Entity<StudentCourse>()
.HasOne(sc => sc.Student) // StudentCourse属于一个Student
.WithMany(s => s.StudentCourses) // Student有多个StudentCourse
.HasForeignKey(sc => sc.StudentId); // 外键StudentId
modelBuilder.Entity<StudentCourse>()
.HasOne(sc => sc.Course) // StudentCourse属于一个Course
.WithMany(c => c.StudentCourses) // Course有多个StudentCourse
.HasForeignKey(sc => sc.CourseId); // 外键CourseId
}
}
-
测试代码(验证关联关系)
csharp
class Program {
static void Main() {
using (var db = new SchoolDbContext()) {
// 1. 添加测试数据
var math = new Course { Name = "高等数学", Credits = 4 };
var english = new Course { Name = "大学英语", Credits = 3 };
var student = new Student { Name = "张三", Age = 20 };
// 关联学生和课程
student.StudentCourses.Add(new StudentCourse { Course = math });
student.StudentCourses.Add(new StudentCourse { Course = english });
db.Students.Add(student);
db.SaveChanges(); // 保存到数据库
// 2. 查询关联数据:获取张三选的课程
var zhangSan = db.Students.First(s => s.Name == "张三");
Console.WriteLine($"张三选的课程:");
foreach (var sc in zhangSan.StudentCourses) {
Console.WriteLine($"- {sc.Course.Name}({sc.Course.Credits}学分)");
}
}
}
}
逐行讲解
-
实体类注解配置
[Table("Students")]:将Student类映射到数据库Students表,若类名与表名一致可省略;
[Key]:标记属性为主键,EF Core默认识别Id或类名+Id(如StudentId)作为主键;
[Column("StudentName", TypeName = "nvarchar(100)")]:指定属性映射到StudentName列,类型为nvarchar(100);
[ForeignKey("Student")]:标记StudentId为外键,关联Student实体;
导航属性:StudentCourses是集合导航属性,代表学生关联的课程记录; -
Fluent API配置
OnModelCreating:EF Core的核心配置方法,用Fluent API实现复杂映射(优先级高于数据注解);
HasOne/WithMany:配置一对多关联,HasOne表示“属于一个”,WithMany表示“有多个”;
HasForeignKey:指定外键列,明确关联关系;
HasMaxLength/IsRequired:补充列约束(长度、非空),与数据注解功能一致但更灵活; -
测试代码逻辑
添加关联数据:通过Student.StudentCourses.Add关联学生和课程,EF Core自动处理外键;
查询关联数据:通过导航属性zhangSan.StudentCourses直接获取学生选的课程,无需手动写JOIN SQL;
基础知识拓展 - EF Core映射三方式对比
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 约定优先 | 零配置,快速开发 | 灵活性差,复杂场景不适用 | 简单项目、原型开发 |
| 数据注解 | 代码简洁,直观易读 | 配置能力有限,耦合实体类 | 中等复杂度项目 |
| Fluent API | 配置灵活,解耦实体类 | 代码量略大 | 复杂项目、需精细控制映射 |
-
导航属性核心概念
引用导航:指向单个实体(如StudentCourse.Student);
集合导航:指向多个实体(如Student.StudentCourses);
双向导航:实体双方都配置导航属性(如Student ↔ Course),便于双向查询; -
多对多关联的简化配置(EF Core 5+)
EF Core 5.0后支持无需中间实体的多对多关联,直接配置:
csharp
modelBuilder.Entity<Student>()
.HasMany(s => s.Courses) // Student有多个Course
.WithMany(c => c.Students) // Course有多个Student
.UsingEntity<Dictionary<string, object>>( // 隐式中间表
"StudentCourses", // 中间表名
j => j.HasOne<Course>().WithMany(),
j => j.HasOne<Student>().WithMany()
);
总结
EF Core实体与表关联的核心是将对象模型转换为关系模型,通过约定、注解或Fluent API实现映射。导航属性是关联查询的关键,能让你用面向对象的方式操作关联数据,大幅减少SQL编写量。
实际开发中,建议优先使用Fluent API(解耦实体类),结合约定优先(减少重复配置),复杂场景再补充数据注解。掌握这些基础映射,你就能快速上手EF Core的日常开发!
本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49437.html










