EF Core中LINQ to Entities表达式内提取字符串过滤无法翻译如何解决
解决方案
EF Core 抛出翻译异常的根本原因是自定义C#扩展方法无法被直接转换为SQL语句,以下是几种兼容多数据库的实现方案:
方案1:直接使用EF Core内置可翻译字符串方法
EF Core原生支持IndexOf、LastIndexOf、Substring、Contains等基础字符串方法的SQL翻译,直接把逻辑写在Where表达式中即可:
var result = context.Company.Where(c => (c.FullName.IndexOf('"') == -1 || c.FullName.IndexOf('"') == c.FullName.LastIndexOf('"')) ? c.FullName.Contains(filterString) : c.FullName.Substring(c.FullName.IndexOf('"') + 1, c.FullName.LastIndexOf('"') - c.FullName.IndexOf('"') - 1) .Contains(filterString) ).ToList();
优势是无需额外配置,代码改动最小,EF会自动适配不同数据库生成对应SQL。
方案2:映射自定义函数到EF Core
如果希望保留封装逻辑的写法,可以通过EF Core的DbFunction特性把自定义方法映射为可翻译函数:
- 首先在DbContext中定义静态方法并标记DbFunction
public class AppDbContext : DbContext { public DbSet<Company> Companies { get; set; } [DbFunction] public static string ExtractShortName(string name) { // 客户端评估时的实现 int firstQuotes = name.IndexOf('"'); int lastQuotes = name.LastIndexOf('"'); if (firstQuotes == -1 || firstQuotes == lastQuotes) { return name; } return name.Substring(firstQuotes + 1, lastQuotes - firstQuotes - 1); } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置函数的SQL翻译逻辑,可针对不同数据库做适配 modelBuilder.HasDbFunction(typeof(AppDbContext).GetMethod(nameof(ExtractShortName), new[] { typeof(string) })) .HasTranslation(args => { // 此处按使用的数据库类型拼接对应表达式即可,EF会自动转换为对应SQL语法 }); } }
- 查询时直接调用该静态方法即可:
var result = context.Company.Where(c => AppDbContext.ExtractShortName(c.FullName).Contains(filterString)).ToList();
方案3:客户端评估(仅适合小数据量表)
如果表数据量很小,可以先把数据加载到内存再执行自定义扩展方法:
var result = context.Company .AsEnumerable() // 切换到客户端评估,后续逻辑不再转SQL .Where(c => c.FullName.ExtractShortNameExtension().Contains(filterString)) .ToList();
注意该方案会加载符合前置条件的所有数据到内存,数据量大会有严重性能问题,不推荐生产环境大表使用。
内容的提问来源于stack exchange,提问作者Misha
相关产品推荐
相关产品推荐

