You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.Net Core中带Distinct()的IQueryable调用Count()异常问题

问题产生原因
  • 这个异常是EF Core(.NET Core生态下常用的ORM组件,负责将IQueryable查询翻译为可执行SQL)的已知版本缺陷,集中出现在EF Core 3.x到6.x的早期版本中:当IQueryable链式调用.Distinct()后再执行.Count(),查询翻译器在生成去重逻辑的映射代码时,会错误地将实体的可空类型属性按非空类型处理,只要查询结果里存在对应字段为null的记录,EF Core做结果值填充时就会抛出如下异常:

System.InvalidOperationException: Nullable object must have a value

  • 移除.Distinct()后Count可正常运行,核心原因是此时翻译器会直接生成SELECT COUNT(*)的简单聚合SQL,不需要处理去重场景下的多列投影映射,自然不会触发这个映射bug。
  • 你当前使用的“转IEnumerable再Count”的临时方案,本质是让查询绕过EF Core的数据库翻译逻辑,把Distinct、Count操作全部放到应用内存执行:这种方案会把符合筛选条件的全量数据全部拉到应用服务器再做计算,小数据量下没有明显感知,数据量稍大就会造成数据库IO飙升、应用内存占用过高的问题,完全丢失了分页查询数据库端计算的性能优势,不适合生产环境长期使用。
更优解决方案

按改造成本从低到高排序:

  • 升级EF Core相关依赖版本
    该翻译bug已经在EF Core 6.0.10+、7.0+的正式版本中被官方修复,只需要将项目中的Microsoft.EntityFrameworkCore核心包,以及对应数据库的提供程序包(如SqlServer、MySql的EF Core provider)升级到对应稳定高版本,不需要修改任何业务代码即可直接解决问题,是投入产出比最高的方案。
  • 用GroupBy替代Distinct做去重计数(兼容旧版本EF Core)
    如果短期内无法升级EF Core版本,可以将Distinct去重逻辑改写为按去重判定字段分组后统计分组数量,EF Core对GroupBy聚合的翻译逻辑更成熟,不会触发可空映射bug,示例代码:
    // 原异常写法
    // var total = query.Distinct().Count();
    
    // 改写后:按判定重复的唯一字段(通常是主键)分组,分组总数即为去重后的总条数
    var total = query.GroupBy(x => x.Id).Count();
    
    该写法生成的SQL全程在数据库端执行,性能和原生Distinct+Count完全一致,不会拉取全量数据到内存。注意GroupBy的键选择器,要和你Distinct判定重复记录的字段规则保持一致。
  • 显式投影非空列后再计数(兼容旧版本EF Core)
    你也可以在Distinct调用后显式选择非空的唯一标识列再执行Count,让翻译器绕过全字段实体映射逻辑,直接统计单列的去重值数量,示例:
    var total = query.Distinct().Select(x => x.Id).Count();
    
    这种写法会在数据库端生成SELECT COUNT(DISTINCT Id)的高效聚合SQL,同样不会触发映射异常,性能表现和原生写法一致。

内容的提问来源于stack exchange,提问作者Adrià Arquimbau

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 21:39:04