Как мне прочитать несколько наборов результатов из функции PostgreSQL, используя Dapper? - PullRequest
0 голосов
/ 09 мая 2019

Я пытаюсь понять, как использовать 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 справиться с этим для вас?

...