我试图了解如何使用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可以为您处理吗?
答案 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是痛苦的,但却是辉煌的。