From 704dcd01915155a9ef7b4dc568339d00f6220f0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BF=97=E9=91=AB?= <2030875622@qq.com> Date: Sun, 7 Jun 2026 15:10:27 +0000 Subject: [PATCH 1/8] =?UTF-8?q?add=20=E5=BC=A0=E5=BF=97=E9=91=AB.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 张志鑫 <2030875622@qq.com> --- "\345\274\240\345\277\227\351\221\253" | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 "\345\274\240\345\277\227\351\221\253" diff --git "a/\345\274\240\345\277\227\351\221\253" "b/\345\274\240\345\277\227\351\221\253" new file mode 100644 index 0000000..e69de29 -- Gitee From 3c4fd4d4d0809818c345b2528a24d12f84039b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BF=97=E9=91=AB?= <2030875622@qq.com> Date: Sun, 7 Jun 2026 15:10:34 +0000 Subject: [PATCH 2/8] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=20?= =?UTF-8?q?=E5=BC=A0=E5=BF=97=E9=91=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "\345\274\240\345\277\227\351\221\253" | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 "\345\274\240\345\277\227\351\221\253" diff --git "a/\345\274\240\345\277\227\351\221\253" "b/\345\274\240\345\277\227\351\221\253" deleted file mode 100644 index e69de29..0000000 -- Gitee From fa3ba0b0d81319b2ae81c8660b0e87802416d1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BF=97=E9=91=AB?= <2030875622@qq.com> Date: Sun, 7 Jun 2026 15:10:51 +0000 Subject: [PATCH 3/8] =?UTF-8?q?=E6=96=B0=E5=BB=BA=20=E5=BC=A0=E5=BF=97?= =?UTF-8?q?=E9=91=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "\345\274\240\345\277\227\351\221\253/.keep" | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 "\345\274\240\345\277\227\351\221\253/.keep" diff --git "a/\345\274\240\345\277\227\351\221\253/.keep" "b/\345\274\240\345\277\227\351\221\253/.keep" new file mode 100644 index 0000000..e69de29 -- Gitee From 11fbf28f7b1027015af58a4f839385118c19daa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BF=97=E9=91=AB?= <2030875622@qq.com> Date: Sun, 7 Jun 2026 15:11:02 +0000 Subject: [PATCH 4/8] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=20?= =?UTF-8?q?=E5=BC=A0=E5=BF=97=E9=91=AB/.keep?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "\345\274\240\345\277\227\351\221\253/.keep" | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 "\345\274\240\345\277\227\351\221\253/.keep" diff --git "a/\345\274\240\345\277\227\351\221\253/.keep" "b/\345\274\240\345\277\227\351\221\253/.keep" deleted file mode 100644 index e69de29..0000000 -- Gitee From bf46a8793bc44367704e5332126346b2356bb33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BF=97=E9=91=AB?= <2030875622@qq.com> Date: Sun, 7 Jun 2026 15:11:12 +0000 Subject: [PATCH 5/8] =?UTF-8?q?=E6=96=B0=E5=BB=BA=20=E5=BC=A0=E5=BF=97?= =?UTF-8?q?=E9=91=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "\345\274\240\345\277\227\351\221\253/.keep" | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 "\345\274\240\345\277\227\351\221\253/.keep" diff --git "a/\345\274\240\345\277\227\351\221\253/.keep" "b/\345\274\240\345\277\227\351\221\253/.keep" new file mode 100644 index 0000000..e69de29 -- Gitee From 9733b76107cc6da9b4de553ebb1a9edb091fb34d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BF=97=E9=91=AB?= <2030875622@qq.com> Date: Sun, 7 Jun 2026 15:13:57 +0000 Subject: [PATCH 6/8] =?UTF-8?q?=E5=BC=A0=E5=BF=97=E9=91=AB=2005?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 张志鑫 <2030875622@qq.com> --- .../20260601-sdk\345\256\211\350\243\205.md" | 5 ++ ...71\347\233\256\345\210\233\345\273\272.md" | 5 ++ ...et\345\214\205\345\256\211\350\243\205.md" | 9 ++ .../20260605-crud\346\224\271\351\200\240.md" | 84 +++++++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 "\345\274\240\345\277\227\351\221\253/20260601-sdk\345\256\211\350\243\205.md" create mode 100644 "\345\274\240\345\277\227\351\221\253/20260603-\351\241\271\347\233\256\345\210\233\345\273\272.md" create mode 100644 "\345\274\240\345\277\227\351\221\253/20260604-nuget\345\214\205\345\256\211\350\243\205.md" create mode 100644 "\345\274\240\345\277\227\351\221\253/20260605-crud\346\224\271\351\200\240.md" diff --git "a/\345\274\240\345\277\227\351\221\253/20260601-sdk\345\256\211\350\243\205.md" "b/\345\274\240\345\277\227\351\221\253/20260601-sdk\345\256\211\350\243\205.md" new file mode 100644 index 0000000..9c2698f --- /dev/null +++ "b/\345\274\240\345\277\227\351\221\253/20260601-sdk\345\256\211\350\243\205.md" @@ -0,0 +1,5 @@ +## 安装dotnet sdk + +- 访问 https://dotnet.microsoft.com/download/dotnet/8.0进行安装 + +- 验证安装使用dotnet --version \ No newline at end of file diff --git "a/\345\274\240\345\277\227\351\221\253/20260603-\351\241\271\347\233\256\345\210\233\345\273\272.md" "b/\345\274\240\345\277\227\351\221\253/20260603-\351\241\271\347\233\256\345\210\233\345\273\272.md" new file mode 100644 index 0000000..11d6454 --- /dev/null +++ "b/\345\274\240\345\277\227\351\221\253/20260603-\351\241\271\347\233\256\345\210\233\345\273\272.md" @@ -0,0 +1,5 @@ +## 项目创建 + +- 使用dotnet new webapi -n MyShopApi --use-controllers创建webapi项目 + +- 创建完成输入dotnet run运行 \ No newline at end of file diff --git "a/\345\274\240\345\277\227\351\221\253/20260604-nuget\345\214\205\345\256\211\350\243\205.md" "b/\345\274\240\345\277\227\351\221\253/20260604-nuget\345\214\205\345\256\211\350\243\205.md" new file mode 100644 index 0000000..1747aa7 --- /dev/null +++ "b/\345\274\240\345\277\227\351\221\253/20260604-nuget\345\214\205\345\256\211\350\243\205.md" @@ -0,0 +1,9 @@ +## 项目需要用到的一蓝宝安装 + +- dotnet add package Microsoft.EntityFrameworkCore.Sqlite +- dotnet add package Microsoft.EntityFrameworkCore.Design +- 在命令后加上-v 8 , 安装8.0的版本 + +- dotnet-ef工具安装 + +- dotnet tool install --global dotnet-ef \ No newline at end of file diff --git "a/\345\274\240\345\277\227\351\221\253/20260605-crud\346\224\271\351\200\240.md" "b/\345\274\240\345\277\227\351\221\253/20260605-crud\346\224\271\351\200\240.md" new file mode 100644 index 0000000..b05d185 --- /dev/null +++ "b/\345\274\240\345\277\227\351\221\253/20260605-crud\346\224\271\351\200\240.md" @@ -0,0 +1,84 @@ +## 增删改查连接数据库实现 + +``` C# +using Microsoft.AspNetCore.Mvc; +using dec.Models; +using dec.Data; +using Microsoft.EntityFrameworkCore; + + +namespace dec.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class CategoriesController : ControllerBase +{ + private readonly AppDbcontext _context; + + public CategoriesController(AppDbcontext context) + { + _context = context; + } + + [HttpGet] + public async Task>> GetCate() + { + return await _context.categories.ToListAsync(); + } + + [HttpGet("{id}")] + public async Task> GetId(int id) + { + var category = await _context.categories.FindAsync(id); + + if (category == null) + { + return NotFound(); + } + + return category; + } + + [HttpPost] + public async Task> PostCate(Category category) + { + _context.categories.Add(category); + + await _context.SaveChangesAsync(); + + return CreatedAtAction(nameof(GetId), new { id = category.Id }, category); + } + + [HttpPut("{id}")] + public async Task PutCate(int id, Category category) + { + if (id != category.Id) + { + return BadRequest(); + } + + _context.Entry(category).State = EntityState.Modified; + + await _context.SaveChangesAsync(); + + return NoContent(); + } + + [HttpDelete("{id}")] + public async Task DeleteCate(int id) + { + var category = await _context.categories.FindAsync(id); + + if (category == null) + { + return NotFound(); + } + + _context.categories.Remove(category); + await _context.SaveChangesAsync(); + + return NoContent(); + } +} + +``` \ No newline at end of file -- Gitee From 4a62873988bc9119123880b02f9d901d557f7cfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BF=97=E9=91=AB?= <2030875622@qq.com> Date: Sun, 7 Jun 2026 15:14:02 +0000 Subject: [PATCH 7/8] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=20?= =?UTF-8?q?=E5=BC=A0=E5=BF=97=E9=91=AB/.keep?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "\345\274\240\345\277\227\351\221\253/.keep" | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 "\345\274\240\345\277\227\351\221\253/.keep" diff --git "a/\345\274\240\345\277\227\351\221\253/.keep" "b/\345\274\240\345\277\227\351\221\253/.keep" deleted file mode 100644 index e69de29..0000000 -- Gitee From cefbe81b5afb7b7f5402b539c4d2169e66554801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BF=97=E9=91=AB?= <2030875622@qq.com> Date: Sun, 14 Jun 2026 08:32:48 +0000 Subject: [PATCH 8/8] =?UTF-8?q?=E5=BC=A0=E5=BF=97=E9=91=AB=2005?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 张志鑫 <2030875622@qq.com> --- ...74\350\210\252\345\261\236\346\200\247.md" | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 "\345\274\240\345\277\227\351\221\253/20260608\344\270\200\345\257\271\345\244\232\345\257\274\350\210\252\345\261\236\346\200\247.md" diff --git "a/\345\274\240\345\277\227\351\221\253/20260608\344\270\200\345\257\271\345\244\232\345\257\274\350\210\252\345\261\236\346\200\247.md" "b/\345\274\240\345\277\227\351\221\253/20260608\344\270\200\345\257\271\345\244\232\345\257\274\350\210\252\345\261\236\346\200\247.md" new file mode 100644 index 0000000..0627677 --- /dev/null +++ "b/\345\274\240\345\277\227\351\221\253/20260608\344\270\200\345\257\271\345\244\232\345\257\274\350\210\252\345\261\236\346\200\247.md" @@ -0,0 +1,109 @@ +# Web API 一对多导航属性精简笔记 + +> **后续笔记一律精简要求,将严格遵循此格式。** 本笔记主要记录 ASP.NET Core / EF Core 中一对多关系的配置与使用。 + +## 🧩 导航属性是什么 + +导航属性是 EF Core 在实体类中定义的**特殊属性**,用于在对象层面表示表间关系(外键),让开发者用“对象点属性”的方式操作关系数据,无需手写 SQL JOIN。导航有两种形式:**集合导航**(List/ICollection< T >,关系中的“多”方)和**引用导航**(单一对象引用,关系中的“一”方)。 + +### 📌 代码定义示例 + +```csharp +// 主体实体(“一”端) +public class Blog +{ + public int Id { get; set; } + // 集合导航:一个 Blog 有多个 Post + public ICollection Posts { get; set; } = new List(); +} + +// 依赖实体(“多”端) +public class Post +{ + public int Id { get; set; } + public int BlogId { get; set; } // 外键属性 + // 引用导航:一个 Post 属于一个 Blog + public Blog Blog { get; set; } = null!; +} +``` + +- `Blog.Posts` 为集合导航,表示“一个博客下有多个文章”; +- `Post.Blog` 为引用导航,表示“每篇文章属于一个博客”。 +- 外键 `Post.BlogId` + 引用导航 `Post.Blog` 共同建立了关系约束。 + +### 🔧 关系配置方式 + +EF Core 默认**约定大于配置**。以下面的实体为例,EF 能自动推断这是一对多关系: + +```csharp +public class Article { + public long Id { get; set; } + public List Comments { get; set; } +} +public class Comment { + public long Id { get; set; } + public long ArticleId { get; set; } + public Article Article { get; set; } +} +``` + +如果需要更明确的控制,或在约定不成立时手动配置,可以使用 **Fluent API**: + +```csharp +protected override void OnModelCreating(ModelBuilder modelBuilder) +{ + modelBuilder.Entity
() + .HasMany(a => a.Comments) // Article 有多个 Comments + .WithOne(c => c.Article) // Comment 有一个 Article + .HasForeignKey(c => c.ArticleId); +} +``` + +- `HasMany()` + `WithOne()` 组合定义一对多; +- 需先用 `Has` 方向确定主体,再用 `With` 方向确认依赖; +- 最后 `HasForeignKey()` 指定外键字段。 + +### ⚡ 数据加载策略 + +导航属性默认**不自动加载**,EF Core 提供三种加载策略: + +1. **贪婪加载 (Eager Loading)**:一次性加载主体与所有关联数据 + + ```csharp + var blogs = context.Blogs + .Include(b => b.Posts) // 加载集合导航 + .ThenInclude(p => p.Comments) // 继续深层加载 + .ToList(); + ``` + + `Include()` 减少后续 SQL 请求,**显著提升查询性能**;深层嵌套时使用 `ThenInclude()`。 + +2. **显式加载 (Explicit Loading)**:按需手动加载 + + ```csharp + context.Entry(blog).Collection(b => b.Posts).Load(); + ``` + +3. **延迟加载 (Lazy Loading)**:访问时自动触发(需安装 `Microsoft.EntityFrameworkCore.Proxies`,配置 + +`UseLazyLoadingProxies`,导航属性设为 `virtual`) + +```csharp +protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) +{ + optionsBuilder.UseLazyLoadingProxies(); +} +``` + +### ✅ 最佳实践 + +- 优先使用**贪婪加载**主动加载必要数据(避免 N+1 查询); +- 复杂关联查询优先 **EF Core + Include** 而不是 C# 侧手写循环; +- 注意**双向导航的序列化循环**(Blog ↔ Posts),返回 DTO/Json 时避免循环引用; +- 合理利用 **ThenInclude** 控制深层数据加载范围。 + +### ⚠️ 常见问题 + +- 忘记 `Include` → 访问导航属性时返回 null; +- 深层 `Include` → 生成复杂 JOIN,可能拉取大量冗余数据,影响性能; +- `WithMany()` 后不调用 `HasForeignKey()` 时,EF 会按默认命名规则自动映射外键,当实体间字段命名不规范时务必手动配置。 \ No newline at end of file -- Gitee