如何使用Dapper从PostgreSQL函数读取多个结果集?

时间:2019-05-08 21:48:45

标签: c# postgresql dapper npgsql

我试图了解如何使用Dapper调用返回多个结果集的PostgreSQL函数。我的理解是,在PostgreSQL中,目前实现此目的的最佳方法(唯一?)是声明函数RETURNS SETOF REFCURSOR

返回多个REFCURSOR的PostgreSQL函数示例

CREATE OR REPLACE FUNCTION public.testmultiplerefcursorfunc()
    RETURNS SETOF REFCURSOR
    LANGUAGE 'plpgsql'

    STABLE 
AS $BODY$
DECLARE 
    ref1    REFCURSOR;
    ref2    REFCURSOR;
BEGIN

    OPEN ref1 FOR
    SELECT      *
    FROM        characters;
    RETURN NEXT ref1;

    OPEN ref2 FOR
    SELECT      *
    FROM        planets;
    RETURN NEXT ref2;

END;
$BODY$;

带有多个REFCURSOR示例的破碎的Dapper + PostgreSQL

[Test]
public void UsingDapper_QueryMultiple_CallFunctionThatReturnsMultipleRefCursors_ReadsMultipleResultSetsViaMultipleRefCursors()
{
    // Arrange
    using (var conn = new NpgsqlConnection(_getConnectionStringToDatabase()))
    {
        var funcName = "testmultiplerefcursorfunc";
        var expect1 = CharacterTestData;
        var expect2 = PlanetTestData;
        conn.Open();

        using (var transaction = conn.BeginTransaction())
        {
            // Act
            using (var results = conn.QueryMultiple(
                funcName, 
                commandType: CommandType.StoredProcedure, 
                transaction: transaction))
            {
                var result1 = results.Read<Character>().AsList();
                var result2 = results.Read<Planet>().AsList();

                // Assert 
                CollectionAssert.AreEquivalent(expect1, result1);
                CollectionAssert.AreEquivalent(expect2, result2);
            }
        }
    }
}

上面的代码存在的问题是,当我进行第一个results.Read<T>()调用时,它试图返回两个都转换为REFCURSOR的{​​{1}}。然后,此强制转换会为所有属性的T值提供T。然后,对null的下一次调用将引发以下异常:

results.Read<T>()

那么, Dapper如何与多个PostgreSQL System.ObjectDisposedException: 'The reader has been disposed; this can happen after all data has been consumed Object name: 'Dapper.SqlMapper+GridReader'.' s一起工作?是否可以在不手动取消引用游标的情况下读取结果?

我有一个不使用Dapper就能返回多个REFCURSOR的原始示例,在我手动取消引用游标并读取结果的情况下,我也有一些示例适用于SQL Server存储过程返回多个结果。

我(尚未)找到任何特定的文档来指出PostgreSQL与SQL Server应该如何调用REFCURSOR的特定区别,但是将不胜感激。

即使调用使用Dapper返回单个QueryMultiple的PostgreSQL函数,我也发现有必要像下面的示例一样手动处理游标的取消引用。

但是到目前为止,尽管我在查找Dapper + PostgreSQL的具体文档/示例时遇到了麻烦,但似乎并没有必要 否则应该可以。

使用单个REFCURSOR示例运行Dapper + PostgreSQL

REFCURSOR

因此,使用Dapper + PostgreSQL + [Test] public void UsingDapper_Query_CallFunctionThatReturnsRefCursor_ReadsRowsViaRefCursor() { // Arrange using (var conn = new NpgsqlConnection(_getConnectionStringToDatabase())) { var procName = "testrefcursorfunc"; var expect = CharacterTestData; conn.Open(); using (var transaction = conn.BeginTransaction()) { // Act var cursorResult = (IDictionary<string, object>)conn .Query<dynamic>(procName, commandType: CommandType.StoredProcedure, transaction: transaction) .Single(); var cursorSql = $@"FETCH ALL FROM ""{(string)cursorResult[procName]}"""; var result = conn.Query<Character>( cursorSql, commandType: CommandType.Text, transaction: transaction); // Assert CollectionAssert.AreEquivalent(expect, result); } } } ,总是需要手动参考光标来读取结果吗?还是Dapper可以为您处理吗?

3 个答案:

答案 0 :(得分:0)

尝试使用conn.QueryMultipleAsync。在当前源中,您正在使用conn.QueryMultiple。这是它的完整指南。 Dapper Multiple

答案 1 :(得分:0)

您可以像这样使用。当然可以。.

public DataSet Manage_user_profiledata(string _prof_code)
        {
            string query = string.Format(@"select * from  Function_Name(@prof_code, @first_tbl, @second_tbl)");
            NpgsqlParameter[] sqlParameters = new NpgsqlParameter[3];
            sqlParameters[0] = new NpgsqlParameter("@prof_code", NpgsqlDbType.Varchar);
            sqlParameters[0].Value = Convert.ToString(_prof_code);

            //
            sqlParameters[1] = new NpgsqlParameter("@first_tbl", NpgsqlTypes.NpgsqlDbType.Refcursor);
            sqlParameters[1].Value = Convert.ToString("Avilable");
            sqlParameters[1].Direction = ParameterDirection.InputOutput;
            sqlParameters[1].NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Refcursor;
            //
            sqlParameters[2] = new NpgsqlParameter("@second_tbl", NpgsqlTypes.NpgsqlDbType.Refcursor);
            sqlParameters[2].Value = Convert.ToString("Assigned");
            sqlParameters[2].Direction = ParameterDirection.InputOutput;
            sqlParameters[2].NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Refcursor;
            return conn.executeMultipleSelectQuery(query, sqlParameters);

        }





public DataSet executeMultipleSelectQuery(string _query, NpgsqlParameter[] sqlParameter)
        {
            // NgpSql Init //
            npg_connection = new NpgsqlConnection(connstr);
            npg_command = new NpgsqlCommand(_query, npg_connection);
            // NgpSql Init //
            i = 0;
            try
            {
                ds = new DataSet();
                npg_connection.Open();
                NpgsqlTransaction tran = npg_connection.BeginTransaction();
                npg_command.CommandType = CommandType.Text;
                npg_command.Parameters.AddRange(sqlParameter);
                npg_command.ExecuteNonQuery();
                foreach (NpgsqlParameter parm in sqlParameter)
                {
                    if (parm.NpgsqlDbType == NpgsqlTypes.NpgsqlDbType.Refcursor)
                    {
                        if (parm.Value.ToString() != "null" || parm.Value.ToString() != "NULL" || parm.Value.ToString() != "")
                        {
                            string parm_val = string.Format("FETCH ALL IN \"{0}\"", parm.Value.ToString());
                            npg_adapter = new NpgsqlDataAdapter(parm_val.Trim().ToString(), npg_connection);
                            ds.Tables.Add(parm.Value.ToString());
                            npg_adapter.Fill(ds.Tables[i]);
                            i++;
                        }
                    }
                }
                tran.Commit();
                return ds;
            }
            catch (Exception ex)
            {
                ds_ERROR.Tables[0].Rows.Add(ex.ToString(), ex.Message.ToString());
                return ds_ERROR;
            }
            finally
            {
                npg_connection.Close();
            }
        }

答案 2 :(得分:0)

来自sql server的后台,它只是存储过程中select语句列表的问题,以及您的“好消息”;使用postgresql并要求使用refcursor并“取回”另一端的这些refcusor可能会很痛苦。

我可以建议的是:

1。)使用带有反射器的PostgreSQL过程作为INOUT参数。

CREATE OR REPLACE PROCEDURE public.proc_testmultiplerefcursor(INOUT ref1 refcursor, INOUT ref2 refcursor)

2。)调用该过程,然后使用“ FETCH ALL”为返回的数据获取反射器。

用反射器的名称填充INOUT参数,以便在我使用'ref1'和'ref2'的情况下可以恢复它们。

var sql = "BEGIN;CALL public.proc_testmultiplerefcursor(@pentity_id,'ref1','ref2');" +
                  "FETCH ALL FROM  ref1; " +
                  "FETCH ALL FROM  ref2;" +
                  "COMMIT;";

3。)然后是您常用的Dapper QueryMutliple和Reads。

var multi = await conn.QueryMultipleAsync(sql);
var result1 = (await multi.ReadAsync<Character>()).AsList();
var result2 =(await multi.ReadAsync<Planet>()).AsList();

这未经测试,但我希望可以有所帮助。 PostgreSQL是痛苦的,但却是辉煌的。