#c# #npgsql
#c# #npgsql
Вопрос:
У меня есть следующая форма кода. Кажется, я неправильно понимаю возвращаемые значения метода C #. Как возможно, что «полный» перечислитель возвращается как пустой?
class ThingDoer
{
public NpgsqlDataReader DoQuery()
{
NpgsqlCommand c = new NpgsqlCommand(...);
NpgsqlDataReader dataread = c.ExecuteReader();
return dataread; // Debugger confirms that six data are enumerable here.
}
}
...
class OtherThing
{
public void higherLevelFunction()
{
NpgsqlDataReader result = myThingDoer.DoQuery();
result.Read(); // No data! result's enumerable returns nothing!
}
}
Комментарии:
1. Как отладчик «подтверждает» это? Что вы вызываете в отладчике? Пустой результат по-прежнему содержит поля, но без строк. Кроме того, где во всем этом NpgsqlConnection?
2. @Jon Hanna Я устранил всю дополнительную работу вокруг этого, включая обычные детали, такие как соединение, в пользу выделения реальной проблемы. Установив точку останова, я смог проверить данные, прочитанные до и после возврата.
Ответ №1:
Вы не указываете, откуда происходит ваше соединение. Предполагая, что это что-то вроде:
public NpgsqlDataReader DoQuery()
{
using(NpgsqlConnection = GetConnectionCode())
{
NpgsqlCommand c = new NpgsqlCommand(...);
NpgsqlDataReader dataread = c.ExecuteReader();
return dataread;
}//Connection closes at this using-scope being left because that triggers Dispose()
}
Затем измените его на:
public NpgsqlDataReader DoQuery()
{
bool ownershipPassed = false;
NpgsqlConnection conn = GetConnectionCode();
try
{
NpgsqlCommand c = new NpgsqlCommand(...);
NpgsqlDataReader dataread = c.ExecuteReader(CommandBehavior.CloseConnection);
ownershipPassed = true;
return dataread;
}
finally
{
if(!ownershipPassed)//only if we didn't create the reader than takes charge of the connection
conn.Dispose();
}
}
Затем, когда вы используете средство чтения, вы должны утилизировать его, чтобы, в свою очередь, утилизировать базовое соединение соединения с базой данных:
public void higherLevelFunction()
{
using(NpgsqlDataReader result = myThingDoer.DoQuery())
result.Read();
}
Комментарии:
1. Ах! Я понимаю, почему вы спросили о соединении сейчас. Извините за язвительность. 🙂
2. На самом деле я явно закрывал () соединение, думая, что оно исчерпало себя.
3. Я не прочитал это как язвительное. Решение о чем-то постороннем для проблемы, когда это на самом деле важно, случается с каждым из нас. Кстати, в более ранней версии Npgsql вызов Close() работал бы нормально, но на самом деле это был недостаток, потому что это приводило к тому, что большие наборы данных считывались полностью, а не по мере необходимости, что влияло на производительность в соответствии с npgsql.projects.postgresql.org/docs/manual /… но поскольку какой-то старый код (включая один из тестов NUnit)используя ваш подход,
Preload Reader
параметр commandstring возвращается к старому подходу…4. … однако, хотя этот параметр заставил бы ваш предыдущий код работать, он оказывает неприятное влияние на производительность (согласно графику, на который ссылается). Кроме того, поскольку спецификация IDataReader не обещает сохранения результатов в таком случае, вы столкнетесь с той же проблемой, если когда-либо перейдете к другому поставщику базы данных.
5. Вы явно или неявно
Close()
это делаете. Вы узнаете все причины, по которым вам следует закрывать соединения, и оusing
том, как упростить это, чтобы в этом не было ошибок. Когда вы вызываетеExecuteDataReader
сCommandBehavior.CloseConnection
помощью thenIDataReader
, соединение «становится владельцем», и его закрытие приведет к закрытию соединения. Следовательно, хотя в этом случае мы сами не закрываем и не удаляем соединение, нам все равно нужно убедиться, что мы закрываем программу чтения. Это может быть черезClose()
Dispose()
илиusing
с вызовомDispose()
. Подробнее см. Ответ, на который я ссылался выше.
Ответ №2:
NpgsqlCommand c = new NpgsqlCommand(...);
NpgsqlDataReader dataread = c.ExecuteReader();
Приведенные выше строки очень локальны для метода DoQuery. Итак, как только элемент управления выходит из метода, каждый объект, созданный внутри этого метода, теряет свою область видимости. Следовательно, вы теряете данные, потому что это ссылочный тип, на который вы ссылаетесь в вызывающем методе.
Комментарии:
1. Тогда как мне избежать потери объекта?
2. Сделайте объект connection переменной класса. Таким образом, он не потеряет свою область видимости. Даже то же самое с DataReader, вы можете создать его экземпляр, когда это требуется, я имею в виду, когда вы можете выполнить запрос, а затем создать его, т.е. new SqlCommand() , но пусть переменные находятся в области видимости класса. Любые способы создания объекта класса DoQuery в вашем потребительском коде, так что все в порядке!
3. Нет, вы наверняка можете вернуть NpgsqlDataReader из метода. Вы бы хотели убедиться, что соединение NpgsqlConnection не закрылось (лучше всего с помощью CommandBehaviour . CloseConnection, чтобы убедиться, что он в конечном итоге закрылся с помощью reader). Возможно, они тоже закрывают свое соединение (это не упоминается в вопросе), так что вы можете быть на правильном пути в отношении проблемы, но даже тогда область видимости сама по себе не является проблемой.
4. Дорогие боги! Не делайте соединение переменной класса.
5. @JonHanna Ваше право, если вообще, если OP создал соединение в глобальной области видимости, но в его коде кажется, что он делает что-то вроде кода new SqlCommand(new SqlConnection ….), Следовательно, очевидно, что он теряет свою область видимости и автоматически может или не может закрыть соединение. И я не думаю, что соединение автоматически закрывается только потому, что оно открыто в локальной области, если вы не используете Using(){} . Поправьте меня, если я ошибаюсь.