C#结合Session使用LINQ按年周过滤数据解决记录重复问题
当前查询出现重复数据的核心原因
- 你在LINQ中连续编写5次
from m in db.Models这类交叉遍历逻辑,但完全没有给这些Model表的遍历增加任何实体间的关联条件,最终生成的SQL会触发全表笛卡尔积——把ProductionDays、5个无关联的Models集合做所有排列组合的拼接,哪怕后续加了Where过滤条件,重复记录也会被不同的遍历路径放大,和你手动在数据库编写带正确关联的查询结果自然无法对齐。 - 现有视图模型存在类型不匹配问题:
ExtractionViewModel中VW270PA属性定义为int类型,但赋值时传入的是字符串类型的m.Name(车型名称),运行时会直接触发类型转换异常。 - 你当前的逻辑完全没有接入Session的动态过滤能力,所有过滤条件都是硬编码在查询语句中,根本无法实现类Excel的灵活筛选效果。
正确的功能实现方案
1. 重构基础LINQ查询,从根源消除重复数据
多次无关联遍历Models表的写法完全冗余,你只需要基于有外键关系的ProductionDays、Weeks表做基础关联,再通过子查询匹配对应车型的字段值即可,完全不需要交叉遍历全表:
public IList<ExtractionViewModel> GetExtractionViewModels(Dictionary<string, object> filterConditions) { var db = new ProductionManagementEntities(); // 基础关联:仅连接存在外键绑定的生产日、周维度表 var baseQuery = from p in db.ProductionDays join w in db.Weeks on p.WeekId equals w.WeekId where p.InActive == false && w.InActive == false select new ExtractionViewModel { Year = w.Year.ToString(), Day = p.ProductionDate, Week = w.WeekNum, // 子查询匹配对应车型值,避免无关联交叉遍历 VW270 = db.Models.Where(m => m.Name == "VW270" && m.InActive == false).Select(m => m.Name).FirstOrDefault(), VW270PA = db.Models.Where(m => m.Name == "VW270PA" && m.InActive == false).Select(m => m.ModelId).FirstOrDefault(), VW250 = db.Models.Where(m => m.Name == "VW250" && m.InActive == true).Select(m => m.Name).FirstOrDefault(), VW2502PA = db.Models.Where(m => m.Name == "VW250/2PA" && m.InActive == false).Select(m => m.Name).FirstOrDefault() }; // 动态拼接传入的过滤条件 if (filterConditions.TryGetValue("Year", out var yearVal)) { int targetYear = int.Parse(yearVal.ToString()); baseQuery = baseQuery.Where(x => int.Parse(x.Year) == targetYear); } if (filterConditions.TryGetValue("WeekNum", out var weekVal)) { int targetWeek = int.Parse(weekVal.ToString()); baseQuery = baseQuery.Where(x => x.Week == targetWeek); } // 按业务规则排序后返回 return baseQuery .OrderByDescending(x => x.Year) .ThenByDescending(x => x.Day) .ThenByDescending(x => x.Week) .ToList(); }
2. 结合Session实现动态过滤逻辑
- 前端每次触发筛选操作(比如选择2021年、第53周)时,先把用户选择的键值对过滤条件存入Session:
// 存Session:仅存简单的筛选参数,不要放实体集合占用内存 Session["ExtractionFilter"] = new Dictionary<string, object> { {"Year", 2021}, {"WeekNum", 53} }; - 每次查询列表、导出Excel时,统一从Session读取过滤参数,传入上述查询方法即可,保证查询和导出使用完全一致的过滤逻辑。
结果一致性校验要点
- 如果修改后的查询结果仍和数据库手动执行的结果有差异,可以在LINQ查询末尾调用
ToString()输出实际生成的SQL语句,直接复制到数据库管理器中执行,对比和你手写查询的差异,定位是关联条件缺失还是过滤逻辑写错。 - 注意打通数据源链路:你现有代码中列表页、导出Excel都是从MongoDB读取数据,但基础查询是连接SQL Server,两个数据源不同步的话结果必然不一致。要么把SQL Server查询得到的过滤结果同步到Mongo,要么直接从SQL Server读取数据做导出,不要在链路中跳换数据源。
- 排查笛卡尔积问题时,只要看到LINQ中写了2次以上对同一张表的
from遍历、且没有写关联条件,优先排查是否触发了全表组合拼接。
内容的提问来源于stack exchange,提问作者Gcobani Mkontwana
相关产品推荐
相关产品推荐

