EF Core通过Linq查询PostgreSQL inet字段时翻译报错的解决方法
我有一个实体类,包含类型为IPAddress的Ipaddr属性,该属性按预期存储在PostgreSQL数据库的inet类型字段中。为实现子串搜索,我编写了如下方法:
public async Task<IEnumerable<Switch>> GetSwitches(string? searchIpaddr, string? searchSysname) { var switches = context.Switches.Include(s => s.Network) as IQueryable<Switch>; if (!string.IsNullOrWhiteSpace(searchSysname)) { searchSysname = searchSysname.Trim(); switches = switches.Where(s => EF.Functions.ILike(s.Sysname, $"%{searchSysname}%")); } if (!string.IsNullOrWhiteSpace(searchIpaddr)) { searchIpaddr = searchIpaddr.Trim(); switches = switches.Where(s => EF.Functions.ILike(s.Ipaddr.ToString(), $"%{searchIpaddr}%")); } return await switches.ToListAsync(); }
针对字符串类型的Sysname属性,该方法可以正常工作,但针对Ipaddr属性时出现报错:
System.InvalidOperationException: The LINQ expression 'DbSet() .Where(s => s.Ipaddr.ToString() == __ip_0)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'.
问题核心是Ipaddr.ToString()无法被EF Core翻译为PostgreSQL可执行的SQL语句。要在数据库端完成过滤,需改用EF Core支持的类型转换方式,以下是两种可行方案:
方案1:使用EF Core内置Cast函数
通过EF.Functions.Cast<string>()将数据库inet字段转换为文本类型,再执行LIKE查询:
if (!string.IsNullOrWhiteSpace(searchIpaddr)) { searchIpaddr = searchIpaddr.Trim(); switches = switches.Where(s => EF.Functions.ILike(EF.Functions.Cast<string>(s.Ipaddr), $"%{searchIpaddr}%")); }
方案2:使用Npgsql专属扩展函数
若已安装Npgsql.EntityFrameworkCore.PostgreSQL包,可使用其提供的ToInetString方法直接转换inet字段为字符串:
using Npgsql.EntityFrameworkCore.PostgreSQL; // ... if (!string.IsNullOrWhiteSpace(searchIpaddr)) { searchIpaddr = searchIpaddr.Trim(); switches = switches.Where(s => EF.Functions.ILike(NpgsqlFunctions.ToInetString(s.Ipaddr), $"%{searchIpaddr}%")); }
两种方案都会生成对应的PostgreSQL SQL(如CAST(ipaddr AS text)),确保过滤操作在数据库端执行,避免加载全量数据到客户端。
内容的提问来源于stack exchange,提问作者Vladal

