SqlSugar 连接 PostgreSQL 报错 42P01: relation does not exist 的排查与修复

一、问题现象

在项目中调用 GET /api/drugs?drugId=xxx 接口时,服务端抛出 500 异常,日志关键信息如下:

arduino 复制代码
2026-06-15 17:09:37.599 +08:00 [ERR] An unhandled exception has occurred while executing the request.
Npgsql.PostgresException (0x80004005): 42P01: relation "drugs" does not exist
   at SqlSugar.QueryableProvider`1.FirstAsync()
   at ...Services.DrugService.GetDrugByIdAsync(String drugId)
   at ...Controllers.DrugController.GetDrugByIdAsync(String drugId)

报错代码 42P01 在 PostgreSQL 中代表「关系不存在」,通常是指查询中引用的表(或视图)在当前数据库内找不到。

二、从 SQL 日志中找线索

打开 Program.cs 查看 SqlSugar 注册代码,注意到配置了 Aop.OnLogExecuting 事件,所以日志中完整输出了生成的 SQL:

sql 复制代码
SQL: SELECT "id","name","normalizedname",... FROM "drugs" WHERE ( "id" = @Id0 ) LIMIT 1 offset 0

与此同时,Drugs 实体类定义在 MyCommon/Entities/Drugs.cs 中:

csharp 复制代码
[SugarTable("Drugs")]
public partial class Drugs
{
    [SugarColumn(IsPrimaryKey = true)]
    public string Id { get; set; } = null!;
    public string Name { get; set; } = null!;
    // ...
}

把上面两段信息放在一起,问题就浮出水面了:

代码中的名称 SQL 中实际出现的名称
Drugs(类名 + SugarTable) "drugs"(双引号包裹,全小写)
Id(属性名) "id"(双引号包裹,全小写)
Name(属性名) "name"(双引号包裹,全小写)

SqlSugar 生成的表名和列名与实体类中定义的完全对不上------这就是 42P01 的直接原因。

三、PostgreSQL 的大小写敏感规则

在继续分析之前,必须先明确一条 PostgreSQL 的核心行为规则:

不加双引号的标识符会被自动折叠为小写,因此大小写不敏感;加上双引号 "Name" 包裹的标识符会严格区分大小写。

举例来说:

sql 复制代码
CREATE TABLE Drugs (...);    -- 实际存进系统表的是 drugs(小写)
CREATE TABLE "Drugs" (...);  -- 实际存进系统表的是 Drugs(保留大写)

SELECT * FROM Drugs;         -- 等价于 SELECT * FROM drugs; 能命中
SELECT * FROM "Drugs";       -- 只能命中保留大写的表 "Drugs"
SELECT * FROM "drugs";       -- 只能命中保留小写的表 "drugs"

Drugs 实体标注了 [SugarTable("Drugs")],说明期望查询的表叫 Drugs;但 SqlSugar 实际生成的 SQL 却变成了 "drugs"。如果数据库中的表名是按 PostgreSQL 默认行为建的(即小写 drugs),理论上应该命中;如果按保留大写建的("Drugs"),那就匹配不上。

无论数据库中的表是哪一种,让 SqlSugar 按 PostgreSQL 的默认小写规则去生成 SQL ,是兼容性最好的做法。而控制 SqlSugar 这一行为的开关,正是 ConnMoreSettings 中的 PgSqlIsAutoToLower

四、真正的根因:缺少 ConnMoreSettings 配置

回到项目中 Program.cs,最初注册 SqlSugar 的代码是这样的(修改前):

csharp 复制代码
builder.Services.AddScoped<ISqlSugarClient>(options =>
{
    ISqlSugarClient sugarClient = new SqlSugarClient(new ConnectionConfig()
    {
        ConnectionString = configuration.GetConnectionString("DefaultConnection"),
        DbType = DbType.PostgreSQL,
        IsAutoCloseConnection = true,
        // ← 这里没有配置 ConnMoreSettings
    }, db => { db.Aop.OnLogExecuting = (sql, pars) => { Log.Information($"SQL: {sql} {pars}"); }; });
    return sugarClient;
});

没有配置 MoreSettings ,意味着 SqlSugar 对 PostgreSQL 的大小写处理使用其内部默认逻辑,生成的表名/列名与数据库中实际的表名列名不匹配 ,于是报 42P01

注意:我在第一次排查时曾误以为代码里已有 PgSqlIsAutoToLower = false,后经指正才明确------原始代码里根本没有这段配置PgSqlIsAutoToLower = false 是用户后来手动加上的「尝试性修复」,但它同样不对(显式禁止小写转换依然无法匹配数据库中的小写表名)。

五、修复方案

ConnectionConfig 中添加 MoreSettings,并将三个 PgSql*ToLower 开关设为 true

csharp 复制代码
builder.Services.AddScoped<ISqlSugarClient>(options =>
{
    ISqlSugarClient sugarClient = new SqlSugarClient(new ConnectionConfig()
    {
        ConnectionString = configuration.GetConnectionString("DefaultConnection"),
        DbType = DbType.PostgreSQL,
        IsAutoCloseConnection = true,
        MoreSettings = new ConnMoreSettings()
        {
            PgSqlIsAutoToLower = true,
            PgSqlIsAutoToLowerSchema = true,
            PgSqlIsAutoToLowerCodeFirst = true,
        }
    }, db => { db.Aop.OnLogExecuting = (sql, pars) => { Log.Information($"SQL: {sql} {pars}"); }; });
    return sugarClient;
});

三个开关的作用:

配置项 作用
PgSqlIsAutoToLower 普通查询时,是否将表名、列名自动转为小写
PgSqlIsAutoToLowerSchema 模式(Schema)名是否自动转小写
PgSqlIsAutoToLowerCodeFirst Code First 建表/迁移时是否使用小写

设置为 true 后,SqlSugar 会把 [SugarTable("Drugs")] 处理成 "drugs",把 IdName 等属性处理成 "id""name",与 PostgreSQL 中默认小写的标识符规则完全对齐。

六、验证步骤

  1. 重新编译dotnet build,确认无编译错误
  2. 重启服务:运行项目
  3. 观察启动日志:确认启动信息和连接字符串正常输出
  4. 调用接口GET /api/drugs?drugId=xxx
  5. 核对 SQL 日志 :期望看到 FROM "drugs"(全小写),不再有 42P01
  6. 接口返回 :能正确返回 Drugs 对象或 null

七、经验总结

这次排查的关键收获有三点:

1. PostgreSQL 的「双引号陷阱」必须牢记。 不加引号时大小写不敏感,加了引号就严格区分大小写。在团队协作中,建议统一采用「建表不加双引号,SQL 中也不要手写双引号」的约定,从源头避免此类问题。

2. SqlSugar 连接 PostgreSQL 必须显式配置 ConnMoreSettings 尤其是 PgSqlIsAutoToLower 这个开关,它直接决定了 ORM 层面与数据库层面标识符能否对上。缺失这段配置是比写 = false 更隐蔽的坑------因为看起来好像什么都没做错。

3. 善用 ORM 的 SQL 日志输出。 SqlSugar 的 OnLogExecuting 是排查这类问题的最直接工具。如果看不到实际生成的 SQL,你可能永远猜不到是大小写在作怪。养成在开发环境开启 SQL 日志的习惯,能帮你省下大量定位时间。

相关推荐
北域码匠10 小时前
嵌入式限幅滤波:工业信号降噪利器
c#·传感器采集·数据预处理·嵌入式算法·限幅滤波·数字滤波·数据降噪
csdn_aspnet19 小时前
C# 提取、截取或匹配字符串内包含指定字符的一些方法分享
c#·字符串·正则·分割·提取·匹配
枳实-叶19 小时前
【Linux驱动开发】第23天:spi_driver 的 probe / remove 函数实现规范
linux·驱动开发·c#
长明19 小时前
C#项目组织与概念梳理
后端·c#
迷路爸爸18020 小时前
Python collections 入门+实战
windows·python·c#·collections·dict
csdn_aspnet20 小时前
C# 截取或匹配字符串内包含指定字符的一些方法
c#·字符串·分割·string·匹配·截取
Rotion_深20 小时前
C# 值类型与引用类型 详解
开发语言·jvm·c#
影寂ldy1 天前
C# try-catch 异常处理全套笔记
服务器·数据库·c#
TeamDev1 天前
JxBrowser 9.3.0 版本发布啦!
java·后端·c#·混合应用·jxbrowser·浏览器控件·异步媒体设备
梦帮科技1 天前
UE5 GAS 实战:用 Gameplay Ability System 搭建「赛博修真」境界与技能体系
c++·人工智能·python·ue5·c#