LINQ查询使用JsonConvert.Deserialize报错 无需调用ToList()如何实现
错误原因
这个异常的本质是LINQ to 数据库提供器(一般是EF Core)无法将JsonConvert.DeserializeObject方法转换为可执行的SQL语句,只能在内存中执行C#方法逻辑。你追加ToList()后,会先把符合FormMasterStatus == "pending"条件的所有数据加载到内存中,后续的Where就变成了LINQ to Objects操作,自然可以正常调用Newtonsoft.Json的反序列化方法。
无需调用ToList()的实现方案
方案1:使用数据库原生JSON查询(最优,全程不加载冗余数据到内存)
如果你的数据库支持JSON查询(主流的SQL Server、PostgreSQL、MySQL均支持),且使用EF Core 7及以上版本,可以直接用EF内置的JSON函数映射,将整个查询逻辑翻译成数据库端的SQL执行,完全不需要拉取数据到内存处理。
以SQL Server为例,你只需要先确认FormMasterApprovalCcb字段在实体配置中标记为JSON类型,再按如下方式写查询即可:
var c = content.FormMasterLists .Where(x => x.FormMasterStatus == "pending") .Where(x => EF.Functions.JsonContains( x.FormMasterApprovalCcb, "{\"ApproverEmail\": \"christ\"}", "$.Group[*].Department[*].ApprovalCCBLevel[*].Approver[*]" ));
你可以根据自己使用的数据库类型调整对应的JSON函数,不同数据库的函数名和JSON路径写法略有差异,核心逻辑都是让EF提供器识别到可以将JSON匹配逻辑转换为原生数据库操作。
方案2:使用AsEnumerable()替代ToList()(内存优化版)
如果你使用的EF版本较低,或者数据库不支持JSON查询,无法做到完全不在内存中处理,也可以用AsEnumerable()替代ToList()优化内存占用:
var c = content.FormMasterLists .Where(x => x.FormMasterStatus == "pending") .Select(x => x.FormMasterApprovalCcb) // 仅查询需要的JSON字段,减少数据传输量 .AsEnumerable() // 切换为LINQ to Objects模式,流式加载数据,不会一次性生成全量列表 .Where(json => JsonConvert.DeserializeObject<ApprovalCCB>(json).Group .SelectMany(g => g.Department) .SelectMany(d => d.ApprovalCCBLevel) .SelectMany(l => l.Approver) .Any(a => a.ApproverEmail == "christ"));
AsEnumerable()不会像ToList()一样立刻把所有查询结果加载到内存列表中,而是逐个返回数据处理,数据量大的时候内存开销会小很多。
内容的提问来源于stack exchange,提问作者Chris Tuang

