Я пытаюсь понять, как использовать Dapper для вызова функции PostgreSQL, которая возвращает несколько наборов результатов.Насколько я понимаю, в PostgreSQL лучший (единственный?) Способ добиться этого в настоящее время - объявить, что функция RETURNS SETOF REFCURSOR
.
Пример функции PostgreSQL, которая возвращает несколько REFCURSOR
s
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$;
Broken Dapper + PostgreSQL с несколькими REFCURSOR
s Пример
[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
s отлиты как T
.Это приведение приводит к T
со значениями null
для всех свойств.Затем следующий вызов results.Read<T>()
выдает следующее исключение:
System.ObjectDisposedException: 'The reader has been disposed; this can happen after all data has been consumed
Object name: 'Dapper.SqlMapper+GridReader'.'
Итак, как работает Dapper с несколькими PostgreSQL REFCURSOR
с? Есть ли способ прочитать результатыбез разыменования курсоров вручную?
У меня есть ванильный пример, который возвращает несколько REFCURSOR
s без использования Dapper, который работает, когда я вручную разыменовываю курсоры и читаю результаты, и у меня также есть примеры, которые работаютпротив хранимой процедуры SQL Server, которая возвращает несколько результатов.
Я (пока) не нашел какой-либо конкретной документации, которая указывает на конкретное различие в том, как QueryMultiple
следует вызывать для PostgreSQL против SQL Server, но такая документация будет принята с благодарностью.
Даже при вызове функции PostgreSQL, которая возвращает единичное REFCURSOR
с помощью Dapper, я счел необходимым вручную обрабатывать разыменование курсора, как в примере ниже.
Но из того, что я читал до сих пор, не похоже, что предполагается необходимым, хотя у меня возникли проблемы с поиском конкретной документации / примеров для Dapper + PostgreSQL, которыепокажите, как это должно работать в противном случае.
Рабочий Dapper + PostgreSQL с одним REFCURSOR
Пример
[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 + PostgreSQL + REFCURSOR
, всегда ли это необходимовручную почитать курсор, чтобы прочитать результаты? Или может Dapper справиться с этим для вас?