EF Core 查询 SQLite JSON 字段有值却不匹配,检查路径引号
display_name写入正常,LINQ过滤却返回空集,可能是SQLite的->>操作数多带了引号却缺少JSON路径前缀。核对SQL,不重命名已有数据来迎合错误路径。
SQLite里的JSON确实保存了 "display_name":"EF Core",按普通 name 查询能命中,按映射为 display_name 的属性过滤却返回空集。若使用 EF Core 的JSON owned collection,应先看 ToQueryString() 里的路径,而不只看实体内容。
具体问题见于 EF Core SQLite 10.0.2与10.0.13:生成器给属性名加了双引号,却把它作为 ->> 的普通对象标签发送,没有加JSON路径前缀。
引号被当成了键名的一部分
两种表达式不能混为一谈:
'{"display_name":"x"}' ->> '"display_name"'
'{"display_name":"x"}' ->> '$."display_name"'
第一行的右操作数不以 $ 开头,SQLite会把它当成对象标签,连双引号也成为标签字符;它查找的不是原来的 display_name。第二行是完整JSON路径,才定位到预期键。
因此可以出现写入正确、读取筛选却得到NULL的情况。异常未必抛出,只是 WHERE 不再匹配。换事务隔离级别或重新保存实体不会自动修正生成的路径。
将已确认的查询放到明确读取边界
临时需要保留现有JSON数据契约时,可以针对这条查询采用受支持的参数化SQL入口,并把路径写成开发者确定的常量。下面是与owned collection形状相符的只读示例:
SELECT p.Id
FROM Posts AS p
WHERE EXISTS (
SELECT 1
FROM json_each(p.TagsJson) AS t
WHERE json_extract(t.value, '$."display_name"') = @wanted
);
假设 Posts.TagsJson 保存对象数组,@wanted 由数据访问层按参数绑定。实际表名、列名和返回模型应按项目确认。不要把请求内容插入路径或SQL;租户、权限、软删除等原有筛选也必须一起保留,不能只复制这条演示查询就替换受保护的业务入口。
如果原接口返回实体、分页或排序,应在适当的数据访问边界完整承接这些约束。选出ID并不自动等于原来的实体查询已兼容,也不应为避开翻译错误把全部JSON载入内存后再过滤。
不要直接把 HasJsonPropertyName("display_name") 改成另一个名字。那会改变存储契约,旧文档里的键不会自动迁移,其他读写方也可能继续使用旧名称。
NULL和缺失属性要分别覆盖
在隔离数据上核对有值、不同值、缺失属性、JSON null、空集合与多元素集合,比较生成SQL和结果集合。若路径本来正确,应继续检查模型映射及真实存储内容,不能把所有JSON空结果都归到这个缺陷。
相关修补存在不代表已发布的10.0.13包含修正。后续升级应核对正式提供程序版本与SQL,不用开放PR或主分支结果代替当前发行物。


