Entity Framework Core执行PostgreSQL查询时出现格式异常
解决PostgreSQL EF Core中"Input string was not in correct format"异常
这个问题我之前遇到过,根源是字符串格式化时的占位符冲突,咱们一步步拆解原因和解决方案:
问题原因
你定义的_RFS_QUERY_FILTER里包含大量JSON格式的{和}字符,而EF Core的FromSql在处理带参数的SQL字符串时,会调用.NET的String.Format来解析{0}这种占位符。但JSON里的{}会被String.Format误判为额外的格式占位符(因为它只认{数字}的规范格式),导致解析失败抛出FormatException。
解决方案
这里有两种可靠的解决方式,推荐第二种参数化查询(更安全,还能防SQL注入):
方式1:转义JSON中的{}字符
把JSON里的每个{替换成{{,每个}替换成}},这样String.Format会把它们当成普通字符处理,而不是占位符:
// 转义JSON中的{}为{{}},避免被String.Format误解析 private const string _RFS_TAG = "NEW_RFS_WLI"; private const string _RFS_QUERY_FILTER = "'{{\"bsareadyforservicewli\": {{\"messageheader\": {{\"messagetype\": \"{0}\"}}}}}}'"; // 先预解析RFS标签到过滤条件中 private const string _RESOLVED_RFS_FILTER = string.Format(_RFS_QUERY_FILTER, _RFS_TAG); // 查询语句中的{0}是entry point的参数,正常保留 private const string _LATEST_RFS_FOR_ENTRY_POINT_QUERY = @" SELECT o.* FROM connectivity_order_entry_points as oep INNER JOIN connectivity_orders as o ON oep.connectivity_order_id = o.id INNER JOIN connectivity_order_updates as ou ON o.id = ou.connectivity_order_id WHERE oep.connectivity_entry_point_id = {0} AND ou.data @> {1} ORDER BY ou.id DESC LIMIT 1;"; // 调用方法 public async override Task<Order> GetActiveOrderForEntryPoint(int id) { var sql = string.Format(_LATEST_RFS_FOR_ENTRY_POINT_QUERY, id, _RESOLVED_RFS_FILTER); return await Context.Orders.FromSql(sql).FirstOrDefaultAsync(); }
方式2:使用参数化查询(推荐)
直接用PostgreSQL的参数占位符(比如@paramName),配合EF Core的参数传递,完全避免字符串格式化的问题,还能防止SQL注入:
private const string _RFS_TAG = "NEW_RFS_WLI"; // 定义带参数占位符的SQL语句 private const string _LATEST_RFS_FOR_ENTRY_POINT_QUERY = @" SELECT o.* FROM connectivity_order_entry_points as oep INNER JOIN connectivity_orders as o ON oep.connectivity_order_id = o.id INNER JOIN connectivity_order_updates as ou ON o.id = ou.connectivity_order_id WHERE oep.connectivity_entry_point_id = @entryPointId AND ou.data @> @rfsFilter::jsonb ORDER BY ou.id DESC LIMIT 1;"; // 调用方法 public async override Task<Order> GetActiveOrderForEntryPoint(int id) { // 用序列化构建JSON过滤条件,避免手动拼接字符串 var rfsFilter = JsonSerializer.Serialize(new { bsareadyforservicewli = new { messageheader = new { messagetype = _RFS_TAG } } }); // 传递参数,EF Core会自动处理参数绑定 return await Context.Orders.FromSql(_LATEST_RFS_FOR_ENTRY_POINT_QUERY, new NpgsqlParameter("@entryPointId", id), new NpgsqlParameter("@rfsFilter", rfsFilter)) .FirstOrDefaultAsync(); }
验证说明
第二种方式的优势很明显:不需要处理繁琐的转义,还能确保参数的安全性,尤其是当JSON结构复杂时,序列化的方式比手动拼接字符串更不容易出错。
内容的提问来源于stack exchange,提问作者mschuurmans
相关产品推荐
相关产品推荐

