diff --git "a/\351\231\210\346\201\251\347\224\237/20260119-MVC \350\247\206\345\233\276\346\250\241\345\236\213\357\274\210View Model\357\274\211.md" "b/\351\231\210\346\201\251\347\224\237/20260119-MVC \350\247\206\345\233\276\346\250\241\345\236\213\357\274\210View Model\357\274\211.md" new file mode 100644 index 0000000000000000000000000000000000000000..37982f3bbae06979ed8eaa62d8b41cb07742a256 --- /dev/null +++ "b/\351\231\210\346\201\251\347\224\237/20260119-MVC \350\247\206\345\233\276\346\250\241\345\236\213\357\274\210View Model\357\274\211.md" @@ -0,0 +1,100 @@ +# MVC 视图模型(View Model):架构中的数据桥梁与逻辑中枢 + +### 一、视图模型(View Model)的核心定义 + + + +![{"type":"load\_by\_key","key":"banner\_image\_0","image\_type":"search"}]() + +视图模型(View Model,简称 VM)是 MVC 架构演化体系中的关键组件,尤其在 MVVM(Model-View-ViewModel)模式中成为核心枢纽。它本质是**视图与模型之间的中间层**,负责将模型(Model)的原始数据转换为视图(View)可直接展示的格式,同时封装与 UI 相关的状态和交互逻辑,实现视图与模型的彻底解耦。 + +与传统 MVC 中控制器(Controller)既处理业务逻辑又协调视图更新的职责不同,视图模型专注于 “数据适配” 和 “UI 逻辑封装”,不直接操作视图,也不包含核心业务逻辑,成为连接数据层与展示层的纯粹桥梁。 + +### 二、视图模型的核心职责 + + + +1. **数据转换与适配**:模型层的数据往往是面向业务的原始格式(如数据库字段、接口返回结构),视图模型需将其转换为视图友好的展示格式。例如,将时间戳转换为 “YYYY-MM-DD” 日期字符串、将数值型状态码映射为 “已完成”“待处理” 等文字描述,避免视图层包含数据处理逻辑。 + +2. **UI 状态管理**:封装视图所需的所有状态信息,如按钮是否可点击、表单是否可编辑、加载状态(加载中 / 成功 / 失败)等。视图通过监听这些状态变化实现界面刷新,无需直接依赖模型数据的变化细节。 + +3. **交互逻辑封装**:接收视图传递的用户操作(如点击按钮、表单提交),将其转换为对模型层的调用指令,同时处理交互过程中的逻辑(如表单验证、请求防抖)。例如,用户点击 “提交订单” 时,视图模型先验证表单完整性,再调用模型的订单创建接口。 + +4. **数据绑定中介**:通过单向或双向数据绑定机制,实现视图与视图模型的自动同步。视图的输入自动同步到视图模型,模型数据更新后也通过视图模型反向同步到视图,大幅减少手动操作 DOM 或刷新界面的代码。 + +### 三、视图模型与 MVC 核心组件的关系 + +#### 1. 与模型(Model)的关系 + + + +* 依赖关系:视图模型依赖模型获取原始数据,通过调用模型的方法(如数据查询、更新)完成业务操作,但**不修改模型的核心逻辑**。 + +* 解耦设计:模型专注于业务逻辑和数据存储,不关心数据如何展示;视图模型通过定义清晰的接口获取模型数据,避免模型与视图直接关联。 + +#### 2. 与视图(View)的关系 + + + +* 单向依赖:视图依赖视图模型提供的展示数据和状态,通过数据绑定实时响应视图模型的变化,但**不直接调用模型的方法**。 + +* 职责分离:视图仅负责界面渲染和用户输入接收,不包含任何业务逻辑或数据处理;视图模型则封装所有与 UI 相关的逻辑,确保视图的 “纯粹性”。 + +#### 3. 与控制器(Controller)的区别(传统 MVC 对比) + + + +| 特性 | 视图模型(View Model) | 控制器(Controller) | +| -------- | ------------------ | --------------- | +| 核心职责 | 数据转换、UI 状态管理、数据绑定 | 接收用户输入、协调模型与视图 | +| 与视图的交互方式 | 数据绑定(自动同步) | 主动调用视图更新方法 | +| 业务逻辑参与度 | 无(仅处理 UI 相关逻辑) | 可能包含部分业务逻辑(易臃肿) | +| 可测试性 | 高(独立于 UI 框架,易单元测试) | 中等(可能依赖视图组件) | + +### 四、视图模型的工作流程(以 Web 应用为例) + + + +1. **初始化阶段**:视图模型通过接口获取模型层的原始数据,完成数据转换(如格式标准化、字段映射),并初始化 UI 状态(如默认加载中)。 + +2. **视图渲染阶段**:视图通过数据绑定从视图模型获取展示数据和状态,渲染出用户界面(如列表、表单)。 + +3. **用户交互阶段**:用户在视图上执行操作(如输入文本、点击按钮),通过数据绑定自动同步到视图模型。 + +4. **逻辑处理阶段**:视图模型处理交互逻辑(如数据验证),调用模型层的方法更新数据(如提交表单到数据库)。 + +5. **数据更新阶段**:模型数据更新后,通知视图模型;视图模型更新自身状态和展示数据,通过数据绑定触发视图自动刷新。 + +### 五、视图模型的核心优势 + + + +1. **彻底解耦**:实现视图与模型的完全分离,修改视图时无需改动模型,更换模型时也无需调整视图,提高代码可维护性。 + +2. **高可测试性**:视图模型不依赖具体的 UI 框架或视图组件,可通过单元测试直接验证其数据转换逻辑和交互逻辑,无需启动完整应用。 + +3. **提高开发效率**:数据绑定机制减少了手动同步 UI 和数据的重复代码,开发者可专注于业务逻辑和界面设计,无需关注数据同步细节。 + +4. **可重用性强**:同一视图模型可适配多个视图(如同一组数据可在列表页、详情页中复用),同一模型数据也可通过不同视图模型适配不同视图需求。 + +### 六、典型应用场景 + + + +* 现代前端框架:Angular、Vue.js、React(结合状态管理库)等框架原生支持视图模型思想,通过组件 props、状态管理(如 Vuex、Redux)实现视图模型的核心功能。 + +* 复杂 UI 应用:需要频繁更新界面状态、多视图共享数据的应用(如电商平台、后台管理系统),视图模型可有效简化状态管理和数据同步逻辑。 + +* 跨端应用:同一套业务逻辑需适配 Web、移动端等多端视图时,视图模型可统一数据转换规则,降低多端开发成本。 + +### 七、使用注意事项 + + + +1. **避免逻辑冗余**:视图模型仅处理 UI 相关逻辑,不可包含核心业务逻辑(如数据计算、权限校验),否则会导致职责混乱。 + +2. **控制状态复杂度**:过多的 UI 状态会增加视图模型的维护成本,需合理拆分状态(如按功能模块划分多个视图模型)。 + +3. **注意数据绑定性能**:双向数据绑定在复杂界面中可能导致性能问题,需合理使用单向绑定、惰性更新等优化手段。 + +> (注:文档部分内容可能由 AI 生成) \ No newline at end of file diff --git "a/\351\231\210\346\201\251\347\224\237/20260121-MVC \346\250\241\345\236\213\347\273\221\345\256\232\357\274\210Model Binding\357\274\211.md" "b/\351\231\210\346\201\251\347\224\237/20260121-MVC \346\250\241\345\236\213\347\273\221\345\256\232\357\274\210Model Binding\357\274\211.md" new file mode 100644 index 0000000000000000000000000000000000000000..6dcb64913bd1cc3c09cf3f571e83545c42421ef8 --- /dev/null +++ "b/\351\231\210\346\201\251\347\224\237/20260121-MVC \346\250\241\345\236\213\347\273\221\345\256\232\357\274\210Model Binding\357\274\211.md" @@ -0,0 +1,547 @@ +# MVC 模型绑定(Model Binding)完全指南 + +## 一、什么是 MVC 模型绑定? + +**模型绑定(Model Binding)** 是 MVC 框架(如 [ASP.NET](https://ASP.NET) MVC、Spring MVC 等)内置的核心机制,用于自动将 HTTP 请求中的数据(URL 参数、表单提交、JSON payload、HTTP 头信息等)映射到后端定义的模型对象(实体类、DTO 等)或方法参数中。它本质上是 “请求数据” 与 “业务模型” 之间的 “自动转换器”,避免了开发者手动解析请求参数、类型转换、数据赋值的重复工作,大幅提升开发效率。 + +简单来说:模型绑定让你无需编写 `request.getParameter("username")` 或 `request.Body.ReadAsStringAsync()` 这类繁琐代码,直接在控制器方法中使用绑定后的模型对象或参数。 + +## 二、模型绑定的核心价值 + + + +1. **简化代码**:消除手动解析请求数据的冗余代码,让开发者聚焦业务逻辑; + +2. **类型安全**:自动完成字符串(请求数据默认格式)到强类型(模型属性类型)的转换,减少类型错误; + +3. **统一校验**:与数据验证机制深度集成,自动完成数据合法性校验(如必填项、格式、范围等); + +4. **多源兼容**:支持从 URL、表单、请求体、HTTP 头、路由等多种来源提取数据,适配不同请求场景。 + +## 三、模型绑定的工作流程(以 [ASP.NET](https://ASP.NET) MVC 为例) + +MVC 框架的模型绑定过程遵循固定流程,确保数据准确映射: + + + +1. **接收请求**:控制器接收到客户端发起的 HTTP 请求(GET/POST/PUT 等); + +2. **确定绑定目标**:根据控制器方法的参数类型(单个基础类型、自定义模型类、集合等),确定绑定目标; + +3. **查找数据源**:默认从以下位置按顺序查找数据(可通过特性自定义顺序): + +* 路由数据(Route Data):如 `{controller}/{action}/{id}` 中的 `id`; + +* URL 查询字符串(Query String):如 `?name=zhangsan&age=18`; + +* 表单数据(Form Data):POST 请求中 `application/x-www-form-urlencoded` 格式的表单字段; + +* 请求体(Request Body):POST/PUT 请求中 `application/json` 等格式的数据流; + +* HTTP 头(HTTP Headers):如 `Authorization`、`User-Agent` 等; + +1. **数据匹配与转换**: + +* 按 “名称匹配” 原则:请求参数名与模型属性名(或方法参数名)大小写不敏感匹配; + +* 自动类型转换:将请求中的字符串数据转换为模型属性的目标类型(如 `string` 转 `int`、`DateTime`、`bool` 等); + +* 集合 / 复杂类型处理:支持数组、List、Dictionary 及嵌套模型(如 `User` 包含 `Address` 类,参数名格式为 `Address.City`); + +1. **数据验证**:根据模型类上的验证特性(如 `[Required]`、`[EmailAddress]`),自动执行数据校验; + +2. **生成绑定结果**: + +* 绑定成功:将转换后的有效数据赋值给目标对象 / 参数,传递给控制器方法; + +* 绑定失败:未匹配到数据或类型转换失败时,属性设为默认值(如 `int` 为 0,`string` 为 `null`),校验失败信息存入 `ModelState`; + +1. **执行控制器方法**:将绑定后的目标对象 / 参数传入控制器方法,执行业务逻辑。 + +## 四、模型绑定的实战场景 + +### 场景 1:绑定基础类型参数(单个值) + +适用于简单的查询、详情查询等场景,直接绑定 URL 查询字符串或路由参数。 + + + +``` +// 控制器方法 + +public class UserController : Controller + +{ + + // GET: /User/Detail/101 (路由参数绑定)或 /User/Detail?id=101(查询字符串绑定) + + public IActionResult Detail(int id) + + { + + // 直接使用绑定后的 id,无需手动解析 + + var user = \_userService.GetById(id); + + return View(user); + + } + + // GET: /User/Search?name=zhangsan\&age=18(多参数绑定) + + public IActionResult Search(string name, int age) + + { + + var users = \_userService.Search(name, age); + + return Json(users); + + } + +} +``` + +### 场景 2:绑定自定义模型类(核心场景) + +适用于表单提交、复杂数据录入等场景,绑定多字段数据到自定义模型。 + +#### 步骤 1:定义模型类(含数据验证) + + + +``` +// 模型类:用户注册信息 + +public class RegisterModel + +{ + + \[Required(ErrorMessage = "用户名不能为空")] // 必填校验 + + \[StringLength(20, MinimumLength = 3, ErrorMessage = "用户名长度为 3-20 字符")] // 长度校验 + + public string UserName { get; set; } + + \[Required(ErrorMessage = "密码不能为空")] + + \[DataType(DataType.Password)] // 密码类型 + + public string Password { get; set; } + + \[Required(ErrorMessage = "邮箱不能为空")] + + \[EmailAddress(ErrorMessage = "邮箱格式不正确")] // 邮箱格式校验 + + public string Email { get; set; } + + \[Range(18, 60, ErrorMessage = "年龄范围为 18-60 岁")] // 数值范围校验 + + public int Age { get; set; } + +} +``` + +#### 步骤 2:控制器绑定模型并验证 + + + +``` +public class UserController : Controller + +{ + + // POST: /User/Register(表单提交或 JSON 请求) + + \[HttpPost] + + public IActionResult Register(RegisterModel model) + + { + + // 检查模型绑定与校验是否通过 + + if (!ModelState.IsValid) + + { + + // 校验失败:返回错误信息(适用于 API)或回显表单(适用于页面) + + if (Request.IsAjaxRequest()) + + { + + return BadRequest(ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage)); + + } + + return View(model); // 回显表单,保留用户输入 + + } + + // 绑定与校验成功,执行业务逻辑 + + \_userService.Register(model); + + return RedirectToAction("Login", "Account"); + + } + +} +``` + +### 场景 3:显式指定绑定源(精准控制) + +默认情况下,模型绑定会从多个数据源查找数据,若需精准指定来源(避免歧义),可使用绑定特性: + + + +| 特性 | 作用 | 适用场景 | +| ---------------- | --------------------------------------------- | --------------------------------------------- | +| `[FromQuery]` | 仅从 URL 查询字符串绑定 | 分页、筛选参数(如 `?page=1&size=10`) | +| `[FromRoute]` | 仅从路由数据绑定 | 路由中的资源 ID(如 `{id}`) | +| `[FromForm]` | 仅从表单数据绑定(`application/x-www-form-urlencoded`) | 传统表单提交 | +| `[FromBody]` | 仅从请求体绑定(如 JSON、XML) | AJAX/API 请求(`Content-Type: application/json`) | +| `[FromHeader]` | 仅从 HTTP 头绑定 | 令牌、语言标识等(如 `Authorization`) | +| `[FromServices]` | 从依赖注入容器获取(非请求数据) | 注入服务(如 `ILogger`) | + +#### 代码示例: + + + +``` +// 1. 从查询字符串绑定分页参数 + +public IActionResult GetUsers(\[FromQuery] int page = 1, \[FromQuery] int size = 10) + +{ + + var users = \_userService.GetPaged(page, size); + + return Json(users); + +} + +// 2. 从 JSON 请求体绑定模型(API 常用) + +\[HttpPost] + +public IActionResult ApiRegister(\[FromBody] RegisterModel model) + +{ + + if (!ModelState.IsValid) + + { + + return BadRequest(ModelState); + + } + + \_userService.Register(model); + + return Ok("注册成功"); + +} + +// 3. 从 HTTP 头绑定令牌 + +public IActionResult GetProfile(\[FromHeader(Name = "Authorization")] string token) + +{ + + var user = \_authService.ValidateToken(token); + + return Json(user); + +} +``` + +### 场景 4:绑定集合类型(数组、List、Dictionary) + +适用于批量操作(如批量删除、批量修改),支持数组、List、Dictionary 等集合类型。 + +#### 示例 1:绑定数组(批量删除) + + + +``` +// POST: /User/BatchDelete?ids=101\&ids=102\&ids=103 + +\[HttpPost] + +public IActionResult BatchDelete(\[FromQuery] int\[] ids) + +{ + + \_userService.DeleteBatch(ids); + + return Ok(\$"成功删除 {ids.Length} 条数据"); + +} +``` + +#### 示例 2:绑定 List 嵌套模型(批量新增) + + + +``` +// 模型类 + +public class ProductModel + +{ + + public string Name { get; set; } + + public decimal Price { get; set; } + +} + +// 控制器方法(JSON 请求体) + +\[HttpPost] + +public IActionResult BatchAdd(\[FromBody] List products) + +{ + + \_productService.AddBatch(products); + + return Ok(\$"成功新增 {products.Count} 个产品"); + +} +``` + +## 五、高级配置与自定义 + +### 1. 自定义模型绑定器 + +当默认绑定逻辑无法满足需求(如特殊格式数据、复杂类型转换)时,可自定义模型绑定器。 + +#### 步骤 1:实现 `IModelBinder` 接口 + + + +``` +// 自定义日期模型绑定器(支持 "yyyy-MM-dd" 格式) + +public class CustomDateBinder : IModelBinder + +{ + + public Task BindModelAsync(ModelBindingContext bindingContext) + + { + + if (bindingContext == null) + + { + + throw new ArgumentNullException(nameof(bindingContext)); + + } + + // 获取请求参数名 + + var modelName = bindingContext.ModelName; + + var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName); + + if (valueProviderResult == ValueProviderResult.None) + + { + + return Task.CompletedTask; + + } + + // 转换日期格式 + + var value = valueProviderResult.FirstValue; + + if (DateTime.TryParseExact(value, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var date)) + + { + + bindingContext.Result = ModelBindingResult.Success(date); + + } + + else + + { + + bindingContext.ModelState.TryAddModelError(modelName, "日期格式不正确,需为 yyyy-MM-dd"); + + } + + return Task.CompletedTask; + + } + +} +``` + +#### 步骤 2:注册并使用自定义绑定器 + + + +``` +// 方式 1:在模型属性上直接指定 + +public class OrderModel + +{ + + \[ModelBinder(typeof(CustomDateBinder))] + + public DateTime OrderDate { get; set; } + +} + +// 方式 2:全局注册(Startup/Program.cs) + +services.AddControllers(options => + +{ + + options.ModelBinderProviders.Insert(0, new BinderTypeModelBinderProvider(typeof(DateTime), new CustomDateBinder())); + +}); +``` + +### 2. 自定义绑定源 + +通过 `IModelBinderProvider` 扩展自定义绑定源,支持从特殊位置提取数据(如缓存、数据库)。 + +### 3. 忽略特定属性 + +使用 `[BindNever]` 特性忽略不需要绑定的属性(防止恶意提交敏感数据): + + + +``` +public class UserModel + +{ + + public string UserName { get; set; } + + \[BindNever] // 禁止从请求中绑定(仅后端赋值) + + public string Role { get; set; } + +} +``` + +## 六、常见问题与排查技巧 + +### 1. 绑定失败(属性为默认值) + + + +* **原因 1**:请求参数名与模型属性名不匹配(大小写敏感?No,默认不敏感,但特殊框架可能例外); + + + * 解决:确保参数名一致,或使用 `[Bind(Prefix = "别名")]` 指定别名; + +* **原因 2**:类型转换失败(如字符串 "abc" 转 `int`); + + + * 解决:检查请求数据格式,或自定义模型绑定器处理特殊类型; + +* **原因 3**:绑定源错误(如 JSON 数据未用 `[FromBody]`); + + + * 解决:显式指定绑定源(如 `[FromBody]`),并确保请求头 `Content-Type` 正确(JSON 需设为 `application/json`)。 + +### 2. `ModelState.IsValid` 为 `false` + + + +* 原因:数据未通过验证特性(如 `[Required]`、`[EmailAddress]`); + +* 解决: + + + +``` +// 输出所有校验错误 + +var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage); + +foreach (var error in errors) + +{ + + Console.WriteLine(error); + +} +``` + +### 3. JSON 绑定失败 + + + +* 确保请求头 `Content-Type: application/json`; + +* 确保 JSON 字段名与模型属性名一致(或使用 `[JsonPropertyName]` 映射); + +* 避免 JSON 数据中包含无法序列化的类型(如 `object` 类型)。 + +### 4. 嵌套模型绑定失败 + + + +* 前端参数名格式需为 `父属性.子属性`(如 `Address.City`、`Address.ZipCode`); + +* JSON 结构需与嵌套模型一致: + + + +``` +{ + + "UserName": "zhangsan", + + "Address": { + + "City": "Beijing", + + "ZipCode": "100000" + + } + +} +``` + +## 七、框架差异说明 + +不同 MVC 框架的模型绑定机制略有差异,核心逻辑一致: + + + +| 框架 | 核心特点 | 绑定特性示例 | +| ------------------------------ | ------------------------------------------ | ----------------------------------- | +| [ASP.NET](https://ASP.NET) MVC | 支持多源绑定、数据注解校验、自定义绑定器 | `[FromBody]`、`[Required]` | +| Spring MVC | 基于 `@RequestParam`/`@RequestBody` 注解,支持转换器 | `@RequestBody`、`@ModelAttribute` | +| Laravel MVC | 自动绑定请求数据到控制器方法参数,支持 `Request` 类 | `public function store(User $user)` | + +## 八、最佳实践 + + + +1. **使用强类型模型**:优先定义 DTO / 实体类,避免直接使用 `Request` 对象手动解析; + +2. **显式指定绑定源**:复杂场景下明确绑定源(如 `[FromBody]`),避免歧义; + +3. **必做数据验证**:通过特性或自定义校验确保数据合法性,依赖 `ModelState.IsValid` 检查; + +4. **忽略敏感属性**:使用 `[BindNever]` 禁止绑定敏感字段(如 `Role`、`IsAdmin`); + +5. **优化嵌套模型**:嵌套层级不宜过深(建议不超过 2 层),提升绑定效率和可读性; + +6. **日志排查**:绑定失败时,输出 `ModelState` 错误信息和请求数据,快速定位问题。 + +> (注:文档部分内容可能由 AI 生成) \ No newline at end of file diff --git "a/\351\231\210\346\201\251\347\224\237/20260122-Entity Framework Core \346\225\260\346\215\256\345\272\223\350\277\236\346\216\245.md" "b/\351\231\210\346\201\251\347\224\237/20260122-Entity Framework Core \346\225\260\346\215\256\345\272\223\350\277\236\346\216\245.md" new file mode 100644 index 0000000000000000000000000000000000000000..91ee4c2d80f5f85245172688ad238c5cb4ff391e --- /dev/null +++ "b/\351\231\210\346\201\251\347\224\237/20260122-Entity Framework Core \346\225\260\346\215\256\345\272\223\350\277\236\346\216\245.md" @@ -0,0 +1,283 @@ +# Entity Framework Core 数据库连接与上下文完全指南 + +### 一、核心概念概述 + + + +Entity Framework Core(EF Core)是微软推出的轻量级、跨平台 ORM(对象关系映射)框架,**数据库连接**负责建立应用程序与数据库的通信通道,**DbContext(数据库上下文)** 则是 EF Core 的核心,充当数据访问层与数据库之间的桥梁,封装了数据库连接、实体映射、数据操作等核心能力。 + +### 二、数据库连接配置(Configuration) + +#### 1. 连接字符串格式 + +不同数据库的连接字符串语法存在差异,以下是常用数据库的标准格式: + + + +* **SQL Server**: + + + +``` +"ConnectionStrings": { + + "DefaultConnection": "Server=localhost;Database=MyDb;User Id=sa;Password=123456;TrustServerCertificate=True;" + +} +``` + +关键参数:`Server`(数据库地址)、`Database`(库名)、`User Id/Password`(认证信息)、`TrustServerCertificate`(开发环境跳过 SSL 验证)。 + + + +* **MySQL(Pomelo.EntityFrameworkCore.MySql)**: + + + +``` +"ConnectionStrings": { + + "DefaultConnection": "Server=localhost;Database=MyDb;Uid=root;Pwd=123456;Port=3306;CharSet=utf8mb4;" + +} +``` + + + +* **SQLite**: + + + +``` +"ConnectionStrings": { + + "DefaultConnection": "Data Source=MyDb.db" // 本地文件路径 + +} +``` + +#### 2. 配置读取方式 + +在.NET 应用中([ASP.NET](https://ASP.NET) Core 为例),通过`IConfiguration`读取连接字符串: + + + +``` +// Program.cs(.NET 6+ 顶级语句) + +var builder = WebApplication.CreateBuilder(args); + +var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); +``` + +### 三、DbContext 创建与配置 + +#### 1. 自定义 DbContext 类 + +继承`DbContext`,通过`DbSet`定义实体与数据库表的映射关系: + + + +``` +using Microsoft.EntityFrameworkCore; + +using YourProject.Models; + +namespace YourProject.Data + +{ + + public class AppDbContext : DbContext + + { + + // 构造函数:接收DbContextOptions配置 + + public AppDbContext(DbContextOptionsDbContext> options) : base(options) + + { } + + // 定义实体集合(对应数据库表) + + public DbSet\ Users { get; set; } + + public DbSet { get; set; } + + // 可选:重写OnModelCreating配置实体映射(替代数据注解) + + protected override void OnModelCreating(ModelBuilder modelBuilder) + + { + + // 示例:配置User表的主键与字段长度 + + modelBuilder.EntityHasKey(u => u.Id); // 主键 + + modelBuilder.Entity .Property(u => u.UserName) + + .HasMaxLength(50) + + .IsRequired(); // 用户名必填,长度50 + + } + + } + +} +``` + +#### 2. 注册 DbContext 到依赖注入容器 + +在`Program.cs`中配置 DbContext,指定数据库提供器与连接字符串: + + + +``` +// 以SQL Server为例(需安装包:Microsoft.EntityFrameworkCore.SqlServer) + +builder.Services.AddDbContextContext>(options => + + options.UseSqlServer(connectionString)); + +// 其他数据库提供器: + +// MySQL:options.UseMySql(connectionString, new MySqlServerVersion(new Version(8, 0, 30))) + +// SQLite:options.UseSqlite(connectionString) +``` + +#### 3. DbContext 核心配置选项 + +通过`DbContextOptionsBuilder`可配置高级选项: + + + +* 日志输出:`options.LogTo(Console.WriteLine)`(打印 SQL 语句到控制台) + +* 跟踪行为:`options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)`(查询时不跟踪实体,提升性能) + +* 连接超时:`options.UseSqlServer(connectionString, o => o.CommandTimeout(30))`(设置命令超时 30 秒) + +### 四、DbContext 生命周期与使用规范 + +#### 1. 生命周期默认行为 + + + +* [ASP.NET](https://ASP.NET) Core 中,DbContext 默认生命周期为`Scoped`(作用域内单例),即每个 HTTP 请求创建一个实例,请求结束后自动释放。 + +* 避免手动创建 DbContext 长期持有(如静态变量),否则会导致连接泄漏与数据一致性问题。 + +#### 2. 正确使用方式(依赖注入) + +在控制器、服务中通过构造函数注入`AppDbContext`: + + + +``` +public class UserController : ControllerBase + +{ + + private readonly AppDbContext \_context; + + // 构造函数注入 + + public UserController(AppDbContext context) + + { + + \_context = context; + + } + + // 示例:查询所有用户 + + \[HttpGet] + + public async Task\() + + .UseSqlServer(connectionString) + + .Options)) + +{ + + // 数据操作 + + var users = context.Users.ToList(); + +} // using块结束后自动释放context +``` + +### 五、数据库迁移与初始化 + +#### 1. 迁移命令(Package Manager Console) + + + +* 创建迁移:`Add-Migration InitialCreate`(生成数据库表结构的迁移文件) + +* 应用迁移:`Update-Database`(执行迁移,创建或更新数据库) + +* 撤销迁移:`Remove-Migration`(删除最近一次未应用的迁移) + +#### 2. 数据库初始化策略 + +通过`DbContext`重写`OnConfiguring`或依赖注入配置初始化逻辑: + + + +``` +// 示例:确保数据库存在,不存在则创建 + +builder.Services.AddDbContext>(options => + + options.UseSqlServer(connectionString) + + .EnsureCreated()); // 仅创建数据库和表,不支持迁移 +``` + +> 注意: +> +> `EnsureCreated()` +> +> 与迁移互斥,生产环境建议使用迁移管理表结构。 + +### 六、常见问题与最佳实践 + + + +1. **连接字符串安全**:生产环境避免硬编码,使用环境变量、Azure Key Vault 等存储。 + +2. **性能优化**: + +* 避免频繁创建 DbContext(依赖注入已自动处理)。 + +* 复杂查询使用`AsNoTracking()`关闭跟踪。 + +* 批量操作使用`AddRange()`/`RemoveRange()`替代循环单个操作。 + +1. **错误处理**:数据操作时捕获`DbUpdateException`(处理主键冲突、外键约束等数据库层面错误)。 + +2. **多数据库支持**:通过条件编译或配置切换数据库提供器,无需修改核心业务逻辑。 + +> (注:文档部分内容可能由 AI 生成) \ No newline at end of file diff --git "a/\351\231\210\346\201\251\347\224\237/20260123ASP.NET Core MVC \344\270\255\351\205\215\347\275\256 DbContext \344\270\216\346\225\260\346\215\256\350\277\236\346\216\245\345\256\214\346\225\264\346\255\245\351\252\244.md" "b/\351\231\210\346\201\251\347\224\237/20260123ASP.NET Core MVC \344\270\255\351\205\215\347\275\256 DbContext \344\270\216\346\225\260\346\215\256\350\277\236\346\216\245\345\256\214\346\225\264\346\255\245\351\252\244.md" new file mode 100644 index 0000000000000000000000000000000000000000..456168f7817f7d330d7789bc14072d44ff7a3d31 --- /dev/null +++ "b/\351\231\210\346\201\251\347\224\237/20260123ASP.NET Core MVC \344\270\255\351\205\215\347\275\256 DbContext \344\270\216\346\225\260\346\215\256\350\277\236\346\216\245\345\256\214\346\225\264\346\255\245\351\252\244.md" @@ -0,0 +1,365 @@ +# ASP.NET Core MVC 中配置 DbContext 与数据连接完整步骤 + +### 一、MVC 项目目录结构准备 + +首先确保 MVC 项目目录符合规范,核心文件位置如下: + + + +``` +YourMvcProject/ + +├─ Models/ // 实体类目录(如 User.cs、Product.cs) + +├─ Data/ // DbContext 目录(存放 AppDbContext.cs) + +├─ Controllers/ // MVC 控制器(如 UserController.cs) + +├─ appsettings.json // 连接字符串配置文件 + +└─ Program.cs // 服务注册(DbContext 依赖注入) +``` + +### 二、Step 1:配置连接字符串(appsettings.json) + +在配置文件中添加数据库连接字符串,与之前核心指南格式一致,MVC 项目中统一放在 `appsettings.json` 根节点: + + + +``` +{ + + "Logging": { + + "LogLevel": { + + "Default": "Information", + + "Microsoft.AspNetCore": "Warning" + + } + + }, + + "ConnectionStrings": { + + // 以 SQL Server 为例,根据实际数据库修改 + + "DefaultConnection": "Server=localhost;Database=MvcDemoDb;User Id=sa;Password=123456;TrustServerCertificate=True;" + + }, + + "AllowedHosts": "\*" + +} +``` + +> 提示:MVC 项目中无需额外配置读取方式, +> +> `Program.cs` +> +> 可直接通过 +> +> `IConfiguration` +> +> 读取该节点。 + +### 三、Step 2:创建实体类(Models 目录) + +在 `Models` 目录下定义与数据库表对应的实体类(示例:`User.cs`): + + + +``` +namespace YourMvcProject.Models + +{ + + public class User + + { + + // 实体属性(对应数据库表字段) + + public int Id { get; set; } // 主键(自增) + + public string UserName { get; set; } = string.Empty; // 用户名 + + public string Email { get; set; } = string.Empty; // 邮箱 + + public DateTime CreateTime { get; set; } = DateTime.Now; // 创建时间 + + } + +} +``` + +### 四、Step 3:创建 DbContext(Data 目录) + +在 `Data` 目录下新建 `AppDbContext.cs`,关联实体与数据库: + + + +``` +using Microsoft.EntityFrameworkCore; + +using YourMvcProject.Models; + +namespace YourMvcProject.Data + +{ + + public class AppDbContext : DbContext + + { + + // 构造函数:接收依赖注入的配置选项 + + public AppDbContext(DbContextOptions options) : base(options) + + { + + // 可选:禁用延迟加载(MVC 中推荐,避免视图渲染时额外查询) + + ChangeTracker.LazyLoadingEnabled = false; + + } + + // 注册实体(对应数据库表:Users) + + public DbSet Users { get; set; } + + // 可选:通过 Fluent API 配置实体(替代数据注解) + + protected override void OnModelCreating(ModelBuilder modelBuilder) + + { + + // 配置 User 表:Email 唯一约束 + + modelBuilder.Entity\() + + .HasIndex(u => u.Email) + + .IsUnique(); + + // 配置 UserName 长度限制 + + modelBuilder.Entity .Property(u => u.UserName) + + .HasMaxLength(30) + + .IsRequired(); + + } + + } + +} +``` + +### 五、Step 4:注册 DbContext 到依赖注入(Program.cs) + +MVC 项目(.NET 6+)通过 `Program.cs` 注册 DbContext,核心代码如下: + + + +``` +var builder = WebApplication.CreateBuilder(args); + +// 1. 添加 MVC 服务(默认已包含,若手动创建需添加) + +builder.Services.AddControllersWithViews(); + +// 2. 读取连接字符串 + +var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); + +// 3. 注册 DbContext(指定数据库提供器) + +builder.Services.AddDbContext\(options => + +{ + + options.UseSqlServer(connectionString); // SQL Server 提供器 + + // 可选配置:打印 SQL 语句(MVC 开发环境调试用) + + options.LogTo(Console.WriteLine, new\[] { DbLoggerCategory.Database.Command.Name }, LogLevel.Information); + +}); + +var app = builder.Build(); + +// 中间件配置(默认已包含,确保顺序正确) + +if (!app.Environment.IsDevelopment()) + +{ + + app.UseExceptionHandler("/Home/Error"); + + app.UseHsts(); + +} + +app.UseHttpsRedirection(); + +app.UseStaticFiles(); + +app.UseRouting(); + +app.UseAuthorization(); + +// MVC 路由配置(默认) + +app.MapControllerRoute( + + name: "default", + + pattern: "{controller=Home}/{action=Index}/{id?}"); + +app.Run(); +``` + +> 依赖包说明:MVC 项目需安装对应数据库的 EF Core 包,如 SQL Server 需安装 +> +> `Microsoft.EntityFrameworkCore.SqlServer` +> +> (NuGet 搜索安装)。 + +### 六、Step 5:数据库迁移与初始化(MVC 专属操作) + + + +1. **打开 Package Manager Console(PMC)**: + +* 工具 → NuGet 包管理器 → 程序包管理器控制台。 + +1. **执行迁移命令**: + +* 创建迁移:`Add-Migration InitialCreate`(生成 `Migrations` 目录,包含表结构脚本)。 + +* 应用迁移:`Update-Database`(自动创建 `MvcDemoDb` 数据库及 `Users` 表)。 + +1. **验证数据库**: + +* 打开 SQL Server Management Studio(SSMS),连接 `localhost`,可看到 `MvcDemoDb` 数据库及对应的表。 + +### 七、Step 6:MVC 控制器中使用 DbContext(实操示例) + +在 `Controllers` 目录下创建 `UserController.cs`,通过构造函数注入 `AppDbContext` 进行数据操作: + + + +``` +using Microsoft.AspNetCore.Mvc; + +using YourMvcProject.Data; + +using YourMvcProject.Models; + +namespace YourMvcProject.Controllers + +{ + + public class UserController : Controller + + { + + // 注入 DbContext + + private readonly AppDbContext \_context; + + public UserController(AppDbContext context) + + { + + \_context = context; + + } + + // 首页:查询所有用户(对应 View) + + public IActionResult Index() + + { + + // 从数据库查询用户列表(MVC 中推荐异步操作) + + var users = \_context.Users.OrderByDescending(u => u.CreateTime).ToList(); + + // 传递数据到视图 + + return View(users); + + } + + // 新增用户(GET:显示表单) + + public IActionResult Create() + + { + + return View(); + + } + + // 新增用户(POST:提交数据) + + \[HttpPost] + + \[ValidateAntiForgeryToken] // MVC 防跨站请求伪造 + + public async Task\ Create(User user) + + { + + if (ModelState.IsValid) // MVC 模型验证 + + { + + \_context.Users.Add(user); + + await \_context.SaveChangesAsync(); // 异步保存到数据库 + + return RedirectToAction(nameof(Index)); // 重定向到列表页 + + } + + return View(user); // 验证失败,返回表单页 + + } + + } + +} +``` + +### 八、MVC 场景专属注意事项 + + + +1. **模型验证**:DbContext 中的实体配置(如 `IsRequired`、`HasMaxLength`)会与 MVC 视图的 `ModelState` 自动联动,无需额外编写验证逻辑。 + +2. **生命周期适配**:MVC 中 DbContext 为 `Scoped` 生命周期,与控制器生命周期一致(每个请求创建一个实例),无需手动释放。 + +3. **视图数据传递**:通过 `View(users)` 传递实体列表到视图,视图中可通过 `@model IEnumerableProject.Models.User>` 接收。 + +4. **错误处理**:在控制器中捕获 `DbUpdateException`,处理数据库层面错误(如邮箱重复),并通过 `ModelState.AddModelError` 反馈到视图: + + + +``` +catch (DbUpdateException ex) + +{ + + ModelState.AddModelError(string.Empty, "新增用户失败:邮箱已存在!"); + + return View(user); + +} +``` + +> (注:文档部分内容可能由 AI 生成) \ No newline at end of file