You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF Core 9+Npgsql自定义PgContains扩展方法无法翻译如何解决?

问题分析与解决

你遇到的问题主要来自两个核心错误:错误替换了Npgsql默认的方法翻译插件,以及翻译器仅支持常量搜索值,导致EF Core无法识别并翻译自定义的PgContains方法。以下是具体修复步骤:

1. 修复插件注册方式,保留默认翻译器

直接使用ReplaceService<IMethodCallTranslatorPlugin, CustomNpgsqlMethodCallTranslatorPlugin>会覆盖Npgsql内置的所有方法翻译器,不仅可能导致EF Core内置功能失效,还会让自定义翻译器无法融入原有体系。正确做法是创建一个组合插件,同时包含Npgsql默认翻译器和你的自定义翻译器:

using Microsoft.EntityFrameworkCore.Query;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators;

public class CombinedMethodCallTranslatorPlugin : IMethodCallTranslatorPlugin
{
    public CombinedMethodCallTranslatorPlugin(
        ISqlExpressionFactory sqlExpressionFactory,
        NpgsqlSqlExpressionFactory npgsqlSqlExpressionFactory,
        RelationalTypeMappingSource typeMappingSource)
    {
        // 初始化Npgsql默认翻译插件
        var npgsqlDefaultPlugin = new NpgsqlMethodCallTranslatorPlugin(
            sqlExpressionFactory, 
            npgsqlSqlExpressionFactory, 
            typeMappingSource);
        
        // 合并默认翻译器与自定义翻译器
        Translators = npgsqlDefaultPlugin.Translators
            .Concat(new[] { new PgContainsTranslator(sqlExpressionFactory) });
    }

    public IEnumerable<IMethodCallTranslator> Translators { get; }
}

然后在DbContext配置中替换为这个组合插件:

options.UseNpgsql(connectionString)
    .UseSnakeCaseNamingConvention()
    .ReplaceService<IMethodCallTranslatorPlugin, CombinedMethodCallTranslatorPlugin>();

2. 修复翻译器的参数限制,支持所有搜索值类型

原翻译器仅处理搜索值为常量的情况,若搜索值是变量(如方法参数、动态生成的表达式),翻译器会直接返回null,导致方法无法被翻译。修改PgContainsTranslator的Translate方法,去掉常量限制,改用SQL的concat函数统一添加通配符:

public class PgContainsTranslator : IMethodCallTranslator
{
    // 显式指定参数类型,确保MethodInfo匹配正确
    private static readonly MethodInfo Method = typeof(PgExtensions)
        .GetMethod(nameof(PgExtensions.PgContains), new[] { typeof(string), typeof(string) })!;

    private readonly ISqlExpressionFactory _sql;

    public PgContainsTranslator(ISqlExpressionFactory sql) => _sql = sql;

    public SqlExpression? Translate(
        SqlExpression? instance,
        MethodInfo method,
        IReadOnlyList<SqlExpression> arguments,
        IDiagnosticsLogger<DbLoggerCategory.Query> logger)
    {
        if (!Method.Equals(method)) return null;
        if (arguments.Count != 2) return null;

        var match = _sql.ApplyDefaultTypeMapping(arguments[0]);
        var value = _sql.ApplyDefaultTypeMapping(arguments[1]);

        // 用concat包裹%通配符,支持常量和变量值
        var wrappedValue = _sql.Function(
            "concat",
            new[] { _sql.Constant("%"), value, _sql.Constant("%") },
            typeof(string));

        var unaccentedMatch = _sql.Function(
            "unaccent", new[] { match }, true, new[] { true }, typeof(string));

        var unaccentedValue = _sql.Function(
            "unaccent", new[] { wrappedValue }, true, new[] { true }, typeof(string));

        return _sql.Function(
            "ILIKE", new[] { unaccentedMatch, unaccentedValue }, true, new[] { true, true }, typeof(bool));
    }
}

3. 验证PostgreSQL扩展安装

确保你的数据库已安装unaccent扩展,否则生成的SQL会报错。执行以下SQL语句:

CREATE EXTENSION IF NOT EXISTS unaccent;

额外验证

开启EF Core的查询日志,查看生成的SQL是否包含unaccent和ILIKE关键字,确认翻译逻辑已生效。

内容的提问来源于stack exchange,提问作者Max Bündchen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 10:54:52