SqlSugar多表Left Join实战:一个物流查询页面的完整代码拆解与优化

发布时间:2026/7/16 6:55:11

SqlSugar多表Left Join实战:一个物流查询页面的完整代码拆解与优化 SqlSugar多表Left Join实战物流查询页面的生产级代码重构物流系统中最常见的需求之一就是订单追踪与运单查询。面对ERP、仓储系统、物流平台等多源数据开发者往往需要编写复杂的多表关联查询。本文将以一个真实的物流查询场景为例带你从零重构这段能用但不够好的代码打造安全、高效、可维护的生产级解决方案。1. 原始代码的问题诊断先看这段被作者称为能看懂就行的代码存在几个典型的技术债.WhereIF(!string.IsNullOrWhiteSpace(LogisticsNo), a.LogisticsNo IN ( FormatLogisticsNo(LogisticsNo) ) )SQL注入风险直接拼接用户输入的LogisticsNo参数且未做参数化处理。当用户输入包含单引号等特殊字符时可能导致语法错误甚至安全漏洞。.Where( a.UpdateTime DateTime.Parse(StartTime).ToString(yyyy-MM-dd) )类型转换隐患直接对StartTime字符串进行DateTime.Parse当格式不符时会抛出异常。更健壮的做法是使用DateTime.TryParse。DataTable dt sugar._db.QueryableLogisticsNoImport, WdtStockoutOrder, WdtStockoutDetails返回类型过时直接返回DataTable类型与现代.NET开发中强类型模型的实践不符也丧失了编译时类型检查的优势。2. 安全重构第一步参数化查询使用SqlSugar的参数化查询特性改造条件拼接var query db.QueryableLogisticsNoImport, WdtStockoutOrder, WdtStockoutDetails( (a, b, c) new JoinQueryInfos( JoinType.Left, a.LogisticsNo b.LogisticsNo, JoinType.Left, c.StockoutId b.StockoutId)) .WhereIF(!string.IsNullOrWhiteSpace(LogisticsType), (a, b, c) a.LogisticsType.Contains(LogisticsType)) .WhereIF(!string.IsNullOrWhiteSpace(LogisticsNo), (a, b, c) SqlFunc.ContainsArray(FormatLogisticsNo(LogisticsNo), a.LogisticsNo));关键改进用Lambda表达式替代字符串拼接Contains方法自动转换为LIKE查询SqlFunc.ContainsArray处理IN条件内置的参数化处理防止SQL注入3. 类型安全的日期处理引入更健壮的日期解析逻辑if (!DateTime.TryParse(StartTime, out var startDate)) startDate DateTime.Today.AddMonths(-1); if (!DateTime.TryParse(EndTime, out var endDate)) endDate DateTime.Today; var query db.Queryable...() .Where((a, b, c) a.UpdateTime startDate) .Where((a, b, c) a.UpdateTime endDate.AddDays(1));4. 强类型模型返回定义DTO替代DataTablepublic class LogisticsQueryResult { public string LogisticsNo { get; set; } public string LogisticsType { get; set; } public DateTime UpdateTime { get; set; } public string ShopName { get; set; } // 其他字段... } var list query.Select((a, b, c) new LogisticsQueryResult { LogisticsNo a.LogisticsNo, LogisticsType a.LogisticsType, UpdateTime a.UpdateTime, ShopName b.ShopName // 其他字段映射... }) .ToPageList(pageIndex, pageSize, ref total);优势编译时类型检查更好的IDE智能提示便于后续业务逻辑处理5. 性能优化技巧5.1 索引建议确保关联字段有索引-- 物流表 CREATE INDEX IX_LogisticsNoImport_No ON LogisticsNoImport(LogisticsNo); -- 出库单表 CREATE INDEX IX_WdtStockoutOrder_No ON WdtStockoutOrder(LogisticsNo); CREATE INDEX IX_WdtStockoutOrder_StockoutId ON WdtStockoutOrder(StockoutId);5.2 分页优化使用ToPageListAsync异步分页var result await query.Select(...) .ToPageListAsync(pageIndex, pageSize, total);5.3 查询缓存对于高频但变化不频繁的查询var list query.WithCache(60).Select(...).ToList();6. 完整生产级代码最终重构后的完整解决方案public async TaskIActionResult QueryLogistics( string startTime, string endTime, string logisticsType, string logisticsNo, int pageIndex 1, int pageSize 20) { // 参数校验 if (pageIndex 1) pageIndex 1; if (pageSize 1 || pageSize 100) pageSize 20; // 日期处理 if (!DateTime.TryParse(startTime, out var startDate)) startDate DateTime.Today.AddMonths(-1); if (!DateTime.TryParse(endTime, out var endDate)) endDate DateTime.Today; try { var total 0; var query db.QueryableLogisticsNoImport, WdtStockoutOrder, WdtStockoutDetails( (a, b, c) new JoinQueryInfos( JoinType.Left, a.LogisticsNo b.LogisticsNo, JoinType.Left, c.StockoutId b.StockoutId)) .Where((a, b, c) a.UpdateTime startDate) .Where((a, b, c) a.UpdateTime endDate.AddDays(1)) .WhereIF(!string.IsNullOrWhiteSpace(logisticsType), (a, b, c) a.LogisticsType.Contains(logisticsType)) .WhereIF(!string.IsNullOrWhiteSpace(logisticsNo), (a, b, c) SqlFunc.ContainsArray(FormatLogisticsNo(logisticsNo), a.LogisticsNo)) .OrderBy(a a.LogisticsNo); var list await query.Select((a, b, c) new LogisticsQueryResult { LogisticsNo a.LogisticsNo, LogisticsType a.LogisticsType, UpdateTime a.UpdateTime, ShopName b.ShopName, ErpOrderNo b.SrcOrderNo, OriginalOrderNo c.SrcTid, SubOrderNo c.SrcOid, OutboundTime b.ConsignTime }) .ToPageListAsync(pageIndex, pageSize, total); return Ok(new { total, data list }); } catch (Exception ex) { _logger.LogError(ex, 物流查询异常); return StatusCode(500, 查询服务暂不可用); } }关键改进点全异步化处理完善的参数校验强类型返回模型集中化的异常处理结构化日志记录7. 扩展实践动态查询构建对于更复杂的查询场景可以引入动态查询构建器var query db.QueryableLogisticsNoImport, WdtStockoutOrder, WdtStockoutDetails(...); // 动态添加筛选条件 var filters new Dictionarystring, object { [LogisticsType] logisticsType, [ShopName] shopName }; foreach (var filter in filters) { if (!string.IsNullOrWhiteSpace(filter.Value?.ToString())) { query query.Where($a.{filter.Key} 0, filter.Value); } }这种模式特别适合构建高级查询界面允许用户自由组合多种筛选条件。

相关新闻