diff --git "a/\346\235\250\345\205\211\346\262\243/20260601-\347\216\257\345\242\203\346\220\255\345\273\272\346\210\226\345\256\211\350\243\205.md" "b/\346\235\250\345\205\211\346\262\243/20260601-\347\216\257\345\242\203\346\220\255\345\273\272\346\210\226\345\256\211\350\243\205.md" new file mode 100644 index 0000000000000000000000000000000000000000..c02a54eb10f688253bc62a00e35a928144e65d8a --- /dev/null +++ "b/\346\235\250\345\205\211\346\262\243/20260601-\347\216\257\345\242\203\346\220\255\345\273\272\346\210\226\345\256\211\350\243\205.md" @@ -0,0 +1,22 @@ +## 笔记 + +- 安装.NET8 SDK + - 终端验证:`dotnet --version` +- 安装 VS Code + - 终端验证:`code --version` +- 安装 C# Dev Kit 扩展 +- 安装 Claude 扩展 +- 注册 MiniMax 并配置 API + - 注册 MiniMax 账号 + - 设置系统环境变量 + - Claude 扩展中选择模型 +- 通过 npm 安装 Claude Code CLI + - 安装 Node.js + - `node --version 和 npm --version` + - 安装 Claude Code + - `npm install -g @anthropic-ai/claude-code` + - 验证:`claude --version` + - 安装 Skill +- 创建控制台项目并运行 +- 在 VS Code 中调试 +- AI 辅助编程正确规范使用 \ No newline at end of file diff --git "a/\346\235\250\345\205\211\346\262\243/20260603-CRUD\345\256\236\344\276\213.md" "b/\346\235\250\345\205\211\346\262\243/20260603-CRUD\345\256\236\344\276\213.md" new file mode 100644 index 0000000000000000000000000000000000000000..66ed2dc472a2343a3b28bd9701e09d9e3163dc9e --- /dev/null +++ "b/\346\235\250\345\205\211\346\262\243/20260603-CRUD\345\256\236\344\276\213.md" @@ -0,0 +1,138 @@ +## 笔记 + +- ### 创建项目文件 + - `dotnet new webapi -n MyShopApi --use-controllers` +- ### 运行项目 + - `dotnet watch` +- 在网址添加`swagger`并访问 +- ### 在控制器文件夹下创建以`Controller`为结尾的控制器 +- ### 创建Modes + - 创建类别 +- 引用 + - ```cs + using Microsoft.AspNetCore.Mvc; + using 根目录名称.Model; + ``` +- 在控制器文件添加`[ApiController]` 启用一系列 Web API 行为:自动 400、模型绑定推断、ProblemDetails 等 +[ApiController],`[Route]` 定义路由模板:/api/categories + - ```cs + [ApiController] + [Route("[controller]")] + public class CategoriesController : ControllerBase + { + + } + ``` +- ### 模拟数据库 + - static 让所有请求共享同一份数据,进程重启就清空 + - ```cs + private static readonly List _list = new() + { + new Category { Id = 1, CateName = "电子产品", Description = "手机、电脑、配件" }, + new Category { Id = 2, CateName = "图书", Description = "技术、小说、教材" } + }; + ``` +- ### 理由请求 + - `[HttpGet]`有请求值`[HttpGet("{id}")]` +- 查找ID + - `Find` + - 判断是否为空 + - ```cs + // var tmp=obj is null?new Category():obj; + // return tmp; + return obj ?? new Category(); + ``` + - ```cs + // 判断有无找到品类 + if(tmp is null) + { + return new Category(); + } + ``` +- 新增 + - `Add` +- 删除 + - `Remove` + + + +## 练习 + +Model\Product.cs +```cs +namespace lx.Model; +public class Product +{ + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + public decimal Price { get; set; } + public int Stock { get; set; } +} +``` + + +Controllers\ProductsController.cs +```cs +using Microsoft.AspNetCore.Mvc; +using lx.Model; + +[ApiController] +[Route("[controller]")] + +public class ProductsController : ControllerBase +{ + private static readonly List list = new() + { + new Product { Id = 1, Name = "牙刷",Price = 3,Stock = 10 }, + new Product { Id = 2, Name = "牙膏",Price = 3,Stock = 20 }, + new Product { Id = 3, Name = "杯子",Price = 4,Stock = 15 }, + }; + + [HttpGet] + public IEnumerable Index() + { + return list; + } + + [HttpGet("{id}")] + public Product getId(int id) + { + var obj = list.Find(item => item.Id == id); + return obj ?? new Product(); + } + + [HttpPut("{id}")] + public Product getup(int id, Product obj) + { + var tem = list.Find(item => item.Id == id); + if (tem is null) + { + return new Product(); + } + tem.Id = obj.Id; + tem.Name = obj.Name; + tem.Price = obj.Price; + tem.Stock = obj.Stock; + return tem; + } + + [HttpPost] + public Product getadd(Product obj) + { + list.Add(obj); + return obj; + } + + [HttpDelete("{id}")] + public Product del(int id) + { + var obj = list.Find(item=>item.Id == id); + if (obj is null) + { + return new Product(); + } + list.Remove(obj); + return obj; + } +} +``` \ No newline at end of file diff --git "a/\346\235\250\345\205\211\346\262\243/20260604-\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245.md" "b/\346\235\250\345\205\211\346\262\243/20260604-\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245.md" new file mode 100644 index 0000000000000000000000000000000000000000..b3ce795140c74be87760a8ca8a2d9849d5a67a66 --- /dev/null +++ "b/\346\235\250\345\205\211\346\262\243/20260604-\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245.md" @@ -0,0 +1,175 @@ +# 一、先安装需要的 NuGet 包 + +在项目里打开 **NuGet 包管理器** 安装这 2 个: + +1. Microsoft.EntityFrameworkCore.Sqlite + + → SQLite 数据库驱动 + +2. Microsoft.EntityFrameworkCore.Tools + + → 用于执行数据库迁移 + +或者用命令行安装(复制粘贴即可): + +``` +Install-Package Microsoft.EntityFrameworkCore.Sqlite +Install-Package Microsoft.EntityFrameworkCore.Tools +``` + +------ + +# 二、安装 dotnet-ef 工具(必须装) + +打开 **VS 顶部 → 工具 → NuGet → 程序包管理器控制台**,运行: + +``` +dotnet tool install --global dotnet-ef +``` + +作用:用来**创建数据库、生成表**。 + +------ + +# 三、完整代码(直接复制) + +## 1. 实体类 Student.cs + +``` +namespace WebApiCRUD.Models +{ + public class Student + { + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + public int Age { get; set; } + public string? Address { get; set; } + } +} +``` + +------ + +## 2. 数据库上下文 DbContext(核心) + +新建 `AppDbContext.cs` + +``` +using Microsoft.EntityFrameworkCore; +using WebApiCRUD.Models; + +namespace WebApiCRUD.Data +{ + // 数据库上下文:连接代码 ↔ 数据库 + public class AppDbContext : DbContext + { + public AppDbContext(DbContextOptions options) : base(options) + { + } + + // 对应数据库里的 Students 表 + public DbSet Students => Set(); + } +} +``` + +------ + +## 3. 控制器 StudentsController.cs(真正的 CRUD) + +``` +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using WebApiCRUD.Data; +using WebApiCRUD.Models; + +namespace WebApiCRUD.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class StudentsController : ControllerBase + { + private readonly AppDbContext _db; + + // 构造函数注入数据库上下文 + public StudentsController(AppDbContext db) + { + _db = db; + } + + // 1. 查询所有 + [HttpGet] + public async Task>> GetAll() + { + return await _db.Students.ToListAsync(); + } + + // 2. 根据ID查单个 + [HttpGet("{id}")] + public async Task> GetOne(int id) + { + var student = await _db.Students.FindAsync(id); + if (student == null) return NotFound("未找到"); + return student; + } + + // 3. 新增 + [HttpPost] + public async Task> Add(Student student) + { + _db.Students.Add(student); + await _db.SaveChangesAsync(); // 真正写入数据库 + return CreatedAtAction(nameof(GetOne), new { id = student.Id }, student); + } + + // 4. 修改 + [HttpPut("{id}")] + public async Task Update(int id, Student student) + { + if (id != student.Id) return BadRequest(); + + _db.Entry(student).State = EntityState.Modified; + await _db.SaveChangesAsync(); + + return NoContent(); + } + + // 5. 删除 + [HttpDelete("{id}")] + public async Task Delete(int id) + { + var student = await _db.Students.FindAsync(id); + if (student == null) return NotFound(); + + _db.Students.Remove(student); + await _db.SaveChangesAsync(); + + return NoContent(); + } + } +} +``` + +------ + +## 4. Program.cs(配置 SQLite 连接串) + +``` +using Microsoft.EntityFrameworkCore; +using WebApiCRUD.Data; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllers(); + +// 👇 配置 SQLite 数据库 +builder.Services.AddDbContext(options => + options.UseSqlite("Data Source=MyDatabase.db")); // 数据库文件会自动生成 + +var app = builder.Build(); + +app.UseHttpsRedirection(); +app.UseAuthorization(); +app.MapControllers(); +app.Run(); +``` \ No newline at end of file diff --git "a/\346\235\250\345\205\211\346\262\243/20260605-RESTful\350\247\204\350\214\203\344\270\216\346\216\245\345\217\243.md" "b/\346\235\250\345\205\211\346\262\243/20260605-RESTful\350\247\204\350\214\203\344\270\216\346\216\245\345\217\243.md" new file mode 100644 index 0000000000000000000000000000000000000000..d3039798a274c3bd08d6ac0ff63060a4d9e607ba --- /dev/null +++ "b/\346\235\250\345\205\211\346\262\243/20260605-RESTful\350\247\204\350\214\203\344\270\216\346\216\245\345\217\243.md" @@ -0,0 +1,95 @@ +## 笔记 + +### REST 命名与 URL 设计 +- 资源复数 +- 不在 URL 里出现动词 + +### 状态码的正确使用 +- 状态码分类: + + | 范围 | 类别 | 含义 | + |------|------|------| + | 4xx | Client Error | 客户端错(请求有问题) | + | 5xx | Server Error | 服务端错(我们写崩了) | + +### DTO 与统一响应 +- **DTO(Data Transfer Object)**就是为接口入参/出参单独定义的"数据传输模型"。 + +### 数据验证(DataAnnotations) + +- 验证为什么不能省 + + 没有验证的后果: + + - `POST { Name: "" }` → 数据库里出现空名分类 + - `POST { Price: -100 }` → 出现负价格 + - `POST { Stock: 9999999999 }` → 库存溢出 int 范围 + - `POST { CategoryId: 9999 }` → 外键约束失败,500 报错 + +### 分页与排序 + +- 为什么需要分页 + + 没有分页的列表接口: + + - 数据量 1 万条:HTTP 响应体几 MB,传输慢 + - 数据量 100 万条:直接 OOM + - 前端渲染 1 万条:浏览器卡死 + + **分页**是后端 API 的标配——一次最多返回 N 条,前端按需翻页。 + +### 一对多导航属性回顾 + +- 一对多关系 + `Category` 和 `Product` 是经典的一对多: + + ``` + Category (1) ──── (*) Product + "电子产品" - iPhone 15 + - 小米 14 + - 机械键盘 + ``` + +在 EF Core 里通过**导航属性(Navigation Property)**表达: + +```csharp +public class Category +{ + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + + // "一"端:集合导航属性 + public ICollection Products { get; set; } = new List(); +} + +public class Product +{ + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + + // 外键 + public int CategoryId { get; set; } + + // "多"端:引用导航属性 + public Category? Category { get; set; } +} +``` + +约定: + +- 引用类型属性(`Category?`)→ "多"端的导航属性 +- 集合类型属性(`ICollection`)→ "一"端的导航属性 +- `Id`(`CategoryId`)→ 外键 + +### 在 DbContext 里显式声明 + +虽然 EF Core 约定能识别,但显式写出来**更清晰**: + +```csharp +// Data/AppDbContext.cs 的 OnModelCreating +modelBuilder.Entity() + .HasOne(p => p.Category) // Product 有一个 Category + .WithMany(c => c.Products) // Category 有多个 Product + .HasForeignKey(p => p.CategoryId) // 外键是 CategoryId + .OnDelete(DeleteBehavior.Restrict); // 有子记录时禁止删父 +``` \ No newline at end of file