ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

.NET 10 Web API集成Clean Architecture与EF Core的AI实践指南

.NET 10 Web API集成Clean Architecture与EF Core的AI实践指南 最近很多人在跟 .NET 10 Web API 的全套课程方向是 Clean Architecture、EF Core再加上 AI 能力接入。这套组合最值得关注的不是某个单点技术而是把架构设计、数据持久化和 AI 服务串成一条完整链路的思路。适合正在做 .NET 后端开发、想从单体 Controller 堆代码转向分层架构或者想给现有 Web API 接入大模型能力的开发者。下面按我实际跑完一轮 Demo 后的经验把环境、分层、EF Core、AI 接入和踩坑点完整拆一遍。1. 这类全栈课程真正值得学的东西是把架构和 AI 组合成一条可落地的链路很多人看到“Clean Architecture EF Core AI”第一反应是知识点太多不知道该从哪入手。实际上这套组合的核心不是让你背分层理论而是帮你解决一个非常具体的问题当 Web API 开始接入 AI 能力时代码结构怎么组织才不会乱。1.1 为什么 Clean Architecture 在这里是基础如果只是写一个简单的接口Controller 里直接写业务逻辑也能跑。但一旦项目里同时存在数据库访问、第三方 AI 服务调用、请求参数校验、日志记录Controller 就会越来越臃肿。Clean Architecture 的做法是把系统按依赖方向分层Domain 放核心业务Application 放用例逻辑Infrastructure 放数据库和外部服务WebAPI 只负责接收请求和返回响应。这样做的好处是AI 服务或者数据库实现未来都可以替换业务层不需要跟着改。课程里把这一点作为主线后面的 EF Core 和 AI 集成都是在为这条主线服务。1.2 EF Core 和 AI 在这条链路里分别承担什么EF Core 解决的是数据持久化问题。在 Clean Architecture 里DbContext、实体映射、迁移脚本都属于 Infrastructure 层Application 层只依赖抽象的仓储接口或查询接口。AI 部分则分成两个维度开发过程中用 Copilot Agent 辅助写代码、测试、查报错业务功能中通过接口接入 Gemini 这类大模型服务。这两个维度不能混为一谈。Copilot Agent 是开发工具不进入运行时Gemini 服务调用则是业务代码的一部分需要按依赖方向封装。把这两件事分开整条链路才清晰。2. 环境准备先把开发链跑通再谈分层和 AI我建议先把本地环境准备好不要一上来就建解决方案。环境问题不解决后面每一步都会卡。2.1 开发机需要准备什么最低配置和推荐配置可以看下面这张表项目最低要求推荐配置操作系统Windows 10 / 11或 macOS 13同左.NET SDK.NET 10 SDK.NET 10 SDK 最新补丁IDEVisual Studio 2022 或 VS CodeVisual Studio 2022 或 Rider数据库SQL Server LocalDBSQL Server / PostgreSQL / SQLite内存8 GB16 GB 以上网络能访问 NuGet 和 AI 服务稳定网络避免超时这里有个细节.NET 10 的某些功能在旧版 SDK 里没有比如新增的 API 模板选项和默认配置行为。如果你用的是预览版或者刚发布的正式版一定要确认 SDK 版本和 NuGet 包版本匹配。原始课程材料没有给出具体版本号所以落地时先跑一次dotnet --info确认环境。2.2 用命令行建出解决方案结构我习惯用命令行创建项目比在 IDE 里点模板更快也更清楚每个项目的依赖关系。dotnet new sln -n CleanArchDemo dotnet new webapi -n CleanArchDemo.WebApi dotnet new classlib -n CleanArchDemo.Domain dotnet new classlib -n CleanArchDemo.Application dotnet new classlib -n CleanArchDemo.Infrastructure dotnet sln add CleanArchDemo.WebApi CleanArchDemo.Domain CleanArchDemo.Application CleanArchDemo.Infrastructure这里要注意.NET 10 的 Web API 模板默认会包含一些最小 API 示例和天气接口。建完项目后先把示例代码删掉再开始写自己的代码避免后面测试时被默认接口干扰。2.3 项目引用关系是分层架构的关键只建项目不设引用等于没有分层。Clean Architecture 的核心是依赖方向必须从外向内cd CleanArchDemo.WebApi dotnet add reference ../CleanArchDemo.Application dotnet add reference ../CleanArchDemo.Infrastructure cd ../CleanArchDemo.Application dotnet add reference ../CleanArchDemo.Domain cd ../CleanArchDemo.Infrastructure dotnet add reference ../CleanArchDemo.Application dotnet add reference ../CleanArchDemo.Domain注意 WebApi 层可以同时引用 Application 和 Infrastructure因为启动项目本身负责组装依赖注入。但 Application 层不能引用 Infrastructure 层这是最容易搞反的地方。如果项目里有“循环依赖”的报错先检查是不是引用方向错了。3. Clean Architecture 分层谁该放哪依赖方向怎么控制分层不是把文件放进不同文件夹就完事了而是要让每一层只依赖它下面那层。下面按实际项目里的常见划分拆开说。3.1 Domain 层只放核心业务实体Domain 层在四个项目里处于最中心它不应该引用任何其他项目。这里放的是业务实体和核心业务规则比如订单、产品、用户这类概念以及它们自身的行为。namespace CleanArchDemo.Domain.Entities; public class Product { public Guid Id { get; set; } public string Name { get; set; } string.Empty; public decimal Price { get; set; } public DateTime CreatedAt { get; set; } DateTime.UtcNow; }在这个示例里Product 只是一个简单的实体。实际项目中领域实体可能包含状态流转、业务校验、事件发布等逻辑。关键判断标准是这个对象不依赖数据库、不依赖框架、不依赖外部服务它能独立存在。3.2 Application 层处理用例Application 层是业务用例的编排层。它定义接口不关心接口怎么实现。比如要做一个“根据提示词生成内容”的功能Application 层先定义一个IAiService接口再定义一个用例类去调用这个接口。using CleanArchDemo.Domain.Entities; namespace CleanArchDemo.Application.Interfaces; public interface IAiService { Taskstring CompleteAsync(string prompt, CancellationToken cancellationToken); }using CleanArchDemo.Application.Interfaces; namespace CleanArchDemo.Application.UseCases; public class GenerateContentUseCase { private readonly IAiService _aiService; public GenerateContentUseCase(IAiService aiService) { _aiService aiService; } public async Taskstring ExecuteAsync(string prompt, CancellationToken cancellationToken) { return await _aiService.CompleteAsync(prompt, cancellationToken); } }为什么要这样做因为控制反转之后测试时可以替换成 Mock未来换服务商时也不需要改业务代码。3.3 Infrastructure 层放 EF Core 和外部服务Infrastructure 层负责把 Application 层定义的接口变成实现。EF Core 的DbContext、实体配置、仓储实现、AI 服务的具体调用都放在这里。这一层是整个架构里代码量最多的部分也是最容易出现依赖混乱的地方。记住一个原则Infrastructure 可以引用 Application 和 Domain但 Application 永远不能引用 Infrastructure。3.4 WebAPI 层只是入口WebAPI 层包含 Controller、Program.cs、中间件和依赖注入注册。Controller 的逻辑应该尽可能薄只做三件事接收参数、调用 Application 层的用例、返回结果。using CleanArchDemo.Application.UseCases; using Microsoft.AspNetCore.Mvc; namespace CleanArchDemo.WebApi.Controllers; [ApiController] [Route(api/[controller])] public class AiController : ControllerBase { private readonly GenerateContentUseCase _useCase; public AiController(GenerateContentUseCase useCase) { _useCase useCase; } [HttpPost(generate)] public async TaskIActionResult Generate([FromBody] GenerateRequest request, CancellationToken cancellationToken) { var result await _useCase.ExecuteAsync(request.Prompt, cancellationToken); return Ok(new { Result result }); } }如果 Controller 里出现了数据库直接调用、AI 客户端实例化、日志手写拼接这些代码说明分层已经失效了。4. EF Core 在 Clean Architecture 中的实践EF Core 本身不难难的是放在哪、怎么迁移、仓储要不要包一层。这部分我多说几句。4.1 DbContext 放在 Infrastructure 还是 Persistence很多模板会单独建一个 Persistence 项目把 DbContext 放进去。这个方案可以但对大多数项目来说没有必要。直接在 Infrastructure 下建一个Persistence文件夹里面放AppDbContext和实体配置就够了。项目多了反而增加维护成本。using CleanArchDemo.Domain.Entities; using Microsoft.EntityFrameworkCore; namespace CleanArchDemo.Infrastructure.Persistence; public class AppDbContext : DbContext { public AppDbContext(DbContextOptionsAppDbContext options) : base(options) { } public DbSetProduct Products SetProduct(); protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly); base.OnModelCreating(modelBuilder); } }ApplyConfigurationsFromAssembly会自动加载当前程序集里所有的IEntityTypeConfiguration实现不用逐个注册省事很多。4.2 迁移操作的顺序迁移是 EF Core 里比较容易出错的环节。主要是顺序问题。先安装工具再添加迁移最后更新数据库dotnet tool install --global dotnet-ef dotnet ef migrations add InitialCreate --project CleanArchDemo.Infrastructure --startup-project CleanArchDemo.WebApi dotnet ef database update --project CleanArchDemo.Infrastructure --startup-project CleanArchDemo.WebApi这里最容易踩的坑是不指定--project和--startup-project工具默认在当前目录找项目结果要么报“没有找到 DbContext”要么把迁移生成到了错误的项目。如果报错说找不到连接字符串多半是startup-project没指向 WebApi 项目因为连接字符串一般配置在启动项目里。4.3 Repository 要不要包一层关于 Repository 模式我的建议是简单查询不要包。直接用DbContext或者IDbContextFactory反而更清晰。EF Core 本身就是 UnitOfWork 和数据访问抽象的集合体再包一层透明的 Repository 只会增加代码量却没有带来实际好处。什么时候才需要 Repository当你有多个持久化方案需要切换或者在测试中需要大量模拟数据访问层时才值得包一层接口。如果只是做普通的 CRUDApplication 层通过构造函数注入AppDbContext或者一个更具体的仓储接口就够了。有一个边界要注意EF Core 支持不等于所有查询性能都好。比如连表查询、嵌套 Include、分页大表、N1 查询这些都需要单独检查生成的 SQL。不能因为框架帮你做了就忽略查询效率。5. AI 能力接入Copilot Agent 在开发中的角色以及 Gemini 的集成方式这个课程的标题里有 Copilot Agent又出现了 Gem实际落地时通常就是两个方向用 Copilot Agent 辅助开发以及接入 Gemini 一类大模型服务。下面分别说。5.1 Copilot Agent 是开发助手不是业务组件Copilot Agent 解决的问题是在写代码、查报错、补测试时减少重复劳动。比如我建完项目后会让它帮我生成一个符合 Clean Architecture 的 Product CRUD 例子它能在几秒内给出 Domain、Application、Infrastructure 三层的基础代码。但它不能替代你做架构决策。Agent 生成的代码经常只是“能用”不一定符合你项目的具体规则。比如它会默认把业务校验写在 Controller 里或者在 Application 层直接用 EF Core这些都需要你按分层原则手动修正。我一般会让它先生成再按架构规则逐层检查。5.2 在 Application 层定义 AI 服务接口接入 Gemini 这类模型时先在 Application 层定义接口然后在 Infrastructure 层实现真实请求。上面第 3 节里已经写了IAiService接口下面看具体实现。5.3 Infrastructure 层实现 Gemini 调用using System.Net.Http.Json; using CleanArchDemo.Application.Interfaces; namespace CleanArchDemo.Infrastructure.Services; public class GeminiAiService : IAiService { private readonly HttpClient _httpClient; private readonly string _apiKey; private readonly string _endpoint; public GeminiAiService(HttpClient httpClient, IConfiguration configuration) { _httpClient httpClient; _apiKey configuration[AI:ApiKey] ?? throw new InvalidOperationException(AI:ApiKey is not configured.); _endpoint configuration[AI:Endpoint] ?? https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent; } public async Taskstring CompleteAsync(string prompt, CancellationToken cancellationToken) { var request new { contents new[] { new { parts new[] { new { text prompt } } } } }; var url ${_endpoint}?key{_apiKey}; var response await _httpClient.PostAsJsonAsync(url, request, cancellationToken); response.EnsureSuccessStatusCode(); var result await response.Content.ReadFromJsonAsyncGeminiResponse(cancellationToken: cancellationToken); return result?.Candidates?.FirstOrDefault()?.Content?.Parts?.FirstOrDefault()?.Text ?? string.Empty; } } public class GeminiResponse { public ListGeminiCandidate? Candidates { get; set; } } public class GeminiCandidate { public GeminiContent? Content { get; set; } } public class GeminiContent { public ListGeminiPart? Parts { get; set; } } public class GeminiPart { public string? Text { get; set; } }这段代码里API Key 从配置读取而不是硬编码。还要用HttpClientFactory来管理 HttpClient避免套接字耗尽。如果你要做流式输出接口需要改成IAsyncEnumerablestring并在实现里处理 SSE 流。这里最容易忽略的是超时和重试。AI 服务响应时间不稳定单一超时配置可能不够。建议加一个重试策略比如指数退避同时设置合理的超时时间。具体的重试次数和超时时间要根据你的业务容忍度来定没有统一答案。6. 运行与验证从单条请求到批量调用的判断标准代码写完不是结束能跑通并且稳定才是真正目的。这部分从最小链路开始逐步扩展到更复杂的调用。6.1 先跑最小链路无论项目多复杂第一步永远是启动 WebAPI确认 Swagger 能打开然后用一个最简单的接口验证。比如先调用传统的 Product 接口再调用 AI 接口。这里有一个很实用的顺序启动项目确认端口正常监听打开 Swagger 页面确认接口枚举正常先用不带 AI 的普通接口测试数据库读写再测 AI 接口输入一个短提示词确认返回不为空。不要一上来就测试长文本、多并发、流式输出。先把最小链路跑通后面所有问题都更容易定位。6.2 日志和异常处理日志是整个链路里最容易被低估的部分。没有日志你只能靠猜。建议在 Program.cs 里配置结构化日志并且在 AI 服务实现里记录请求耗时和状态码。builder.Services.AddHttpClientGeminiAiService(client { client.Timeout TimeSpan.FromSeconds(60); });在业务代码里对 AI 服务的异常要单独处理。网络超时、API Key 失效、模型返回空内容、输入被内容审核拦截这些都是不同的错误不能混在一个 catch 里。6.3 超时、并发、输出完整性判断 AI 接口是否稳定不能只看一次调用是否成功。我建议关注这几个指标指标判断标准排查方向单次耗时普通短文本应在 5 到 30 秒内网络、模型大小、请求内容长度成功率连续 20 次调用失败次数应为 0API Key、超时、限流输出完整性返回内容不截断、不丢失流式处理是否正确、长度限制并发稳定性同时 5 个请求不报错HttpClient 管理、连接池、限流如果你要做批量调用比如一次处理 10 条文本不要用并行度拉满的方式。先用SemaphoreSlim限制并发数比如 3 到 5 个然后逐条记录结果。批量任务失败时还要考虑重试和跳过不能因为一条失败就中断整个队列。7. 常见踩坑和排查顺序最后这部分是我最想写的。很多问题看起来像框架或 AI 服务的问题实际往往是环境、配置或输入格式的问题。7.1 编译和运行时的高频问题先列几个容易遇到的现象和排查方向现象常见原因排查顺序迁移生成到错误项目未指定--project先看命令参数再看项目结构连接字符串找不到配置写在非启动项目确认startup-project确认环境变量依赖注入报错服务未注册或生命周期不匹配看 Program.cs 注册代码确认 AddScoped 还是 AddTransientNuGet 包版本冲突不同项目引用了不同 EF Core 版本统一 SDK 版本和包版本API Key 为空配置未加载或环境变量名不一致检查 appsettings.json、launchSettings.json、环境变量这里有个常见误区构建时通过不代表运行时没有问题。E F Core 的查询可能在编译期完全正常但运行时因为数据库表不存在、字段类型不匹配、迁移未应用而崩溃。所以每次改动实体后优先跑迁移再启动项目。7.2 AI 接口调用的排查链路AI 接口一旦报错很多人第一反应是“模型是不是挂了”实际上更多的原因是请求格式、认证或网络。我一般按这个顺序排查先看报错信息里的状态码。401 是 Key 问题429 是限流500 是服务端问题再检查请求体格式。Gemini 的contents结构不能写错字段名大小写要严格检查网络是否能正常访问目标地址。这里只判断你的服务器和目标服务之间的连通性不涉及任何代理工具检查超时配置。如果 HttpClient 的 Timeout 设置过短长文本请求会被提前中断最后看返回内容。有时候请求成功但内容为空可能是输入提示词被过滤或者模型返回了空 parts。如果报错信息里有SSL或证书相关字样先确认目标机器的证书链是否完整。这类问题通常跟代码逻辑无关。7.3 我的建议这套课程里最值得模仿的不是某个类怎么写而是它的组织方式和验证节奏。我们先从最小案例跑起再逐步加深不要让 AI 功能绑架了整个架构设计。如果你只是学习用 SQLite 加一个短文本示例就够了如果要上生产就需要把日志、超时、重试、配额管理、内容格式校验都提前设计好。踩过几次之后我发现很多问题不是框架能力不够而是前置环境和输入材料没有处理干净。项目引用方向错了连接字符串没有放对位置API Key 没有生效这些占了排查时间的一大部分。先把这些基础环节做扎实Clean Architecture、EF Core 和 AI 集成这套方案才能真正给你带来长期收益。
返回列表