Member 14625254 Ответов: 2

Sqldatareader не возвращает значения


У меня есть SqlDataReader в методе "sqlSelectFreeStyle", вызываемом из другого класса, который используется для заполнения списка.

При отладке кода я вижу, что SqlDataReader имеет значения, но никакие значения не передаются классу, из которого он вызывается.

Что я уже пробовал:

Пожалуйста, сообщите, чего мне не хватает:

Метод:
public  SqlDataReader sqlSelectFreeStyle(string script)
        {
            SqlDataReader rdr;
            try
            {
                SqlCommand command = new SqlCommand(script, connection);
                rdr = command.ExecuteReader(CommandBehavior.SequentialAccess);

                return rdr;
            }
            catch (Exception ex)
            {
                status = ex.Message;
                return null;
            }

        }

________________________________________________________

Ниже вызывается SqlDataReader:
_________________________________________________________

public List<string> listOfCompanies()
        {
            
            List<string> companyList = new List<string>();
            string newRecord;
            getAppDBConnectionReady();

            SqlDataReader redr = connectSQL.sqlSelectFreeStyle(
                       "SELECT * FROM [CASH_F].[dbo].[companies] " +
                       "order by company_name;");
            //add compnay names to list
            if (redr.HasRows)
            {
                while (redr.Read())
                {
                    newRecord = redr["company_name"].ToString();
                    companyList.Add(newRecord);
                }
                redr.Close();
            }
            connectSQL.closeConnection();

            return companyList;
        }

2 Ответов

Рейтинг:
19

OriginalGriff

Во-первых, не делайте этого так, это имеет множество проблем.

1) это ленивый способ сделать это, который потенциально оставляет вас широко открытыми для атаки SQL-инъекций, которая может уничтожить всю вашу базу данных. Вместо этого всегда используйте параметризованные запросы.

Когда вы объединяете строки, вы вызываете проблемы, потому что SQL получает такие команды, как:

SELECT * FROM MyTable WHERE StreetAddress = 'Baker's Wood'
Цитата, добавленная пользователем, завершает строку в том, что касается SQL, и вы получаете проблемы. Но могло быть и хуже. Если я приду и наберу вместо этого: "x';DROP TABLE MyTable;--", то SQL получит совсем другую команду:
SELECT * FROM MyTable WHERE StreetAddress = 'x';DROP TABLE MyTable;--'
Которые SQL видит как три отдельные команды:
SELECT * FROM MyTable WHERE StreetAddress = 'x';
Совершенно правильный выбор
DROP TABLE MyTable;
Вполне допустимая команда "удалить таблицу"
--'
А все остальное-это комментарии.
Так оно и происходит: выбирает любые совпадающие строки, удаляет таблицу из базы данных и игнорирует все остальное.

Поэтому предоставление метода для чтения из БД, который принимает выбранную строку, просто опасно!

2) поскольку вы создаете объект SQLCommand в отдельном методе, нет никакой гарантии, что команда будет закрыта, как и соединение - на самом деле ваш код не закрывает считыватель, если в нем нет строк.
Создание команды и встроенного считывателя позволяет использовать using блоки, чтобы убедиться, что они закрыты и утилизированы, когда они выходят за рамки - и это уменьшает вероятность возникновения проблем позже.

3) что приводит нас к следующей проблеме: поскольку вы совместно используете соединение, если какая - либо часть вашего кода "забывает" закрыть возвращаемый считыватель, то соединение постоянно занято им-и следующая попытка запроса потерпит неудачу, потому что считыватель открыт на соединении, и SQL не позволит вам это сделать. Затем вы должны искать свой код в любом направлении, которое может оставить читателя открытым ...

4) Не указывайте SEQUENTIAL_ACCESS, если он вам действительно не нужен - в этом случае никогда не используйте SELECT*, потому что он возвращает все столбцы, нужны они вам или нет. Поскольку последовательный доступ предназначен только для очень-очень больших столбцов данных, зачем возвращать столбцы, которые вы не собираетесь использовать, и тратить всю эту пропускную способность? Скорее всего, это также приводит к сбою теста HasRows, поскольку вам приходится по-разному обрабатывать данные, когда вы их указываете.


MadMyche

+5

Member 14625254

Спасибо за оперативный ответ и вклад.

Рейтинг:
11

MadMyche

Перво - наперво-пожалуйста, обратите внимание на ответ № 1, который указывает на несколько проблем с уязвимостями кода, а также плохой дизайн, который может вызвать проблемы с вашими соединениями.

В моих приложениях я использую аналогичные процедуры для захвата данных; используя инкапсулированный класс SQL для извлечения.
Отличие заключается в том, что я не возвращаю объект SqlReader; скорее, я возвращаю простой datatable.

public static DataTable GetDataTableFromQuery(string connectionName, string SqlStatement, Collection<KeyValuePair<string, object>> CommandParamaterSet = null) {
	DataTable dt = null;
	using (SqlConnection conn = BuildConnection(connectionName)) {
		SqlCommand cmd = null;
		try {
			cmd = new SqlCommand(SqlStatement, conn) { CommandType = CommandType.Text };
			if (CommandParamaterSet.HasContent()) {
			   foreach (KeyValuePair<string, object> kvp in CommandParamaterSet){
					cmd.Parameters.AddWithValue(kvp.Key, kvp.Value); 		
				}
			}
			conn.Open();
			SqlDataReader dr = cmd.ExecuteReader();
			if (dr.HasRows) {
				dt = new DataTable();
				dt.Load(dr);
			}
		}
		catch (Exception ex) { throw ex; }
		finally { cmd.Dispose(); conn.Close(); conn.Dispose(); }
	}
	return dt;
}
Это более старая версия, и в ней есть некоторые инкапсулированные/специальные элементы, но она должна дать вам представление о том, как реализовать и что вы можете сделать, если/когда вам нужно добавить параметры к вашему запросу, чтобы избежать SQL-инъекции


Member 14625254

Спасибо за оперативный ответ и вклад. Мне нравится подход возврата DataTable.

MadMyche

Пожалуйста.
Пожалуйста, подумайте об использовании виджета star-review, чтобы дать оценку этому ответу