首页
/ 在Npgsql.EntityFrameworkCore.PostgreSQL中调用PostgreSQL存储过程返回游标的问题解析

在Npgsql.EntityFrameworkCore.PostgreSQL中调用PostgreSQL存储过程返回游标的问题解析

2025-07-10 14:10:31作者:钟日瑜

存储过程与函数在PostgreSQL中的区别

在使用Npgsql.EntityFrameworkCore.PostgreSQL访问PostgreSQL数据库时,开发者经常会对存储过程(Stored Procedure)和存储函数(Stored Function)产生混淆。PostgreSQL中这两者有本质区别:

  1. 存储过程:使用CREATE PROCEDURE创建,主要用于执行操作而不返回结果,通过OUT参数或引用游标(REF CURSOR)返回数据
  2. 存储函数:使用CREATE FUNCTION创建,可以直接返回结果集或标量值

游标返回方式的常见问题

当开发者尝试通过EF Core调用返回游标的PostgreSQL存储过程时,经常会遇到"Required column was not present in the results"错误。这通常是因为:

  1. 错误地使用了存储过程而不是函数
  2. 游标处理方式不当
  3. 结果集映射存在问题

正确的实现方式

使用存储函数替代存储过程

PostgreSQL中更推荐使用函数来返回结果集,因为函数可以直接返回表类型:

CREATE OR REPLACE FUNCTION testschema.get_test_data()
RETURNS TABLE (id int, name text, email text) 
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN QUERY SELECT t.id, t.name, t.email FROM testschema.test t;
END;
$$;

在EF Core中调用函数

var testData = _context.Tests
    .FromSql($"SELECT * FROM testschema.get_test_data()")
    .ToList();

如果必须使用游标

如果确实需要使用游标返回结果,需要确保:

  1. 正确打开和关闭游标
  2. 在同一个事务中执行FETCH操作
  3. 确保结果集列名与实体属性完全匹配
CREATE OR REPLACE FUNCTION testschema.get_test_cursor(p_cursor_name text)
RETURNS void 
LANGUAGE plpgsql
AS $$
BEGIN
    OPEN p_cursor_name FOR SELECT id, name, email FROM testschema.test;
END;
$$;

在C#代码中:

using (var transaction = _context.Database.BeginTransaction())
{
    // 调用函数打开游标
    _context.Database.ExecuteSqlRaw($"SELECT testschema.get_test_cursor('mycursor')");
    
    // 获取游标数据
    var testData = _context.Tests
        .FromSql($"FETCH ALL FROM mycursor")
        .ToList();
    
    transaction.Commit();
}

结果集映射注意事项

无论使用哪种方式,都需要确保:

  1. 实体类属性与返回的列名完全一致(包括大小写)
  2. 主键属性使用[Key]特性标记
  3. 列数据类型与实体属性类型兼容

性能考虑

对于大数据集,游标方式可以减少内存使用,因为它允许分批获取数据。但对于大多数场景,直接返回表类型的函数更简单高效。

总结

在Npgsql.EntityFrameworkCore.PostgreSQL中处理PostgreSQL返回结果集时,优先考虑使用返回表类型的函数而非存储过程。如果必须使用游标,需要特别注意事务管理和游标生命周期。正确的结果集映射是避免"Required column was not present"错误的关键。

登录后查看全文
热门项目推荐
相关项目推荐