我在使用 Postgres 枚举时遇到了一个奇怪的问题,尝试使用
ENUM
查询表时遇到错误,但前提是 CREATE
命令在同一会话期间运行。我不知道为什么会发生这种情况,这使得运行迁移脚本变得非常困难,因为我现在必须运行应用程序两次才能一切正常。
认为这是某种缓存问题,我尝试关闭连接池,但这似乎也不起作用。
它很容易重现,所以我制作了一个代码示例:
using Dapper;
using Npgsql;
var connection = new NpgsqlConnection("Host=127.0.0.1;Port=5432;User ID=postgres;Password=password;Database=messaging;Include Error Detail=true;");
await connection.OpenAsync();
var tableCommand = new NpgsqlCommand()
{
Connection = connection,
CommandText = """
CREATE TYPE message_type AS ENUM ('sms', 'email');
CREATE TABLE message (
id SERIAL PRIMARY KEY,
type message_type NOT NULL
);
"""
};
// Comment this line on a second run
await tableCommand.ExecuteNonQueryAsync();
var queryCommand = new NpgsqlCommand()
{
Connection = connection,
CommandText = """
SELECT id, type
FROM message;
"""
};
var reader = await queryCommand.ExecuteReaderAsync();
// The offending line
var parser = reader.GetRowParser<Message>();
class Message
{
public int Id { get; set; }
public string? Type { get; set; }
}
第一次运行时会产生以下错误:
System.InvalidCastException: 'Reading as 'System.Object' is not supported for fields having DataTypeName '.<unknown>''
Inner Exception
ArgumentException: A PostgreSQL type with the oid '0' was not found in the current database info
在第二次运行时(注释掉创建命令时),我没有收到任何错误,一切都按预期进行。第一次运行时几乎看不到枚举存在,但第二次一切都很好,因为在建立连接时它已经存在了。
我正在使用所有内容的最新版本(在撰写本文时):
Dapper 对 postgresql 枚举一无所知;我只能假设这是在 Npgsql 内部处理的,Npgsql 在需要时构建已知类型的缓存,因此如果您添加类型:它不会立即知道它。请注意,Dapper.AOT 正在添加更多枚举支持(目前还不存在),它将使用不同的 API,而不是
object
API;这可能会改变 Npgsql 的工作方式,希望变得更好!
您必须使用
await connection.ReloadTypesAsync();
重新加载在命令中创建的新类型。
这是一个示例,使用 Dapper 而不是命令,但您可以简单地在代码中插入重新加载类型命令:
await connection.ExecuteAsync("""
CREATE TYPE message_type AS ENUM ('sms', 'email');
CREATE TABLE message (
id SERIAL PRIMARY KEY,
type message_type NOT NULL
);
""");
await connection.ReloadTypesAsync(); // add this to your code
var parser = await connection.QueryAsync<Message>("""
SELECT id, type
FROM message;
""");
在 NpgsqlConnection 中记录了此方法刷新连接中加载的类型的缓存。