LINQ中针对匿名对象调用Distinct()方法无法去重问题求助
问题原因
你遇到的问题最核心的原因是匿名类的相等判断会比较所有属性的完整值,你看到的输出内容省略了部分属性的精度信息。
最可能的触发点是你匿名类里的CreatedDate和UpdatedDate两个属性,你赋值用的是DateTime.Now,而你打印输出的字符串默认只显示到秒级,不会展示毫秒、滴答数这些更细的时间单位。哪怕两个对象的创建时间只差几毫秒,它们的DateTime值实际是不等的,匿名类的Equals方法会判断为两个不同对象,自然Distinct()不会过滤。
另外还有两个可能的次要原因可以排查:
- 匿名类要求属性的名称、类型、声明顺序完全一致才会被编译器认定为同一个类型,如果你代码中存在隐式类型转换(比如某个属性一个是
int可空类型,另一个是非可空int),会生成不同的匿名类型,相等判断自然失效。 - 检查有没有属性是自定义引用类型,匿名类对引用类型属性的判断用的是默认引用相等,而不是值相等,如果你有自定义类的属性,需要自己重写对应类的
Equals和GetHashCode方法。
解决方法
方法1:修正时间精度
如果你需要保留CreatedDate和UpdatedDate字段,可以在赋值时把时间对齐到秒,消除毫秒级的差异:
var now = DateTime.Now; // 对齐到整秒,去掉毫秒部分 var roundedNow = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second);
然后把匿名类里的CreatedDate和UpdatedDate都赋值为roundedNow即可。
方法2:自定义去重逻辑
如果你判断重复不需要用到时间字段,可以用分组的方式自定义去重的匹配字段,不需要依赖匿名类的默认相等判断:
// 先拿到原始查询结果,去掉最后的.Distinct() var calculations = /* 你的原有LINQ查询 */.ToList(); // 按你需要的去重字段分组,取每组第一个元素 var distinctCalculations = calculations .GroupBy(x => new { x.TransferLinkId, x.ReceiptTransferId, x.IssueTransferId, x.ReceiptReversed, x.ReceiptReversedPartial, x.ReceiptCorrection, x.IssueReversed, x.IssueReversedPartial, x.IssueCorrection, x.DuplicateReceipt, x.HasVariance, x.EquipmentId, x.ReceiptCheckSum, x.ReceiptQuantityOri, x.ReceiptQuantityRev, x.ReceiptQuantityCor, x.ReceiptQuantity, x.IssueCheckSum, x.IssueQuantityOri, x.IssueQuantityRev, x.IssueQuantityCor, x.IssueQuantity, x.Variance // 排除时间字段即可 }) .Select(g => g.First()) .ToList();
方法3:移除不需要的时间字段
如果时间字段对你的业务没有意义,直接从匿名类的定义中删除CreatedDate和UpdatedDate,默认的Distinct()就可以正常工作了。
内容的提问来源于stack exchange,提问作者monstertjie_za
相关产品推荐
相关产品推荐

