【EF Core】 4. 关系配置、自引用树

EF Core的学习之路01 什么时ORM、EF Core与其他ORM比较、EF Core与EF比较搭建EF Core环境、EF Core增删改查、EF Core实体配置 阅读详情

本系列专栏基于杨中科老师的《ASP.NET Core技术内幕与项目 实战》,本人记录梳理的学习笔记,有部分的增补和省略。更全面系统的讲解,请看杨老师的视频课:【.NET教程,.Net Core视频教程,杨中科主讲】。

一、关系配置

EF Core 中实体间的关系是业务模型的核心,正确配置一对一、一对多、多对多关系,能保证数据存储的完整性和查询的便捷性。所有关系配置都基于 EntityTypeBuilder(可通过实现 IEntityTypeConfiguration<T> 或 OnModelCreating 方法配置),核心是通过 HasOne/HasMany 声明 “当前实体” 的导航属性,通过 WithOne/WithMany 声明 “关联实体” 的导航属性,形成双向绑定。

1. 一对一关系

一对一关系要求两个实体严格一一对应,必须显式声明外键属性(否则 EF Core 可能无法正确识别关系),且只需在其中一个实体的配置中完成即可。

语法
// 以“用户-用户详情”为例,在UserProfile配置中声明一对一关系
builder.HasOne<User>(p => p.User)       // 详情表关联到一个用户(当前实体:UserProfile)
       .WithOne(u => u.Profile)        // 一个用户对应一个详情(关联实体:User)
       .HasForeignKey<UserProfile>(p => p.UserId); // 声明外键(外键所在的实体:UserProfile)
示例

实体类(显式声明外键):

// 用户主表
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    // 导航属性:关联用户详情
    public UserProfile Profile { get; set; }
}

// 用户详情表(外键所在表)
public class UserProfile
{
    public int Id { get; set; }
    public string Address { get; set; }
    // 显式外键属性(必须声明)
    public int UserId { get; set; }
    // 导航属性:关联用户
    public User User { get; set; }
}

配置类:

public class UserProfileConfig : IEntityTypeConfiguration<UserProfile>
{
    public void Configure(EntityTypeBuilder<UserProfile> builder)
    {
        builder.ToTable("T_UserProfiles");
        // 配置一对一关系
        builder.HasOne<User>(p => p.User)
               .WithOne(u => u.Profile)
               .HasForeignKey<UserProfile>(p => p.UserId)
               .IsRequired(); // 外键非空,强化一对一约束
    }
}

2. 一对多关系

一对多是最常用的关系(如 “文章 - 评论”“订单 - 订单项”),建议在 “多” 的一方配置(更符合业务逻辑),也可根据需求配置单向 / 双向导航。

语法
// 基础语法(双向导航)
HasOne<主实体>(c => c.主实体导航属性)  // 多的一方关联一个主实体
.WithMany(a => a.从实体集合属性);       // 主实体关联多个从实体

// 单向导航(仅从“多”到“一”,主实体无集合导航)
HasOne<主实体>(c => c.主实体导航属性)
.WithMany(); // 无集合参数,表示单向
示例

实体类:

// 主实体:文章
public class Article
{
    public long Id { get; set; }
    public string Title { get; set; }
    // 导航属性:一个文章有多个评论
    public List<Comment> Comments { get; set; } = new List<Comment>();
}

// 从实体:评论(多的一方)
public class Comment
{
    public long Id { get; set; }
    public string Message { get; set; }
    // 可选:显式声明外键(推荐,便于手动赋值和查询)
    public long ArticleId { get; set; }
    // 导航属性:一个评论属于一个文章
    public Article Article { get; set; }
}

配置类:

public class CommentConfig : IEntityTypeConfiguration<Comment>
{
    public void Configure(EntityTypeBuilder<Comment> builder)
    {
        builder.ToTable("T_Comments");
        
        // 配置一对多关系
        builder.HasOne<Article>(c => c.Article) // 评论关联一个文章
               .WithMany(a => a.Comments)       // 一个文章有多个评论
               .HasForeignKey(c => c.ArticleId) // 显式指定外键(若实体声明了外键则建议加)
               .IsRequired();                   // 评论必须关联文章(外键非空)
        
        // 字段约束
        builder.Property(c => c.Message).IsRequired().IsUnicode();
    }
}
关联数据查询

通过 Include 方法预加载关联数据(避免懒加载的性能问题):

// 读取ID=1的文章及其所有评论
using (var ctx = new MyDbContext())
{
    Article a = ctx.Articles
                   .Include(a => a.Comments) // 预加载评论集合
                   .Single(a => a.Id == 1);
    
    Console.WriteLine($"文章标题:{a.Title}");
    foreach (Comment c in a.Comments)
    {
        Console.WriteLine($"评论ID:{c.Id},内容:{c.Message}");
    }
}

注意:

  • 若实体中未显式声明外键,EF Core 会自动生成隐式外键(如 ArticleId),但手动声明外键更易维护(便于手动赋值、查询过滤);
  • 除非业务不需要直接操作外键,否则建议显式声明(所谓 “重复” 问题在实际开发中几乎可忽略,反而能提升代码可读性)。

3. 多对多关系

多对多关系(如 “学生 - 课程”“商品 - 订单”)需要中间关联表,EF Core 5+ 支持无需显式定义中间实体,直接通过 UsingEntity 指定关联表名即可。

语法
HasMany<关联实体>(c => c.集合导航属性)  // 当前实体关联多个关联实体
.WithMany(a => a.反向集合导航属性)       // 关联实体关联多个当前实体
.UsingEntity(j => j.ToTable("关联表名")); // 指定中间关联表的表名
示例

实体类:

// 学生
public class Student
{
    public long Id { get; set; }
    public string Name { get; set; }
    // 导航属性:一个学生选多门课程
    public List<Course> Courses { get; set; } = new List<Course>();
}

// 课程
public class Course
{
    public long Id { get; set; }
    public string CourseName { get; set; }
    // 导航属性:一门课程被多个学生选
    public List<Student> Students { get; set; } = new List<Student>();
}

配置类:

public class StudentConfig : IEntityTypeConfiguration<Student>
{
    public void Configure(EntityTypeBuilder<Student> builder)
    {
        builder.ToTable("T_Students");
        
        // 配置多对多关系
        builder.HasMany<Course>(s => s.Courses)
               .WithMany(c => c.Students)
               .UsingEntity(j => j.ToTable("T_StudentCourse")); // 中间表名:T_StudentCourse
    }
}
自定义中间表字段

若需要给中间表加额外字段(如选课时间),需显式定义中间实体:

// 中间实体:学生选课记录
public class StudentCourse
{
    public long StudentId { get; set; }
    public long CourseId { get; set; }
    public DateTime SelectTime { get; set; } // 额外字段:选课时间
    
    // 导航属性
    public Student Student { get; set; }
    public Course Course { get; set; }
}

// 配置多对多(关联中间实体)
builder.HasMany<Course>(s => s.Courses)
       .WithMany(c => c.Students)
       .UsingEntity<StudentCourse>(
           j => j.HasOne<Course>(sc => sc.Course).WithMany().HasForeignKey(sc => sc.CourseId),
           j => j.HasOne<Student>(sc => sc.Student).WithMany().HasForeignKey(sc => sc.StudentId),
           j =>
           {
               j.ToTable("T_StudentCourse");
               j.Property(sc => sc.SelectTime).IsRequired(); // 约束额外字段
           });

二、自引用的组织结构树

自引用关系是特殊的一对多关系(实体自身关联自身),常用于实现 “部门树”“分类树”“组织架构树” 等层级结构。

1. 实体类定义

public class OrgUnit
{
    public long Id { get; set; }
    public string Name { get; set; } // 组织单元名称(如“技术部”“研发组”)
    
    // 导航属性:父级组织(一对一)
    public OrgUnit Parent { get; set; }
    // 可选:显式声明父级外键(推荐,便于查询和赋值)
    public long? ParentId { get; set; } // 可空,根节点无父级
    
    // 导航属性:子级组织(一对多)
    public List<OrgUnit> Children { get; set; } = new List<OrgUnit>();
}

2. 关系配置

public class OrgUnitConfig : IEntityTypeConfiguration<OrgUnit>
{
    public void Configure(EntityTypeBuilder<OrgUnit> builder)
    {
        builder.ToTable("T_OrgUnits");
        
        // 配置自引用一对多关系
        builder.HasOne<OrgUnit>(u => u.Parent) // 当前组织关联一个父组织
               .WithMany(p => p.Children)     // 一个父组织有多个子组织
               .HasForeignKey(u => u.ParentId) // 指定外键(ParentId)
               .OnDelete(DeleteBehavior.Restrict); // 级联删除限制(避免删除父级时误删所有子级)
    }
}

3. 数据操作示例

添加层级数据

(根节点 + 子节点)

using (var ctx = new MyDbContext())
{
    // 根节点:总公司
    var root = new OrgUnit
    {
        Name = "总公司",
        ParentId = null, // 根节点无父级
        Children = new List<OrgUnit>
        {
            // 一级子节点:技术部
            new OrgUnit { Name = "技术部", ParentId = 0 }, // 先临时赋值,Add后自动更新
            // 一级子节点:市场部
            new OrgUnit 
            { 
                Name = "市场部", 
                Children = new List<OrgUnit>
                {
                    // 二级子节点:市场部-华东区
                    new OrgUnit { Name = "华东区" }
                }
            }
        }
    };
    
    // 只需添加根节点,EF Core 会自动级联添加所有子节点
    ctx.OrgUnits.Add(root);
    ctx.SaveChanges();
}
查询层级数据

(加载所有子节点)

// 加载根节点及其所有子节点(递归预加载)
var rootOrg = ctx.OrgUnits
                .Include(o => o.Children) // 加载一级子节点
                .ThenInclude(c => c.Children) // 加载二级子节点(可多层嵌套)
                .First(o => o.ParentId == null);

// 遍历输出组织架构
Console.WriteLine($"根节点:{rootOrg.Name}");
foreach (var firstChild in rootOrg.Children)
{
    Console.WriteLine($"- {firstChild.Name}");
    foreach (var secondChild in firstChild.Children)
    {
        Console.WriteLine($"  - {secondChild.Name}");
    }
}

注意:

  • 根节点的 ParentId 必须为 null,否则会导致层级错乱;
  • 级联删除需谨慎:建议设置 OnDelete(DeleteBehavior.Restrict),避免删除父节点时自动删除所有子节点;
  • 若层级较深(如超过 3 层),建议使用递归查询或 EF Core 的 Include 多层嵌套加载子节点。

总结

  1. 关系配置核心:HasOne/HasMany 声明当前实体的导航属性,WithOne/WithMany 声明关联实体的导航属性,HasForeignKey 指定外键(一对一必须显式声明外键,一对多 / 多对多可选);
  2. 一对多是基础:建议在 “多” 的一方配置,通过 Include 预加载关联数据,避免懒加载性能问题;
  3. 自引用关系:本质是特殊的一对多,需注意外键可空(根节点)、级联删除限制,添加数据时只需加根节点即可级联保存子节点。
.net core 中EF Core用法 Entity Framework Core 是 .NET Core 下的轻量级、跨平台、可扩展、开源的对象关系映射(ORM)框架。它用于方便地处理关系型数据库操作,将数据库中的表映射为 C# 类对象,并通过 LINQ 查询等方式简化数据访问操作。以下是 EF Core 的核心用法,包括如何安装、配置、迁移和进行基本的增删查改操作。 阅读详情

相关推荐

1. EF Core 的配置

让开发者用对象操作的形式操作关系数据库 (比如查询现在主流的数据库大概分为两种,一个是传统的关系型数据库,在建表的时候利用外键把表与表联系起来;而另一种就是,比如 Mongodb,里面存的就是 json 字符串,是一个个对象有哪些ORM:EF Core、Dapper、SqlSugar、FreeSql等。

weixin_55888879的博客 783

第4章 4.6 EF Core关系配置

介绍了在 EF Core 中进行一对多、一对一及多对多关系配置的方式。

weixin_46540714的博客 967

EF Core优化技巧之预热处理

今天和听到同事们在讨论一个关于使用EFCore时,为什么第一次查询数据库总是很慢的原因。我们在工作中经常使用EFCore进行数据访问,但发现每次第一次查询都需要较长的时间,这给我们带来了困扰。因此,我们聚在一起,探讨了这个问题的原因和可能的解决方案。通过查询相关资料,于是就有了这篇博客,现在分享给有需要的你。EF Core是一个轻量级、可扩展的ORM(对象关系映射)框架,用于在.NET应用程序中处理数据库操作。在应用程序启动时,EF Core需要进行一些初始化操作,如构建模型、加载元数据和建立数据库连接等。

dotnet研习社 1982

EFCore-11 自引用关系配置

在数据库中表示这样的一个组织关系,可以使用自引用的方式实现,也就是主键和外键在同一张表中。 新建一个控制台应用程序 控制台项目结构: 项目引用的程序集: Microsoft.EntityFrameworkCore.SqlServer Microsoft.EntityFrameworkCore.Tools 1.在实体类中定义关系属性 OrgUnit.cs using System.Collections.Generic; namesp..

每天学习一点点 2150

掌握EF Core:全方位面试指南,助你从初级到高级轻松晋级

这份指南旨在帮助你为主要考察 Entity Framework Core (EF Core) 的面试做好准备,内容涵盖基础、中级和高级三个不同经验级别。每个级别包括10个高频面试题,附有解题思路和详细的解答示例。解释 EF Core 是什么,包括它的用途和基本功能。Entity Framework Core(简称 EF Core)是一个开源、轻量级、可扩展且跨平台的版本,它是 .NET 的流行对象关系映射(ORM)框架 Entity Framework 的现代化版本。

dotnet研习社 2449

EF Core 基础应用

.EF Core 基础应用 基础知识链接字符串操作数据合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 基础知识 各位大哥好! 最...

qq_35049872的博客 972

EF Core一对多关系

1、“关系数据库”2、数据库表之间的关系:一对一、一对多、多对多3、EF Core不仅支持单实体操作,更支持多实体的关系操作。4、三部:—— 实体类中关系属性;——FluentAPI关系配置;——使用关系操作。

lei1083929965的博客 453

# EF Core 常用注解完全指南

EF Core注解提供了一种直观、便捷的方式来配置实体与数据库之间的映射关系。简单配置:优先使用注解复杂配置:考虑使用Fluent API团队协作:保持配置风格一致合理使用这些注解可以显著提高开发效率,同时确保数据库模型的正确性和一致性。提示:所有注解都需要正确引用对应的命名空间,大多数EF Core特定注解位于命名空间下,而通用数据验证注解位于命名空间下。

qq_44206824的博客 1224

EF Core反向导航属性解决多对一关系

多对一是一种很常见的关系,例如:一个班级有一个学生集合属性,同时,班级有班长、语文课代表、数学课代表等单个学生属性,如果定义2个实体类,班级SchoolClass和学生Student,那么,班级SchoolClass类有多个学生Student类的导航属性,学生Student类有一个班级SchoolClass类的导航属性。此时就需要使用InverseProperty反向导航属性去指定通过哪个属性建立...

weixin_33840661的博客 2441

EF Core中通过Fluent API配置一对多关系

接上篇博客,继续演示Fluent API配置一对多的关系。 以班级-学生为例,一个班级有多个学生,一个学生属于一个班级。 ER图如下: 同样的,我们需要准备好如上实体类。 班级类: public class Class { public int ClassId { get; set; } public string ClassName { get; set; } public virtual ICollection<Student> Students { get;

极简的随笔 1006

基于MYD-YT153与AD7616的16通道高速ADC采集系统实战指南

在嵌入式系统与工业数据采集领域,模拟信号采集是连接物理世界与数字系统的关键技术。其核心原理是通过模数转换器(ADC)将连续的模拟电压信号转换为离散的数字码值,供处理器处理。这项技术的价值在于实现了对温度、压力、振动等关键物理量的精确感知与数字化,是工业自动化、设备监测、科学实验等场景的基石。本文聚焦于**多通道**、**高精度**采集这一常见需求,深入剖析如何利用全志T153处理器的LocalBus并行总线接口,高效驱动ADI的16位、16通道同步采样ADC芯片AD7616,构建一个稳定可靠的高速数据采集系

weixin_30335353的博客 443

Entity Framework Core——3.实体关系的配置

https://docs.microsoft.com/zh-cn/ef/core/modeling/relationships 文章目录1. 术语介绍2. 自动配置的关系2.1 完整配置2.2 无外键情况下进行配置2.3 无导航属性对情况下配置3. 手动配置关系3.1 无导航属性对下的配置3.2 手动指定外键3.3 无导航属性下的配置3.4 将外键指向到非主键上3.5 设置外键可否为空3.6 设置级联删除4. 其他关系的配置4.1 一对一4.2 多对多 1. 术语介绍 Dependent entity.

catshitone的专栏 2068

纽扣电池供电系统优化:NBM5100A与STM32F745VG组合方案

在低功耗物联网设备设计中,能量管理是核心挑战之一。纽扣电池因其体积小、成本低被广泛采用,但瞬时大电流需求常导致电压骤降和寿命缩短。通过超级电容缓冲技术,可以实现能量的细水长流式存储与集中释放。NBM5100A电源管理IC与STM32F745VG微控制器的组合方案,采用双阶段能量管理架构,先以恒定小电流充电,再通过电容释放大电流。这种方案不仅能提升系统稳定性,还能延长电池寿命3-5倍,特别适合智能门锁、无线传感器等需要间歇性高功率输出的场景。

weixin_29194255的博客 257

EF Core关系配置

EF Core关系配置学习记录

zhmen的博客 1118

医学影像处理实战:如何用Python+SimpleITK搞定DICOM/NIfTI重采样(附完整代码)

本文详细介绍了如何使用Python和SimpleITK库实现医学影像(DICOM/NIfTI格式)的智能重采样技术。通过实战代码演示,帮助读者掌握医学图像处理中的关键步骤,包括环境搭建、数据准备、核心算法实现和质量验证,解决医学AI领域中的影像标准化难题。

linux的博客 981

手把手教你学Simulink——无刷直流电机(BLDC)霍尔传感器(Hall Sensor)信号处理电路仿真

在中,转子位置靠​ 给出,换相逻辑(Commutation Table)据此决定。实际霍尔输出是,且常有:比较迟滞(内置或外加分压)RC 低通(防 EMI 尖刺)与 MCU 间想让你霍尔三相 UVW 理想方波(120° each, 换相每 60°elec)经观察:原始 HU/HV/HW、滤波后波形、整形后 TTL 电平验证换相表(001→100→010…)与转子转向匹配是破局关键。

amy_mhd的博客 38

efcore 实体配置_EF Core 快速上手——EF Core的三种主要关系类型

本节导航三种数据库关系类型建模Migration方式创建和习修改数据库定义和创建应用DbContext将复杂查询拆分为子查询本章主要介绍如何使用EF Core进行数据库读取,俗称查询。您将创建一个数据库,它包含EFCore中三种主要数据库关系类型。在这个过程当中,你将学会使用EF Core 创建和改变数据库的结构。接下来你将学习到如何使用EF Core访问数据库并从数据库表中获取数据。在查看用主数...

weixin_42393315的博客 727

【VibeCoding系列】小白入门指南:模型选择、编辑器选择、项目预定全分析

开箱即用:下载安装就能用,不用配 API Key(也可以自己配)Cmd+K 快速修改:选中代码,按 Cmd+K,直接让 AI 修改Cmd+L 对话:侧边栏聊天,支持引用文件和代码@文件引用:输入 @ 就能引用项目中的文件,AI 自动读取上下文Agent 模式:告诉 AI 一个需求,它自动跨文件修改下载地址cursor.com(免费版有额度限制,足够入门)✅下载 Cursorcursor.com),安装打开✅选一个小项目(个人名片页 or Todo List)✅按照七步工作流开干。

十年老程序猿,AI 疯狂研究专家,前沿硬核知识分享 211

【EF Core 性能革命】:EF Core 9 批量更新必须掌握的4个隐藏特性

掌握EF Core 9批量操作优化技巧,显著提升数据处理性能。本文揭秘4个隐藏特性,涵盖批量更新、插入场景,利用新API减少数据库往返,提高执行效率。无需第三方库,原生支持更稳定,值得收藏。

IterStream的博客 865

「UG/NX」多功能dll集成一个dll的三种方法

C++多功能集成一个dll的三种方法。

何曾参静谧的博客 1390

JAX/JAXlib GPU 加速版本安装教程(CUDA)

本文介绍了在Windows WSL2+Ubuntu22.04环境下配置JAX/JAXlib GPU加速版本的详细步骤。使用RTX 4060显卡,安装CUDA 12.4和cuDNN 8.9.7驱动,通过Miniconda创建Python 3.9虚拟环境。具体步骤包括:安装Miniconda、配置CUDA环境变量、安装cuDNN驱动、下载并安装JAX/JAXlib。最后通过测试代码验证GPU加速功能,成功检测到GPU并完成矩阵运算。整个过程配有详细命令和截图说明,适合需要搭建JAX GPU开发环境的用户参考。

猿小白的博客 627

EF Core模型生成总出错?:4个关键配置让你一次成功

解决EF Core模型生成难题,掌握数据库优先逆向关键配置。适用于已有数据库的项目开发,通过指定Scaffold-DbContext命令、正确设置连接字符串、忽略无关表与调整映射关系,提升生成效率与代码准确性。4个实用技巧一次到位,值得收藏。

DeepLens的博客 882

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

.NET core EF Core

asp.NET是由 微软 创建的开放源代码 Web 框架,使用 .NET 生成现代 Web 应用和服务。是一个成熟的框架,提供在 Windows 上生成基于服务器的企业级 Web 应用所需的服务。是Asp.Net的升级版,遵循的.Net框架,基于.Net Core的Web开发框架,可运行多个操作系统,更快,更容易,更模块化,拓展性更强2. 目前.net ,.net core 使用的版本是哪个版本答:.net core6.0 web 开源框架3. ASP.NET Core工程里面有多少个工程文件?

weixin_42595355的博客 782

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

efcore 实体配置_6. EF Core 数据库实体关系外键的配置

一、EF Core 默认约定的导航属性1、如果两个类型之间找到一对导航属性,则它们将被配置为同一关系的反转导航属性。public classBlog{public int BlogId { get; set; }public string Url { get; set; }public List Posts { get; set; }}public classPost{public int Pos...

weixin_39734493的博客 693

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

上一篇: 【EF Core】 3. 迁移、反向工程与 SQL 查看
下一篇: 【EF Core】 5. IQueryable、分页查询
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值