C# ASP.NET 架构封神之路:分层 + 仓储 + EFCore,写出企业级可维护代码!

张开发
2026/4/7 21:08:43 15 分钟阅读

分享文章

C# ASP.NET 架构封神之路:分层 + 仓储 + EFCore,写出企业级可维护代码!
目录一、先搞懂什么是 MVC 扩展分层架构通俗类比二、DAL 层核心Repository 仓储模式 EFCore三、实战代码从 0 到 1 搭建标准架构1. 第一步定义实体Model 层2. 第二步搭建 DbContextEFCore 核心3. 第三步定义仓储接口抽象规范5. 第五步注册服务Program.cs6. 第六步Controller 调用演示四、开发必看RepositoryEFCore 10 大经典坑90% 人中招1.忘记调用 SaveChangesAsync2.滥用 AsNoTracking3.在仓储外操作 DbContext4.同步方法代替异步5.事务不统一6.主外键懒加载死循环7.实体加业务逻辑8.泛型仓储写死条件9.多个仓储多次 SaveChanges10.连接字符串硬编码五、最佳实践总结直接复制当团队规范六、结尾互动留言区聊聊总结今天带来ASP.NET 企业级架构核心篇——MVC 扩展分层架构 DAL 层 (RepositoryEFCore)实战纯干货、无废话带代码、避坑、流程图新手也能直接落地本篇主打规范命名、低耦合、高可维护、易扩展是中小厂到大厂通用的最佳实践一、先搞懂什么是 MVC 扩展分层架构通俗类比小节架构分层 公司部门分工很多新手一上来就把代码写在 Controller 里后期改不动、查错难本质是没有分工我们把项目拆成5 层标准结构MVC 基础上扩展就像一家正规公司表格层名称英文缩写职责生活类比表现层UI/Controller接收请求、返回结果前台客服业务逻辑层BLL处理核心业务规则运营经理数据访问层DAL只和数据库打交道仓库管理员模型层Model数据实体、DTO商品档案核心公共层Core工具类、常量、枚举公司行政部标准分层流程图返回结果Controller 表现层Service 业务逻辑层Repository 数据访问层EFCore数据库小节为什么必须分层解耦 改数据库不影响业务改业务不影响页面可维护代码各司其职一眼找到问题可测试业务逻辑可单独单元测试规范多人协作不混乱二、DAL 层核心Repository 仓储模式 EFCore小节仓储模式 数据库专属 “管家”仓储模式Repository是DAL 层唯一和数据库交互的入口作用统一 CRUD避免重复代码屏蔽 EFCore 细节上层不用关心数据库方便切换 ORM以后不用 EFCore 也不影响业务一句话Service 只管调用Repository 只管干活EFCore 只管执行 SQL。三、实战代码从 0 到 1 搭建标准架构环境准备.NET 6/7/8LTS 版本最佳ASP.NET MVCEFCore SQL Server依赖注入微软原生 DI1. 第一步定义实体Model 层小节实体 数据库表的 “代码映射”规范实体放在Models/Entities文件夹类名 表名大驼峰属性 字段帕斯卡命名无业务逻辑纯数据载体// Models/Entities/User.csusingSystem.ComponentModel.DataAnnotations;/// summary/// 用户实体对应数据库User表/// /summarypublicclassUser{[Key]// 主键publicintId{get;set;}[Required]// 非空[MaxLength(20)]publicstringUserName{get;set;}[MaxLength(50)]publicstringEmail{get;set;}publicboolIsActive{get;set;}true;// 默认值}2. 第二步搭建 DbContextEFCore 核心小节DbContext 数据库连接 “总开关”规范放在DAL/DbContexts继承DbContext构造函数注入配置用DbSet映射表// DAL/DbContexts/AppDbContext.csusingMicrosoft.EntityFrameworkCore;usingModels.Entities;/// summary/// EFCore 数据库上下文/// /summarypublicclassAppDbContext:DbContext{// 注入配置publicAppDbContext(DbContextOptionsAppDbContextoptions):base(options){}// 映射数据库表publicDbSetUserUsersSetUser();// 统一配置种子数据/约束protectedoverridevoidOnModelCreating(ModelBuildermodelBuilder){base.OnModelCreating(modelBuilder);}}3. 第三步定义仓储接口抽象规范小节接口 “管家的工作清单”最佳实践先写接口后写实现面向接口编程// DAL/Repositories/IRepository/IRepository.csusingSystem.Linq.Expressions;/// summary/// 通用仓储接口所有实体通用CRUD/// /summary/// typeparam nameT实体类型/typeparampublicinterfaceIRepositoryTwhereT:class{// 查询TaskTGetByIdAsync(intid);TaskListTGetAllAsync();TaskListTGetListAsync(ExpressionFuncT,boolwhere);// 新增TaskAddAsync(Tentity);TaskAddRangeAsync(ListTentities);// 修改voidUpdate(Tentity);voidUpdateRange(ListTentities);// 删除voidDelete(Tentity);voidDeleteRange(ListTentities);// 提交事务TaskintSaveChangesAsync();}4.第四步实现通用仓储核心代码 小节实现类“管家真正干活” csharp 运行// DAL/Repositories/Impl/RepositoryBase.csusingMicrosoft.EntityFrameworkCore;usingSystem.Linq.Expressions;/// summary/// 仓储基类所有实体共用实现/// /summarypublicclassRepositoryBaseT:IRepositoryTwhereT:class{protectedreadonlyAppDbContext_dbContext;protectedreadonlyDbSetT_dbSet;// 依赖注入DbContextpublicRepositoryBase(AppDbContextdbContext){_dbContextdbContext;_dbSetdbContext.SetT();}#region查询publicasyncTaskTGetByIdAsync(intid){returnawait_dbSet.FindAsync(id);}publicasyncTaskListTGetAllAsync(){returnawait_dbSet.AsNoTracking().ToListAsync();}publicasyncTaskListTGetListAsync(ExpressionFuncT,boolwhere){returnawait_dbSet.AsNoTracking().Where(where).ToListAsync();}#endregion#region增删改publicasyncTaskAddAsync(Tentity){await_dbSet.AddAsync(entity);}publicvoidUpdate(Tentity){_dbSet.Update(entity);}publicvoidDelete(Tentity){_dbSet.Remove(entity);}#endregion// 统一提交事务publicasyncTaskintSaveChangesAsync(){returnawait_dbContext.SaveChangesAsync();}}5. 第五步注册服务Program.cs小节依赖注入 “给系统分配员工”// Program.csusingDAL.DbContexts;usingDAL.Repositories.Impl;usingDAL.Repositories.IRepository;usingMicrosoft.EntityFrameworkCore;varbuilderWebApplication.CreateBuilder(args);// 1. 注册EFCorebuilder.Services.AddDbContextAppDbContext(optionsoptions.UseSqlServer(builder.Configuration.GetConnectionString(DefaultConnection)));// 2. 注册仓储生命周期Scoped 一次请求一个实例builder.Services.AddScoped(typeof(IRepository),typeof(RepositoryBase));// 3. MVC控制器builder.Services.AddControllersWithViews();varappbuilder.Build();// 中间件省略...app.Run();6. 第六步Controller 调用演示小节上层调用 “客服找经理经理找仓库”// Controllers/UserController.csusingDAL.Repositories.IRepository;usingMicrosoft.AspNetCore.Mvc;usingModels.Entities;publicclassUserController:Controller{// 直接注入仓储privatereadonlyIRepositoryUser_userRepository;publicUserController(IRepositoryUseruserRepository){_userRepositoryuserRepository;}// 查询用户列表publicasyncTaskIActionResultIndex(){varusersawait_userRepository.GetAllAsync();returnView(users);}// 新增用户[HttpPost]publicasyncTaskIActionResultAdd(Useruser){await_userRepository.AddAsync(user);await_userRepository.SaveChangesAsync();returnRedirectToAction(Index);}}四、开发必看RepositoryEFCore 10 大经典坑90% 人中招小节踩坑 新手 “必经之路”避开就是高手1.忘记调用 SaveChangesAsync坑增删改执行了但数据库没变化原因EFCore 是状态跟踪必须提交才生效类比你把商品放进仓库但没 “确认入库”2.滥用 AsNoTracking坑查询快但修改时报错规则只读用 AsNoTracking要修改必须不用3.在仓储外操作 DbContext坑架构失效代码到处改数据库规范所有数据库操作必须走 Repository4.同步方法代替异步坑高并发卡死性能暴跌规范EFCore 全部用Async结尾方法5.事务不统一坑部分成功部分失败数据错乱最佳实践一个请求一个 SaveChanges6.主外键懒加载死循环坑JSON 序列化爆栈解决关闭懒加载 or 使用 DTO7.实体加业务逻辑坑实体不纯粹难以维护规范实体只存数据业务放 Service8.泛型仓储写死条件坑无法复用失去仓储意义解决用表达式树传条件9.多个仓储多次 SaveChanges坑事务失效数据不一致解决Service 层统一提交10.连接字符串硬编码坑换环境要改代码不安全规范配置文件读取五、最佳实践总结直接复制当团队规范小节这一段直接收藏团队开发统一标准✅ 命名规范接口IXXXRepository实现XXXRepository实体表名大驼峰上下文XXXDbContext✅ 结构规范项目/ ├─ Models/Entities 实体 ├─ DAL/ │ ├─ DbContexts EFCore上下文 │ ├─ Repositories/ │ ├─ IRepository 仓储接口 │ └─ Impl 仓储实现 ├─ BLL/Services 业务层 └─ Controllers 表现层✅ 职责规范Controller接收请求、返回视图Service业务规则、校验、组合仓储Repository只做 CRUD无业务逻辑EFCore只做数据库交互六、结尾互动今天把MVC 扩展分层 DAL 仓储 EFCore全套代码、架构图、避坑指南一次性讲完了新手也能直接落地留言区聊聊1.你在项目中踩过 EFCore / 仓储模式的什么坑2.下期想看Service 层业务规范 还是 UnitOfWork 工作单元3.代码看不懂的地方直接截图提问我会一一回复帮你打通架构任督二脉总结1.分层架构 分工合作Controller/Service/Repository 各司其职2.RepositoryEFCore是.NET 企业级标准数据访问方案3.避开 10 大经典坑代码可维护性直接提升 10 倍4.面向接口 依赖注入让项目易扩展、易测试觉得干货有用点赞 收藏 关注下期更精彩

更多文章