如何在.NET 6中读取PostgreSQL函数返回的<unnamed portal 1>数据?
从PostgreSQL的<unnamed portal 1>中读取数据的解决方案
背景信息
PostgreSQL函数
CREATE OR REPLACE FUNCTION public.fn_test() RETURNS refcursor AS $$ DECLARE ref1 refcursor; BEGIN OPEN ref1 FOR Select * from public.testTable; RETURN ref1; END; $$ LANGUAGE plpgsql;
C# .NET 6代码
string connString = String.Format("Server={0};Database={1};User Id={2};Password={3};Port={4}", Host, "testDB", Username, Password, Port); NpgsqlConnection pgCon = new NpgsqlConnection(connString); pgCon.Open(); string strCmd = @"select * from public.fn_test();"; NpgsqlCommand pgCmd = new NpgsqlCommand(strCmd, pgCon); NpgsqlDataReader pgDr = pgCmd.ExecuteReader(); //返回<unnamed portal 1>
问题
调用上述函数后,ExecuteReader()返回了<unnamed portal 1>,如何从中读取数据?
环境信息:
- PostgreSQL版本:14.5
- .NET版本:6
- Visual Studio版本:2022
解决方案
要读取refcursor返回的结果,需遵循PostgreSQL的游标机制,具体实现方式如下:
方式一:使用命名游标(推荐)
修改PostgreSQL函数,指定明确的游标名称,避免依赖系统生成的无名游标:
CREATE OR REPLACE FUNCTION public.fn_test() RETURNS refcursor AS $$ DECLARE ref1 refcursor := 'my_test_cursor'; -- 指定自定义游标名称 BEGIN OPEN ref1 FOR Select * from public.testTable; RETURN ref1; END; $$ LANGUAGE plpgsql;
对应调整C#代码,通过游标名称执行FETCH命令读取数据:
string connString = String.Format("Server={0};Database={1};User Id={2};Password={3};Port={4}", Host, "testDB", Username, Password, Port); // 使用using语句自动释放资源 using (NpgsqlConnection pgCon = new NpgsqlConnection(connString)) { pgCon.Open(); // 游标必须在事务上下文内操作 using (var transaction = pgCon.BeginTransaction()) { string strCmd = @"select * from public.fn_test();"; NpgsqlCommand pgCmd = new NpgsqlCommand(strCmd, pgCon, transaction); // 获取函数返回的游标名称 var cursorName = pgCmd.ExecuteScalar().ToString(); // 执行FETCH命令读取游标内所有数据 var fetchCmd = new NpgsqlCommand($"FETCH ALL FROM {cursorName};", pgCon, transaction); using (NpgsqlDataReader pgDr = fetchCmd.ExecuteReader()) { while (pgDr.Read()) { // 处理单条数据示例:读取指定列值 var targetValue = pgDr["your_column_name"]; // 此处编写业务逻辑 } } transaction.Commit(); } }
方式二:直接读取无名游标
若不想修改PostgreSQL函数,可直接使用系统返回的<unnamed portal 1>名称执行FETCH,但该方式依赖PostgreSQL内部命名规则,稳定性较差:
string connString = String.Format("Server={0};Database={1};User Id={2};Password={3};Port={4}", Host, "testDB", Username, Password, Port); using (NpgsqlConnection pgCon = new NpgsqlConnection(connString)) { pgCon.Open(); using (var transaction = pgCon.BeginTransaction()) { string strCmd = @"select * from public.fn_test();"; NpgsqlCommand pgCmd = new NpgsqlCommand(strCmd, pgCon, transaction); // 先执行函数打开游标,关闭临时DataReader pgCmd.ExecuteReader().Close(); // 通过无名游标名称读取数据 var fetchCmd = new NpgsqlCommand("FETCH ALL FROM \"<unnamed portal 1>\";", pgCon, transaction); using (NpgsqlDataReader pgDr = fetchCmd.ExecuteReader()) { while (pgDr.Read()) { // 处理数据逻辑 } } transaction.Commit(); } }
关键注意事项
- 游标操作必须在事务上下文中执行,PostgreSQL不允许在事务外使用游标。
- 始终使用
using语句管理数据库连接、命令和数据阅读器,避免资源泄漏。 - 优先选择命名游标方案,避免依赖系统生成的无名游标名称,提升代码稳定性。
内容的提问来源于stack exchange,提问作者Shankar Naru
相关产品推荐
相关产品推荐

