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 0000000000000000000000000000000000000000..9c2698f727885a3e8823c2da25aaee74bbfd1c48 --- /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 0000000000000000000000000000000000000000..11d645439784104c340c4d8ab11104eddbd4f538 --- /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 0000000000000000000000000000000000000000..1747aa7aa3dd5e9ad89d69255b2c0274f9fb6b02 --- /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 0000000000000000000000000000000000000000..b05d1857ecc6f78dea0e060dffc61af2a2f9cd7f --- /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 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 0000000000000000000000000000000000000000..0627677c25e026708353745bc3123f59e86c6427 --- /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