Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSPDB-Lab3-2012.doc
Скачиваний:
12
Добавлен:
15.04.2015
Размер:
258.05 Кб
Скачать

Федеральное агентство по образованию

ГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ

ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ

РЯЗАНСКИЙ ГОСУДАРСТВЕННЫЙ РАДИОТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ

КЛИЕНТ-СЕРВЕРНЫЕ ПРИЛОЖЕНИЯ БАЗ ДАННЫХ

Методические указания к лабораторным работам

Рязань 2012

УДК 004.655.3 (078.8)

Клиент-серверные приложения баз данных: Методические указания к лабораторным работам. Лабораторная работа №2 / Рязан. гос. радиотехн. универ.; Сост. А.Н. Андреев, А.В. Благодаров, Н.Н. Гринченко, Рязань, 2012. 14с.

Содержат рекомендации для проведения лабораторных работ, посвященных изучению взаимодействия клиентских приложений с базами данных.

Предназначены для студентов очной и заочной форм обучения специальностей 230101 «Вычислительные машины, комплексы, системы и сети», 230104 «Системы автоматизированного проектирования», 230105 «Программное обеспечение вычислительной техники и автоматизированных систем», 080801 «Прикладная информатика», 090102 «Компьютерная безопасность» по дисциплинам «Базы данных», «Системы управления базами данных», «Проектирование баз данных», «Клиент-серверные приложения баз данных», «Проектирование распределенных баз данных».

Клиент-серверные технологии баз данных

Составители

Андреев Александр Николаевич

БлагодаровАндрей Витальевич

ГринченкоНаталья Николаевна

Лабораторная работа №3 организация запросов к базам данных

Цель работы:Научиться выполнять различные типы запросов к БД из клиентских приложений на языке С# с использованием технологииADO.NET.

Организация запросов к БД

Рассмотрим особенности организации запросов к БД из клиентского приложения. Различают три вида запросов:

  • Статические запросы– текст запроса полностью формируется на этапе разработки приложения;

  • Параметрические запросы– текст запроса формируется на этапе разработки приложения и содержит несколько параметров, во время выполнения приложения можно задавать значения параметров;

  • Динамические запросы– текст запроса полностью формируется на этапе выполнения приложения.

Запросы к БД под СУБД MSSQLServerможно выполнить с помощью объектов классаSqlCommand.

Класс SqlCommandможно использовать для выполнения как операторов подмножестваDML(SELECT, INSERT, UPDATE, DELETE), так и для иных операторов языкаSQL(CREATE,ALTER,DROPи др.), что будет рассмотрено в данной работе. Также классSqlCommandиспользуются для вызова хранимых процедур или получения прямого доступа к записям определенной таблицы (TableDirect). Эти возможности будут расcмотрены в последующих лабораторных работах.

Объект класса SqlCommandвзаимодействует с базой данных, через объект классаSqlConnection, который рассматривался в предыдущей лабораторной работе.

Рассмотрим особенности работы с классом SqlCommand. Необходимыми для настройки свойствами объекта классаSqlCommandявляются:

  • Connection– ссылка на объект классаSqlConnectionдля подключения к базе данных;

  • CommandText– текст запроса, который требуется выполнить;

  • CommandType– свойство, показывающее, как необходимо интерпретировать текст запроса. Возможны следующие значения:Text–SQLзапрос;StoredProcedure– хранимая процедура;TableDirect – прямой доступ к таблице. По умолчанию используется значениеText.

Как и другие виды объектов, объект класса SqlCommand может создаваться и настраиваться как визуально, так и программно.

Для визуального создания объекта класса SqlCommandследует сначала добавить его компонент вToolbox. Для этого выполним действиеToolsChoose Toolbox Items(Сервис – Выбрать элементы панели инструментов). В открывшемся окне установим флажок для пунктаSqlCommand. Теперь объект классаSqlCommandможно поместить на форму, настроить и использовать в приложении.

Для программного создания объекта типа SqlCommandможно использовать два способа.

1. Cпомощью конструктора. Например:

SqlCommand mySqlCommand = new SqlCommand(

"SELECT * FROM Товар"

);

2. Cпомощью вызова методаCreateCommand()объекта классаSqlConnection:

SqlCommand mySqlCommand = mySqlConnection.CreateCommand();

В последнем случае свойство Connection объектаmySqlCommand автоматически настраивается на объектmySqlConnection.

Методы объекта SqlCommand

Выполнение запросов может инициироваться в объекте класса SqlCommandразличными методами в зависимости от используемого оператора языкаSQL.

Метод ExecuteNonQuery()применяется для выполнения запросов, не возвращающих данные, таких как CREATE, ALTER, DROP, UPDATE, INSERT, DELETE и др. Такие запросы вносят изменения в таблицы базы данных, не возвращая набор данных в результате выполнения.

Для команд UPDATE, INSERT, DELETE метод ExecuteNonQuery()возвращает целое число, которое соответствует количеству записей, затронутых запросом. Это значение может применяться для проверки правильности выполнения запроса.

При выполнении DML-запросов, таких как CREATE, ALTER, DROP метод ExecuteNonQuery()возвращает значение -1.

Метод ExecuteScalar()возвращает первое поле первой записи набора результатов. Такой метод следует применять для запросов, возвращающих одно значение. Такие запросы возникают при использовании агрегатных функций COUNT, MIN, MAX и др.

Метод ExecuteReader()применяется для получения набора данных из базы данных. Особенностью этого метода является то, что он возвращает специальный объект классаSqlDataReader, с помощью которого просматриваются записи.

SqlDataReaderпредставляет собой результирующий набор данных только для чтения и является самым простым и быстрым средством доступа к данным. Результирующий набор – это совокупность строк и столбцов, возвращаемых оператором SELECT. Недостатком классаSqlDataReaderявляется отсутствие в нем некоторых возможностей, например, сортировки, как в более развитых классах.

Для чтения данных класс SqlDataReaderпредоставляет методRead(). Данный метод перемещает курсор строки на следующую строку в наборе. До первого вызова методаRead()курсор установлен перед первой строкой объектаSqlDataReader. При каждом вызове методаRead() курсор переходит к следующей строке набора. После считывания одной строки набора и перехода к следующей, предыдущая становится недоступной.

Метод Read()возвращает логическое значение, указывающее есть ли в объектеSqlDataReaderследующая запись. Возвращает true, если следующая строка доступна, false - если чтение набора данных завершено.

К данным в текущей строке результата можно обращаться как по порядковому номеру столбца, начиная с 0, так и по имени столбца.

Объект класса SqlDataReaderнеобходимо закрывать методомClose(), чтобы освободить данное соединение, например, для выполнения через него других запросов.

Далее рассмотрим, каким образом могут создаваться различные виды запросов.

Создание статического запроса

Рассмотрим пример создания статического запроса к БД.

Пусть требуется изменить цены на товары, произведенные в России, увеличив их стоимость на 10%.

Для этого создадим приложение, выполнив следующие действия.

1. Создайте новый проект с именем SampleCommandStatic1.

2. Измените свойство формы Text, задав значение "Статический запрос1". Перетащите с вкладкиDataпанели инструментов объектButton, задав имяbtnShowи свойствоText = "Выполнить", а также объектTextBox, задав ему имяTextBoxResult.

3. Добавьте пространство имен:

using System.Data.SqlClient;

4. Добавьте обработчик для кнопки.

SqlConnection con = new SqlConnection(

"Data Source=srv\\sql_srv;"+

"Initial Catalog=Supply;"+

"User ID=sa;"+

"Password=sa;" );

SqlCommand cmd = con.CreateCommand();

cmd.CommandText = "UPDATE Товары

SET Цена = Цена * 1.1

WHERE Производитель = 'Россия'";

con.Open();

int result = cmd.ExecuteNonQuery();

if (result>0)

TextBoxResult.Text="Изменено строк" + result.ToString();

else

TextBoxResult.Text="Нет изменений";

con.Close();

5. Запустите приложение.

После нажатия на кнопку, в окне приложения появится результат – текстовое сообщение об изменениях в таблице Товары (рисунок 1).

Рисунок 1 – Приложение, использующее метод ExecuteNonQuery()

В данном примере создается объект cmd, с помощью которого выполняется запрос на обновление методомExecuteNonQuery(). В переменнуюresultзаносится результат выполнения запроса. Если полученное значение больше 0, то в текстовое полеTextBoxResultна форме приложения выводится сообщение о количестве измененных строк, в противном случае – сообщение, что изменений не было.

Заметим, что текст запроса, заносимый в свойство CommandTextобъекта классаSqlCommand, полностью формируется на этапе разработки приложения. Поэтому в данном случае получаетсястатический запрос.

Рассмотрим пример, демонстрирующий использование метода ExecuteScalar() классаSqlCommand.

Пусть требуется вывести количество товаров из таблицы Товары.

Для этого создадим приложение, выполнив следующие действия.

1. Создайте новый проект с именем SampleCommandStatic2.

2. Измените свойство формы Text, задав значение "Статический запрос2". Перетащите с вкладкиDataпанели инструментов объектButton, задав имяbtnShowи свойствоText = "Выполнить", объектLabel, задав ему имяlbl и свойствоText = "Количество товаров", а также объектTextBox, задав ему имяTextBoxResult.

3. Добавьте пространство имен:

using System.Data.SqlClient;

4. Добавьте обработчик для кнопки.

SqlConnection con = new SqlConnection(

"Data Source=srv\\sql_srv;"+

"Initial Catalog=Supply;"+

"User ID=sa;"+

"Password=sa;" );

SqlCommand cmd = con.CreateCommand();

cmd.CommandText = "SELECT COUNT (*) FROM Товары";

con.Open();

TextBoxResult.Text=cmd.ExecuteScalar().ToString();

con.Close();

5. Запустите приложение.

После нажатия на кнопку Выполнить, в окне приложения появится результат – количество товаров, полученное с помощью запроса (рисунок 2).

Рисунок 2 – Приложение, использующее метод ExecuteScalar()

В данном примере создается объект cmd, с помощью которого выполняется запрос на выборку методомExecuteScalar(). В текстовое полеTextBoxResultзаносится результат выполнения запроса – количество записей в таблице Товары, которое приводится к строке с помощью методаToString().

В данном случае также получается статический запрос.

Рассмотрим еще один пример создания статического запроса, демонстрирующий использование метода ExecuteReader().

Пусть требуется получить список всех товаров, имеющихся в таблице Товары. Для этого создадим приложение, выполнив следующие действия.

1. Создайте новый проект с именем SampleCommandStatic3.

2. Измените свойство формы Text, задав значение "Статический запрос3". Перетащите с вкладкиDataпанели инструментов объектButton, задав имяbtnShowи свойствоText = "Выполнить", а также объектListBox, задав ему имяListOfGoods.

3. Добавьте пространство имен:

using System.Data.SqlClient;

4. Добавьте обработчик для кнопки.

SqlConnection con = new SqlConnection(

"Data Source=srv\\sql_srv;"+

"Initial Catalog=Supply;"+ "User ID=sa;"+

"Password=sa;");

SqlCommand cmd = con.CreateCommand();

cmd.CommandText = "select * from Товары";

con.Open();

SqlDataReader reader = cmd.ExecuteReader();

ListOfGoods.Items.Add("---------------------------");

while (reader.Read()){

ListOfGoods.Items.Add(reader["Код товара"].ToString()

+ " "

+ reader["Наименование"].ToString());

}

reader.Close();

con.Close();

5. Запустите приложение.

После нажатия на кнопку Выполнить, в окне приложения появится список товаров, полученный с помощью запроса (рисунок 3).

Рисунок 3 – Приложение, использующее метод ExecuteReader()

В данном примере создается объект cmd, с помощью которого выполняется запрос на выборку методомExecuteReader(). Он возвращает результирующий набор данных в специальный объектreader классаSqlDataReader, с помощью которого просматриваются записи.

Для извлечения очередной записи из набора используется метод Read() объектаreader, а для обращения к значению требуемого поля используется конструкция вида:

reader["Наименование"].ToString();

В данном случае обращение к данным в текущей строке результата происходит по имени столбца.

Считанные из набора данные заносятся в список ListOfGoodsна форме приложения с помощью методаAdd(), добавляющего очередной элемент в коллекциюItems списка.

Создание параметрического запроса

Пусть требуется получить информацию о товаре, с заданным наименованием. Для этого хорошо подходит параметрический запрос, который создается на этапе разработки приложения, но при этом в его теле предусматривается параметр, значение которого можно задавать во время выполнения приложения.

Создадим приложение, выполнив следующие действия.

1. Создайте новый проект с именем SampleCommandParam.

2. Измените свойство формы Text, задав значение "Параметрический запрос". Перетащите с вкладкиDataпанели инструментов объектButton, задав имяbtnShowи свойствоText = "Выполнить", объектListBox, задав ему имяListOfGoods, объектLabel, задав ему имяlbl и свойствоText = "Параметр", а также объектTextBoxс именемtxtParam.

3. Добавьте пространство имен:

using System.Data.SqlClient;

4. Добавьте код для кнопки:

SqlConnection con = new SqlConnection(

"Data Source=srv\\sql_srv;"+

"Initial Catalog=Supply;"+

"User ID=sa;"+

"Password=sa;"

);

SqlCommand cmd = con.CreateCommand();

cmd.CommandText = "SELECT * FROM Товар

WHERE Наименование LIKE "

+ " @name ";

cmd.Parameters.Add("@name", SqlDbType.NVarChar, 100);

cmd.Parameters["@name"].Value =

"%" + txtParam.Text.ToString() + "%";

con.Open();

SqlDataReader reader = cmd.ExecuteReader();

ListOfGoods.Items.Add("---------------------------");

while (reader.Read()) {

ListOfGoods.Items.Add(reader["Код товара"].ToString()

+ " " + reader["Наименование"].ToString());

}

reader.Close();

con.Close();

5. Запустите приложение. В поле ввода укажите часть какого-нибудь наименования товара и нажмите кнопку Выполнить. В результате в окне приложения отобразится список товаров, наименование которых содержит строку, введенную пользователем (рисунок 4).

Рисунок 4 – Приложение, использующее параметрический запрос

В данном примере мы заносим в свойство CommandTextобъектаcmdзапрос, в теле которого располагается параметр@name– наименование товара. Для выполнения запроса необходимо добавить параметр@nameв коллекциюParameters. Каждый параметр в коллекции представляет собой объект классаSqlParameter.

Самый простой способ добавить параметры в коллекцию Parametersс помощью методаAddWithValue(), указав его имя и значение.

Добавить параметр в коллекцию Parametersтакже можно с помощью методаAdd(), указав его имя, .NET-тип и размер, к которым нужно привести значение параметра. Указание .NET-типа необходимо в том случае, если значение параметра должно быть приведено к определенному типу. Присвоить значение параметру можно с помощью свойстваValue.

В нашем примере используется метод Add(), который добавляет параметр@nameв коллекциюParameters. Значение параметра мы считываем из объектаtxtParamи присваиваем его параметру с помощью свойстваParameters["@name"].Value.

Поскольку в теле запроса используется параметр, такой запрос называют параметрическим.

Создание динамического запроса

Иногда текст запроса невозможно бывает предусмотреть на этапе разработки приложения. В этом случае текст запроса синтезируется на этапе выполнения приложения. Такие запросы называют динамическими.

Для иллюстрации возможности создания динамического запроса разработаем приложение аналогичное приложению с параметрическим запросом, но дадим возможность пользователю самому задавать любое условие отбора товаров. Для этого выполним следующие действия.

1. Создайте новый проект с именем SampleCommandDynamic.

2. Измените свойство формы Text, задав значение "Динамический запрос". Перетащите с вкладкиDataпанели инструментов объектButton, задав имяbtnShowи свойствоText = "Выполнить", объектListBox, задав ему имяListOfGoods, объектLabel, задав ему имяlbl и свойствоText = "Параметр", а также объектTextBoxс именемtxtParam.

3. Добавьте пространство имен:

using System.Data.SqlClient;

4. Добавьте код для кнопки:

SqlConnection con = new SqlConnection(

"Data Source=srv\\sql_srv;"+

"Initial Catalog=Supply;"+

"User ID=sa;"+

"Password=sa;"

);

SqlCommand cmd = con.CreateCommand();

cmd.CommandText = "SELECT * FROM Товар WHERE " +

txtParam.Text.ToString();

con.Open();

SqlDataReader reader = cmd.ExecuteReader();

ListOfGoods.Items.Add("--------------------------");

ListOfGoods.Items.Add(cmd.CommandText);

ListOfGoods.Items.Add("--------------------------");

while (reader.Read()) {

ListOfGoods.Items.Add(reader["Код товара"].ToString()

+ " " + reader["Наименование"].ToString());

}

reader.Close();

con.Close();

Отличия от приложения с параметрическим запросом выделены жирным шрифтом.

5. Запустите приложение. Введите в поле ввода конструкцию: Цена > 30 и нажмите кнопку Выполнить. В результате будет сформирован и выполнен динамический запрос, и мы увидим список товаров с ценой больше 30 (рисунок 5).

Рисунок 5 – Приложение, использующее динамический запрос

В данном примере мы заносим в свойство CommandTextобъектаcmdзапрос состоящий из двух частей: первая часть запроса фиксированная, а вторая представляет собой текст, введенный пользователем в объектtxtParam. В результате текст запроса синтезируется на этапе выполнения приложения, поэтому получаетсядинамическийзапрос.

Проблема SQL-инъекций

При организации запросов к БД из клиентских приложений может возникнуть проблема так называемых SQL-инъекций (SQL-injection). SQL-инъекцией называют вредоносный SQL-код, внедряемый пользователем системы в основной запрос. SQL-инъекции являются одним из основных способов взлома информационных систем, поэтому программисту нужно хорошо представлять механизм их действия и способы предотвращения их появления. В приложениях на С# основным путем возможного появления SQL-инъекцией являются динамические запросы, в которые пользователь передает некоторые исходные данные, вводимые с клавиатуры.

Рассмотрим пример. Создадим приложение, с помощью которого можно добавить новую запись в таблицу Товары. Для этого выполним следующие действия.

1. Создайте новый проект с именем SampleSQL_injection.

2. Измените свойство формы Text, задав значение "Пример SQL-инъекций".На форме расположим четыре объекта TextBox с именами TextGoodsCode, TextGoodsName, TextGoodsPrice и TextGoodsMadeIn для ввода кода, наименования, цены и производителя товара, соответственно.

Также поместите на форму две кнопки: "Опасно" и "Безопасно".

3. Добавьте пространство имен:

using System.Data.SqlClient;

4. Добавьте следующий код для кнопки "Опасно":

string Query = "INSERT INTO Товары ([Код товара],

Наименование, Цена, Производитель)

VALUES("

+ TextGoodsCode.Text + ", '"

+ TextGoodsName.Text + "', "

+ TextGoodsPrice.Text + ", '"

+ TextGoodsMadeIn.Text + "')";

SqlConnection con = new SqlConnection(

"Data Source=srv\\sql_srv;"+

"Initial Catalog=Supply;"+

"User ID=sa;"+

"Password=sa;");

SqlCommand cmd = con.CreateCommand();

cmd.CommandText = Query;

con.Open();

cmd.ExecuteNonQuery();

con.Close();

При нажатии на кнопку "Опасно" формируется динамический запрос, в который передаются данные, введенные с формы. При вводе данных пользователь имеет возможность ввести в любое поле дополнительные данные, представляющие собой SQL-инъекцию.

5. Для демонстрации такой возможности создайте новую таблицу, которая затем будет подвергнута удалению с помощью SQL-инъекции.

CREATE TABLE TableToDrop

(

Текст varchar(50)

)

6. Запустите приложение. Укажите код, наименование и цену товара, а в поле TextGoodsMadeIn введите следующую строку (рисунок 6):

'); DROP TABLE TableToDrop;--

Рисунок 6 – Приложение, использующее динамический запрос

В результате будет сформирован объект Query, содержащий текст:

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]