.NET Core与Vue构建高性能外卖系统的实战经验 1. 为什么选择.NET Core构建外卖订餐系统当我第一次接到开发外卖系统的需求时面对Java Spring Boot、Python Django和.NET Core这几个主流选择最终选择了后者。这个决定源于三个实际考量首先.NET Core 6.0的启动速度比Java快3倍在压力测试中相同配置的云服务器能多承载40%的并发订单其次EF Core对SQL Server的优化程度远超其他ORM我们的分页查询性能比Django ORM快2.8倍最重要的是Visual Studio提供的热重载功能让前端页面和后端API的联调效率提升了60%。在技术栈搭配上我采用Vue 3的组合式API配合Element Plus组件库。这个组合有个隐藏优势当订单列表需要实现复杂筛选时Vue的响应式系统比React减少约30%的冗余渲染。以下是核心架构的依赖配置# 后端项目 dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 6.0.10 dotnet add package Swashbuckle.AspNetCore --version 6.4.0 # 前端项目 npm install element-plus element-plus/icons-vue axios2. 数据库设计中的业务陷阱采用EF Core Code First时新手常犯的错误是直接照搬外卖平台的表结构。经过三个版本的迭代我总结出适合中小型系统的精简模型public class Order { public int Id { get; set; } public decimal ActualPrice { get; set; } // 经过优惠计算后的实付金额 public ListOrderItem Items { get; set; } new(); // 关键设计将地址快照与实时地址分离 public DeliveryAddress SnapshotAddress { get; set; } public int? CurrentAddressId { get; set; } } public class MenuItem { public int Id { get; set; } [Column(TypeName decimal(10,2))] public decimal Price { get; set; } // 防止库存超卖 [ConcurrencyCheck] public int Stock { get; set; } }特别注意必须为价格字段显式指定精度否则EF Core默认的decimal(18,2)会导致金额计算出现意外舍入错误。我们在灰度发布时因此损失了37笔订单的零头金额。3. 高并发场景下的实战技巧当促销活动导致瞬时订单量激增时系统需要处理三个致命问题库存超卖采用EF Core的并发令牌机制var dish await _context.MenuItems .Where(x x.Id itemId) .FirstOrDefaultAsync(); if (dish.Stock quantity) { dish.Stock - quantity; await _context.SaveChangesAsync(); // 自动触发并发检查 }订单重复提交前端使用Element UI的el-button组件加载状态后端Redis令牌桶// Vue组件 el-button :loadingsubmitting clicksubmitOrder提交订单/el-button // 后端拦截器 if (!await _redisDatabase.KeyDeleteAsync($order_token:{token})) { return BadRequest(请勿重复提交); }地理位置计算将SQL Server的空间数据类型与EF Core配合使用var restaurants await _context.Restaurants .Where(r r.Location.Distance(userLocation) 5000) // 5公里内 .ToListAsync();4. Vue与.NET Core的联调陷阱在前后端分离架构中跨域问题只是冰山一角。我们遇到过更隐蔽的问题时区陷阱.NET Core默认序列化的DateTime带有时区信息而Vue显示时会二次转换。解决方案// Startup.cs services.AddControllers() .AddJsonOptions(opts opts.JsonSerializerOptions.Converters.Add(new DateTimeConverter())); public class DateTimeConverter : JsonConverterDateTime { public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) DateTime.Parse(reader.GetString()); public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) writer.WriteStringValue(value.ToString(yyyy-MM-ddTHH:mm:ss)); }文件上传内存泄漏Element UI的上传组件需要特殊处理// 必须手动释放文件对象 handleRemove(file) { URL.revokeObjectURL(file.url); }5. 灰度发布中的血泪教训我们采用AB测试发布新功能时曾因Cookie作用域设置错误导致30%用户看到功能混乱的界面。正确的做法是services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options { options.Cookie.Path /; options.Cookie.SameSite SameSiteMode.Lax; // 关键设置 options.Cookie.HttpOnly true; });监控方面建议在Kestrel配置中开启请求日志// appsettings.json Logging: { LogLevel: { Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware: Information } }// Startup.cs app.UseHttpLogging();6. 性能优化实战记录通过Azure Application Insights收集的数据显示系统存在三个性能瓶颈菜单查询N1问题使用EF Core的Include优化后响应时间从1200ms降至280msvar menu await _context.Restaurants .Include(r r.Categories) .ThenInclude(c c.Items) .AsNoTracking() .FirstOrDefaultAsync(r r.Id id);GeoJSON解析耗时改用内存缓存后距离计算性能提升8倍services.AddStackExchangeRedisCache(options { options.Configuration Configuration.GetConnectionString(Redis); options.InstanceName RestaurantGeo_; });Vue组件重复渲染使用v-memo优化订单列表OrderItem v-foritem in orders :keyitem.id v-memo[item.status] :dataitem /这套系统最终在双十一促销期间平稳处理了12万笔订单期间CPU负载始终保持在65%以下。最让我自豪的是通过合理的架构设计新加入的开发人员能在2天内熟悉代码并开始贡献功能——这才是判断系统设计成功与否的真正标准。