Базы данных - это хранилища структурированных данных, с которыми удобно работать. СУБД - это программное приложение, извлекающее, отображающее в удобном виде и модифицирующее структурированные данные. Чаще всего применяются
Важное место в архитектуре ADO.NET занимают
Connection - используется для установки соединения с Command - используется для выполнения команд SQL и DataReader - предоставляет быстрый доступ только для чтения к DataAdapter - этот класс решает две задачи:DataSet ( набор данных ) информацией, извлеченной из DataSet ADO.NET содержит набор специализированных поставщиков для различных источников данных. Каждый поставщик данных имеет специфическую реализацию классов Connection, , DataReader, DataAdapter, оптимизированных для конкретных баз данных. Например, если нужно подключиться к базе данных SQL Server, то используется класс .
Библиотека .NET Framework содержит четыре типа готовых
Для других типов данных разработчики могут либо создавать свои поставщики, либо приобретать их у сторонних организаций. Выбирая класс поставщика, нужно сначала попытаться найти родного поставщика для данного
Технология OLE DB существует уже много лет как часть ADO, поэтому большинство распространенных источников данных ( SQL Server, Oracle, Access, MySQL и т.д.) предусматривают драйверы OLE DB. Наконец, можно использовать поставщик ODBC в сочетании с драйвером ODBC, поскольку это самая старая технология доступа к данным, но производительность такого подключения может быть очень низкой.
Применение специализированных поставщиков может потребовать изменения кода приложения при переходе к другому типу базы данных. Но эти изменения будут незначительными, поскольку поставщики различаются только до уровня набора данных

Классы ADO.NET группируются в нескольких пространствах имен:
System.Data - содержит ключевые System.Data.Common - содержит System.Data.OleDb - включают поставщиков для источников данных OLE DBSystem.Data.SqlClient - включают поставщиков для источников данных SQL ServerSystem.Data.OracleClient - включают поставщиков для источников данных OracleSystem.Data.SqlTypes - содержит структуры, соответствующие родным типам данных SQL Server. Эти классы не являются необходимыми, но в силу их специфичности оптимизируют доступ к данным SQL ServerSystem.Data.Odbc - включают поставщиков для источников данных ODBC. Операционная система Windows имеет драйверы ODBC для всех видов источников данных, которые конфигурируются в панели управления Пуск/Настройка/Панель управления/Администрирование/Источники данных (ODBC)
Этот класс устанавливает соединение с Connection реализует от наследуемого интерфейса IDbConnection.
При установке соединения требуется строка соединения, которая представляет собой список пар имя=значение, разделенных точкой с запятой. За исключением небольших различий,
localhostNorthwind, которая инсталируется по умолчанию с установкой SQL Server 2005Ниже представлены примеры Northwind на текущем компьютере
| Источник данных | |
|---|---|
| SQL Server с использованием |
string connectionString = " |
SQL Server с использованием учетной записи ( id= ) |
string connectionString = " |
Используется поставщик OLE DB для подключения к Sales типа Oracle через драйвер MSDAORA с правами администратора и пустым паролем |
string connectionString = " |
Подключение к Access. Символ "@" требует от компилятора интерпретировать строку буквально |
string connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;" + @" |
Connection или в методе Connection.Open().
<?xml version="1.0"?> <configuration> <connectionStrings> <add name="Northwind" connectionString="Data Source=localhost; Initial Catalog=Northwind; Integrated Security=SSPI" /> </connectionStrings> <system.web> </system.web> </configuration>
В любое время в коде страницы можно извлечь эту
string connectionString = WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
После установки SQL Server 2005 нужно убедиться, что служба SQLServerAgent включена в режим Auto. Это можно сделать через Пуск/Настройка/Панель управления/Администрирование/Службы

Приведем простой пример установки соединения с Northwind.
Создайте командой
Переведите страницу в режим Design и добавьте из вкладки Standard элемент Label с именем lblInfo
В режиме Design Page_Load() в блоке скриптов, который заполните так, чтобы общий код страницы был следующим(рис ) Код страницы TestConnection.aspx<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
try
{
con.Open();
lblInfo.Text = "<b>Версия сервера:</b> " + con.ServerVersion;
lblInfo.Text += "<br /><b>Соединение:</b> " + con.State.ToString();
}
catch(Exception err)
{
lblInfo.Text = "<b>Ошибка чтения базы данных.</b>";
lblInfo.Text += err.Message;
}
finally
{
con.Close();
lblInfo.Text += "<br /><b>Теперь соединение:</b> ";
lblInfo.Text += con.State.ToString();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
</form>
</body>
</html>
Поместите в файл web.config (рис ) Строка соединения в файле web.config<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="Northwind" connectionString =
"Data Source=localhost;
Initial Catalog=Northwind;
user id=sa; password=;" />
</connectionStrings>
<system.web>
<compilation debug="true"/>
</system.web>
</configuration>
Выполните страницу, сделав ее стартовой, результат должен быть примерно следующим
Соединение как объект занимает определенные ресурсы, поэтому открывать соединение нужно как можно позже, и освобождать как можно быстрее. В примере предусмотрен блок на тот случай, что даже при возникновении исключения соединение все равно будет закрыто. Если это не предусмотреть, то в случае необработанного исключения соединение останется открытым до тех пор, пока сборщик мусора не уничтожит объект типа .
Другой подход гарантированного закрытия соединения - использовать блок using. Оператор using декларирует, что мы используем уничтожаемый объект в течение краткого периода времени. Как только выполнение блока using завершится, среда выполнения немедленно освободит соответствующий объект, вызвав метод . Вызов этого метода эквивалентен вызову метода Close() для объекта Connection.
Создайте копию страницы TestConnection.aspx и назначьте ей имя TestConnection1.aspx
Назначьте новую страницу стартовой и заполните ее следующим кодом(рис ) Код страницы TestConnection1.aspx<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
using (con)
{
con.Open();
lblInfo.Text = "<b>Версия сервера:</b> " + con.ServerVersion;
lblInfo.Text += "<br /><b>Соединение:</b> " + con.State.ToString();
}
lblInfo.Text += "<br /><b>Теперь соединение:</b> ";
lblInfo.Text += con.State.ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
</form>
</body>
</html>
Выполните страницу, результат должен быть прежним
При запросе любой страницы, использующей
Пул соединений - это практика хранения приложением постоянного набора открытых соединений, разделяемых между сеансами при использовании одного и того же Open() обслуживается непосредственно из доступного пула без повторного открытия соединения. Когда клиент освобождает соединение методом Close() или , оно не закрывается, а возвращается в пул, чтобы обслужить следующий запрос.
Пулы автоматически организуются самими
Пулы соединений SQL Server и Oracle используют механизм полнотекстового сравнения. Это значит, что любое минимальное изменение в
Пулы соединений можно настраивать, добавляя соответствующие атрибуты в
| Параметр | Описание |
|---|---|
Max |
Максимальное количество соединений, разрешенных для хранения в пуле (по умолчанию 100). Если достигается этот максимальный размер пула, любые последующие попытки соединения становятся в очередь. Если время жизни соединения, установленное параметром Connection., истечет раньше, чем подошла очередь в пуле, возникает исключение. |
|
Минимальное количество соединений, которое должно оставаться в пуле (по умолчанию 0). Это число соединений будет создано в пуле при открытии первого соединения. |
Pooling |
По умолчанию true. При значении false отключается механизм пула |
Connection Lifetime |
Устанавливает время хранения соединения в пуле (в секундах). По умолчанию значение 0 устанавливает неограниченное время жизни |
Некоторые поставщики имеют методы очистки пула соединений. Например, поставщик имеет - очистка пула конкретного соединения и - очистка пулов всех соединений в текущем
Класс позволяет выполнить любой CommandType, CommandText и Connection, либо передать необходимую информацию в аргументах .
Текстом команды может быть SQL-оператор, ) или CommandType, присваиваемого свойству .
| Значение | Описание |
|---|---|
System.Data.CommandType.Text |
Требование выполнить SQL-оператор, указанный в свойстве . Установлено по умолчанию в |
System.Data.CommandType.StoredProcedure |
Требование выполнить |
System.Data.CommandType.TableDirect |
Требование извлечь все записи из таблицы, указанной в свойстве .Не поддерживается поставщиком данных SQL Server |
Расширим наш пример, чтобы применить код создания объекта .
Создайте из копии файла TestConnection1.aspx файл с именем TestConCommand.aspx и установите его стартовым
Дополните код страницы TestConCommand.aspx так(рис ) Страница TestConCommand.aspx создания объектов соединения и команды<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Установили минимальный размер пула соединения равный 10
connectString += "Min Pool Size=10;";
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
using (con)
{
// Получить соединение из пула (если есть)
// или создать пул с 10 соединениями (если нет)
con.Open();
lblInfo.Text = "<b>Соединение:</b> " + con.State.ToString();
// Создали объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand();
// Настроили объект Command
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "SELECT * FROM Employees";
}
// Вернуть соединение в пул
lblInfo.Text += "<br /><b>Соединение:</b> ";
lblInfo.Text += con.State.ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
</form>
</body>
</html>
Исполните страницу, чтобы получить такой результат
Учитывая, что по умолчанию установлено CommandType = CommandType.Text, команду можно сразу указать в перегруженном конструкторе при создании объекта команд. Например, код
// Создали объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand();
// Настроили объект Command
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "SELECT * FROM Employees";
можно заменить кодом
// Создали и настроили объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand("SELECT * FROM Employees", con);
В данном примере мы просто настроили, но не выполнили объект класса , поэтому никакой имеет три метода выполнения команды, приведенные в таблице на примере класса SqlCommand
| Метод | Описание |
|---|---|
System.Data.SqlClient.SqlCommand.ExecuteNonQuery() |
Выполняет команды, отличные от SELECT, такие как SQL-операторы вставки, удаления или обновления записей. Возвращает количество строк, обработанное командой |
System.Data.SqlClient.SqlCommand.ExecuteScalar() |
Выполняет запрос SELECT и возвращает первое поле первой строки результирующего набора, сгенерированного командой. Обычно применяется в запросах с функциями или COUNT() для вычисления единственного значения |
System.Data.SqlClient.SqlCommand.ExecuteReader() |
Выполняет запрос SELECT и возвращает объект DataReader - результирующий набор данных, доступный только для чтения |
Объект класса DataReader представляет собой результирующий набор данных только для чтения и является самым простым и быстрым средством доступа к данным. Он является выборкой данных по SELECT и состоит из строк и колонок, которые можно анализировать и отображать. Но в нем нет возможностей сортировки, как в более развитых объектах. Ниже приведены несколько методов DataReader на примере класса System.Data.SqlClient.SqlDataReader
| Метод | Описание |
|---|---|
SqlDataReader.Read() |
Перемещает курсор строки на следующую строку в потоке. Его нужно также вызывать первым после создания объекта DataReader, поскольку начальный курсор позиционируется перед первой строкой. Возвращает false при чтении последней строки набора данных |
SqlDataReader.GetValue(int) |
Читает значение поля текущей строки по указанному индексу (индекс начинается с нуля) |
SqlDataReader.GetValues(object[ ]) |
Сохраняет значение текущей строки в массиве. Количество сохраняемых полей зависит от размера массива, переданного этому методу. Чтобы сохранить все поля текущей строки, нужно создать массив SqlDataReader. |
SqlDataReader.GetInt32(int) |
Возвращает значение поля |
SqlDataReader.GetDateTime(int) |
Возвращает значение поля |
SqlDataReader.NextResult() |
Если команда, которая сгенерировала DataReader, возвратила более одного результирующего набора строк, то этот метод перемещает текущий курсор данных на первую строку следующего набора |
SqlDataReader.Close() |
Закрывает модуль чтения |
Приведем пример, в котором создается и выполняется простая команда запроса для выборки всех записей из таблицы Employees учебной базы данных Northwind. Мы помним, что
Создайте копию страницы TestConCommand.aspx с именем TestDataReader.aspx
Заполните страницу приведенным кодом(рис ) Код страницы TestDataReader.aspx с чтением из базы Northwind<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
// Создали объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand();
// Настроили объект Command
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "SELECT * FROM Employees";
// Создаем ссылку на вспомогательную строку
System.Text.StringBuilder htmlStr = new StringBuilder("");
using (con)
{
// Открыли соединение
con.Open();
// Выполняем команду и получаем результирующий набор данных
System.Data.SqlClient.SqlDataReader reader =
cmd.ExecuteReader();
// Перебираем все записи результирующего набора
// и строим HTML-строку
while (reader.Read())
{
htmlStr.Append("<li>Служащий ");
htmlStr.Append(reader["TitleOfCourtesy"]);
htmlStr.Append(" <b>");
htmlStr.Append(reader.GetString(1));
htmlStr.Append("</b>, ");
htmlStr.Append(reader.GetString(2));
htmlStr.Append(" - работает с ");
htmlStr.Append(reader.GetDateTime(6).ToString("d"));
htmlStr.Append("</li>");
}
// Закрываем набор данных
reader.Close();
}
// Выдаем результаты запроса
lblInfo.Text = htmlStr.ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
</form>
</body>
</html>
Исполните страницу TestDataReader.aspx, должен получиться такой клиентский результатВ этом примере объект класса StringBuilder существенно увеличивает производительность за счет выделения буфера памяти для символов. Если использовать объект класса String, то при каждой операции
В большинстве случаев нам такой способ выборки информации из базы в будущем не понадобится, поскольку существуют библиотечные элементы управления для
Команда SqlCommand.ExecuteReader() может возвращать множественный результирующий набор данных в двух случаях:
SELECTВот пример строки SELECT:
string sql = "SELECT TOP 5 * FROM Employees;" + "SELECT TOP 5 * FROM Customers;" + "SELECT TOP 5 * FROM Suppliers";
Приведенная строка содержит три запроса, каждый из которых возвращает в результирующий набор данных по пять первых записей из трех таблиц базы Northwind. Ниже приведен код страницы, в котором выполняется последовательная NextResult(), который возвратит false, когда больше не останется результирующих наборов.
Создайте копию страницы TestDataReader.aspx с именем TestMultiDataReader.aspx и определите ее стартовой
Заполните страницу приведенным кодом(рис ) Код страницы TestMultiDataReader.aspx для нескольких таблиц базы Northwind<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
// Создали объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand();
// Настроили объект Command
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "SELECT TOP 5 * FROM Employees;"
+ "SELECT TOP 5 * FROM Customers;"
+ "SELECT TOP 5 * FROM Suppliers";
System.Text.StringBuilder htmlStr = new StringBuilder("");
int i = 0;
using (con)
{
// Открыли соединение
con.Open();
// Выполняем команду и получаем результирующий набор данных
System.Data.SqlClient.SqlDataReader reader =
cmd.ExecuteReader();
// Перебираем частные наборы в результирующем наборе данных
do
{
htmlStr.Append("<h2>Набор: ");// Заголовок h2 HTML
htmlStr.Append(i.ToString());
htmlStr.Append("</h2>");
htmlStr.Append("<ul>"); // Начало маркированного списка
// Перебираем все записи текущего набора
// (их должно по команде быть 5 первых)
// в результирующем наборе и строим HTML-строку
while (reader.Read())
{
htmlStr.Append("<li>");// Строка списка HTML
// Перебираем все поля строки набора
for (int field = 0; field < reader.FieldCount; field++)
{
if (field >= 3) // Ограничимся тремя первыми полями
continue;
htmlStr.Append("<b>");
htmlStr.Append(reader.GetName(field).ToString());// Имя поля
htmlStr.Append(": </b>");
htmlStr.Append(reader.GetValue(field).ToString());// Значение поля
htmlStr.Append("nbsp;nbsp;nbsp;nbsp;nbsp;");// Жесткие пробелы HTML
}
htmlStr.Append("</li>");
}
htmlStr.Append("</ul>"); // Конец маркированного списка
i++;
} while (reader.NextResult());
// Закрываем набор данных
reader.Close();
}
// Показываем пользователю результаты запроса
lblInfo.Text = htmlStr.ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
</form>
</body>
</html>
Исполните страницу, должен получиться такой результат
Мы искусственно ограничили количество отображаемых полей тремя. Если этот оператор убрать, то на пользователя обрушится
Если нам наплевать на пользователя и мы хотим показать ему . В этом случае необходимую страницу можно получить так:
Создайте копию страницы TestMultiDataReader.aspx с именем GridViewDataReader.aspx и определите ее стартовой
Поместите на страницу элемент управления GridView из вкладки Data панели Toolbox
Заполните страницу приведенным кодом(рис ) Код страницы GridViewDataReader.aspx с неформатированным просмотром <%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
// Создали объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand();
// Настроили объект Command
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "SELECT TOP 5 * FROM Suppliers";
using (con)
{
// Открыли соединение
con.Open();
// Выполняем команду и получаем результирующий набор данных
System.Data.SqlClient.SqlDataReader reader =
cmd.ExecuteReader();
// Подключаем элемент управления GridView
// к полученному результирующему набору
GridView1.DataSource = reader;
// Наполняем элемент управления GridView
// всеми извлеченными записями DataReader
GridView1.DataBind();
// Закрываем набор данных
reader.Close();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="GridView1" runat="server">
</asp:GridView>
</div>
</form>
</body>
</html>
Исполните страницу, должен получиться такой результат| SupplierID | CompanyName | ContactName | ContactTitle | Address | City | PostalCode | Country | HomePage | |||
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | Exotic Liquids | Charlotte Cooper | Purchasing Manager | 49 Gilbert |
London | EC1 4SD | UK | (171) 555-2222 | |||
| 2 | New Orleans Cajun |
Shelley Burke | P.O. |
New Orleans | LA | 70117 | USA | (100) 555-4822 | #CAJUN. |
||
| 3 | Grandma Kelly's Homestead | Regina Murphy | Sales Representative | 707 Oxford Rd. | MI | 48104 | USA | (313) 555-5735 | (313) 555-3349 | ||
| 4 | Tokyo Traders | Yoshi Nagase | 9-8 Sekimai Musashino-shi | Tokyo | 100 | Japan | (03) 3555-5011 | ||||
| 5 | Cooperativa de Quesos 'Las Cabras' | Antonio |
Calle |
Oviedo | Asturias | 33007 | Spain | (98) 598 76 54 |
Этот метод объекта возвращает Object, которая является результатом работы COUNT() или
Создайте копию страницы TestConCommand.aspx с именем TestExecuteScalar.aspx и сделайте ее стартовой
Скорректируйте страницу TestExecuteScalar.aspx так(рис ) Страница TestExecuteScalar.aspx<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
// Создали объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand();
// Настроили объект Command
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "SELECT COUNT(*) FROM Employees";
using (con)
{
// Открыли соединение
con.Open();
// Выполнили команду подсчета записей таблицы Employees
// с применением явного преобразования типа
int recordCount = (int)cmd.ExecuteScalar();
lblInfo.Text = "<b>Количество записей:</b> " + recordCount.ToString();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
</form>
</body>
</html>
Постройте страницу, результат будет таким
Этот метод напоминает консольную утилиту, которая работает в пакетном режиме, выполняет какие-либо действия, но не возвращает результатов. Метод ExecuteNonQuery() выполняет такие SQL-команды, как , DELETE, UPDATE, а возвращает только число, равное количеству обработанных записей.
В качестве примера приведем код страницы, удаляющий запись с определенным значением поля EmployeeID в таблице Employees Northwind. Поскольку эта база учебная, не будем ее портить и зададим заведомо несуществующее значение поля EmployeeID.
<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Формируем соединение
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
// Формируем команду в конструкторе класса
int empID = 111; // Заведомо несуществующее значение
string sql = "DELETE FROM Employees WHERE EmployeeID = " + empID.ToString();
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand(sql, con);
try
{
con.Open();
int recCount = cmd.ExecuteNonQuery();
lblInfo.Text = string.Format("<b>Удалено записей:</b> {0}", recCount);
}
catch(System.Data.SqlClient.SqlException err)
{
lblInfo.Text = string.Format("<b>Ошибка удаления:</b> {0}", err.Message);
}
finally
{
con.Close();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
</form>
</body>
</html>
Очень часто возникает необходимость дать пользователю возможность самому сформировать строку
Рассмотрим это на примере.
Сделайте копию с именем UserSqlGridData.aspx из файла GridViewDataReader.aspx и назначьте ее стартовой
Разместите выше элемента
Откорректируйте дескрипторный и встроенный коды страницы так(рис ) Код страницы UserSqlGridData.aspx<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
// Создали строку SQL-запроса
string sql =
"SELECT Orders.CustomerID, Orders.OrderID, COUNT(UnitPrice) AS Items, "
+ "SUM(UnitPrice * Quantity) AS Total FROM Orders "
+ "INNER JOIN[Order Details] "
+ "ON Orders.OrderID = [Order Details].OrderID "
+ "WHERE Orders.CustomerID = '" + TextBox1.Text + "' "
+ "GROUP BY Orders.OrderID, Orders.CustomerID";
// Создали объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand(sql, con);
using (con)
{
// Открыли соединение
con.Open();
// Выполняем команду и получаем результирующий набор данных
System.Data.SqlClient.SqlDataReader reader =
cmd.ExecuteReader();
// Подключаем элемент управления GridView
// к полученному результирующему набору
GridView1.DataSource = reader;
// Наполняем элемент управления GridView
// всеми извлеченными записями DataReader
GridView1.DataBind();
// Закрываем набор данных
reader.Close();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Введите ID пользователя:<br />
<asp:TextBox ID="TextBox1" runat="server" Width="248px" Font-Bold="True">ALFKI</asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Получить" /><br />
<br />
<asp:GridView ID="GridView1" runat="server">
</asp:GridView>
</div>
</form>
</body>
</html>
Исполните страницу и должен получиться такой результат
| CustomerID | OrderID | Items | |
|---|---|---|---|
| ALFKI | 10643 | 3 | 1086,0000 |
| ALFKI | 10692 | 1 | 878,0000 |
| ALFKI | 10702 | 2 | 330,0000 |
| ALFKI | 10835 | 2 | 851,0000 |
| ALFKI | 10952 | 2 | 491,2000 |
| ALFKI | 11011 | 2 | 960,0000 |
При значении текстового поля ALFKI вычисленный SQL-оператор будет иметь такой вид:
string sql =
"SELECT Orders.CustomerID, Orders.OrderID, COUNT(UnitPrice) AS Items, "
+ "SUM(UnitPrice * Quantity) AS Total FROM Orders "
+ "INNER JOIN[Order Details] "
+ "ON Orders.OrderID = [Order Details].OrderID "
+ "WHERE Orders.CustomerID = 'ALFKI' "
+ "GROUP BY Orders.OrderID, Orders.CustomerID";
Злоумышленник может попытаться подделать
Предположим, что злоумышленник введет в текстовое поле следующий текст
ALFKI' OR '1' = '1
В этом случае
string sql =
"SELECT Orders.CustomerID, Orders.OrderID, COUNT(UnitPrice) AS Items, "
+ "SUM(UnitPrice * Quantity) AS Total FROM Orders "
+ "INNER JOIN[Order Details] "
+ "ON Orders.OrderID = [Order Details].OrderID "
+ "WHERE Orders.CustomerID = 'ALFKI' OR '1' = '1' "
+ "GROUP BY Orders.OrderID, Orders.CustomerID";
Этот оператор вернет все записи о заказах, поскольку условие 1=1 истинно для всех строк
Исполните страницу UserSqlGridData.aspx и введите как злоумышленник в текстовое поле строкуALFKI' OR '1' = '1
Должен получиться результат, малая часть которого приведена ниже

| CustomerID | OrderID | Items | |
|---|---|---|---|
| VINET | 10248 | 3 | 440,0000 |
| TOMSP | 10249 | 2 | 1863,4000 |
| HANAR | 10250 | 3 | 1813,0000 |
| VICTE | 10251 | 3 | 670,8000 |
| SUPRD | 10252 | 3 | 3730,0000 |
| HANAR | 10253 | 3 | 1444,8000 |
| CHOPS | 10254 | 3 | 625,2000 |
| RICSU | 10255 | 4 | 2490,5000 |
| WELLI | 10256 | 2 | 517,8000 |
| HILAA | 10257 | 3 | 1119,9000 |
| ERNSH | 10258 | 3 | 2018,6000 |
| CENTC | 10259 | 2 | 100,8000 |
В результате злоумышленнику передается вся информация о сделанных заказах. Если информация содержит номера кредитных карточек, карточек социального страхования или дату рождения заказчиков, то репутация такого сайта будет с треском провалена и никто пользоваться им не будет.
Возможны и более сложные атаки. Например, злонамеренный пользователь может просто закомментировать
Злоумышленник может применить пакетную команду, чтобы выполнить произвольный оператор SQL. При использовании поставщика данных SQL Server достаточно просто добавить точку с запятой, за которой передать дополнительную команду. Таким образом можно, например, удалить содержимое другой таблицы, или даже вызвать системную xp_cmdshell, чтобы запустить произвольную программу из командной строки.
Вот что злоумышленник может ввести в текстовое поле, чтобы осуществить более изощренную атаку внедрением SQL, удалив все строки из таблицы :
ALFKI'; DELETE * FROM Customers --
Не делайте этого с нашей учебной базой!!!
Для того, чтобы противостоять атаке внедрением, следует помнить несколько правил:
TextBox.MaxLength, чтобы предотвратить длинный ввод, когда в этом нет необходимостиException.Messagestring ID = TextBox1.Text.Replace("'", "''");
определенные параметры-заполнители SQL через коллекцию . Параметры-заполнители прописываются отдельно и автоматически кодируются.
Синтаксис
Приведем пример, использующий параметризованную команду, в котором исключена возможность атаки внедрением SQL.
Сделайте из страницы UserSqlGridData.aspx копию с именем UserSqlParamCommand.aspx и назначьте ее стартовой
Откорректируйте страницу UserSqlParamCommand.aspx, чтобы она имела следующий код(рис ) Код страницы UserSqlParamCommand.aspx<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
// Создали строку SQL-запроса
string sql =
"SELECT Orders.CustomerID, Orders.OrderID, COUNT(UnitPrice) AS Items, "
+ "SUM(UnitPrice * Quantity) AS Total FROM Orders "
+ "INNER JOIN[Order Details] "
+ "ON Orders.OrderID = [Order Details].OrderID "
+ "WHERE Orders.CustomerID = @CustomID "
+ "GROUP BY Orders.OrderID, Orders.CustomerID";
// Создали объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand(sql, con);
// Дополнили коллекцию Parameters
cmd.Parameters.Add("@CustomID", TextBox1.Text);
using (con)
{
// Открыли соединение
con.Open();
// Выполняем команду и получаем результирующий набор данных
System.Data.SqlClient.SqlDataReader reader =
cmd.ExecuteReader();
// Подключаем элемент управления GridView
// к полученному результирующему набору
GridView1.DataSource = reader;
// Наполняем элемент управления GridView
// всеми извлеченными записями DataReader
GridView1.DataBind();
// Закрываем набор данных
reader.Close();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Введите ID пользователя:<br />
<asp:TextBox ID="TextBox1" runat="server" Width="248px" Font-Bold="True">ALFKI</asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Получить" /><br />
<br />
<asp:GridView ID="GridView1" runat="server">
</asp:GridView>
</div>
</form>
</body>
</html>
Код страницы остался почти таким же, за исключением двух строк, вводящих команду с именованным параметром @CustomID.
Попробуйте исполнить атаку внедрением с содержимым ALFKI' OR '1' = '1Мы видим, что страница не возвращает никаких данных, поскольку ни одна запись таблицы в поле CustomerID не имеет значение, равное введенной строке ALFKI' OR '1' = '1. Таким простым способом мы
Рассмотрим пример SQL-кода, необходимого при Employees. Этой Northwind.
CREATE PROCEDURE InsertEmployee @TitleOfCourtesy varchar(25), @LastName varchar(20), @FirstName varchar(10), @EmployeeID int OUTPUT AS INSERT INTO Employees (TitleOfCourtesy, LastName, FirstName, HireDate) VALUES(@TitleOfCourtesy, @LastName, @FirstName, GETDATE()); SET @EmployeeID = @@IDENTITY
Эту Nordhwind. Но я не смог это сделать, поскольку оболочка не видит эту базу из-за неправильно установленного пакета SQL Server 2005 (или потому, что у меня руки кривые). Чтобы как-то вывернуться, в приведенном ниже коде мы добавим ее программно.
Создайте новую страницу с именем TestStoredProcedure.aspx и назначьте ее стартовой
Поместите на страницу из вкладки Standard элементы управления согласно дескрипторному коду, приведенному ниже (устал писать, да и студент Зиборов ругается, но зато все работает!)
Настройте код страницы, чтобы в итоге он был таким<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
try
{
string sql =
"CREATE PROCEDURE InsertEmployee "
+ "@TitleOfCourtesy varchar(25),"
+ "@LastName varchar(20),"
+ "@FirstName varchar(10),"
+ "@EmployeeID int OUTPUT "
+ "AS "
+ "INSERT INTO Employees "
+ "(TitleOfCourtesy, LastName, FirstName, HireDate) "
+ "VALUES(@TitleOfCourtesy, @LastName, @FirstName, GETDATE());"
+ "SET @EmployeeID = @@IDENTITY";
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand(sql, con);
con.Open();
cmd.ExecuteNonQuery();
}
catch (System.Data.SqlClient.SqlException error)
{
}
finally
{
con.Close();
}
}
protected void AddEmployee_Click(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
// Создать и настроить объект Command
// для вызова хранимой процедуры InsertEmployee
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand("InsertEmployee", con);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
// Добавить входные параметры хранимой процедуры
// в коллекцию параметров Command.Parameters
// Добавляем первый входной параметр
cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter(
"@TitleOfCourtesy", System.Data.SqlDbType.NVarChar, 25));
cmd.Parameters["@TitleOfCourtesy"].Value = title.Text;
// Добавляем второй входной параметр
cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter(
"@LastName", System.Data.SqlDbType.NVarChar, 20));
cmd.Parameters["@LastName"].Value = lastName.Text;
// Добавляем третий входной параметр
cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter(
"@FirstName", System.Data.SqlDbType.NVarChar, 10));
cmd.Parameters["@FirstName"].Value = firstName.Text;
// Добавить выходной параметр хранимой процедуры
// в коллекцию параметров Command.Parameters
cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter(
"@EmployeeID", System.Data.SqlDbType.Int, 4));
cmd.Parameters["@EmployeeID"].Direction = System.Data.ParameterDirection.Output;
using (con)
{
// Открыли соединение
con.Open();
// Выполняем команду
int recCount = cmd.ExecuteNonQuery();
lblInfo.Text = string.Format("<b>Вставлено записей:</b> {0}<br />", recCount);
// Получить вновь сгенерированный идентификатор
int empID = (int)cmd.Parameters["@EmployeeID"].Value;
lblInfo.Text += "Новый идентификатор: " + empID.ToString();
}
// Соединение закрылось автоматически
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
Введите титул:<asp:TextBox ID="title" runat="server">студент</asp:TextBox><br />
Введите имя:<asp:TextBox ID="firstName" runat="server">Иван</asp:TextBox><br />
Введите фамилию:<asp:TextBox ID="lastName" runat="server">Петров</asp:TextBox><br />
<asp:Label ID="lblInfo" runat="server" />
<br />
<br />
<asp:Button ID="AddEmployee" runat="server" Text="Добавить служащего"
OnClick="AddEmployee_Click" />
<asp:LinkButton ID="btnResult" runat="server"
PostBackUrl="~/TestDataReader.aspx">Показать список</asp:LinkButton>
</form>
</body>
</html>
Интерфейс страницы будет таким

Добавьте служащего щелчком по кнопке, после чего выведите список. Результат будет примерно такимВ обработчике Page_Load() мы намеренно применили пустой блок
Транзакция, это набор операций SQL, которые либо должны быть успешно выполнены все, либо должны быть все отменены, если хоть одна операция дала сбой. Такой механизм гарантирует, что данные всегда находятся в корректном состоянии.
Connection.BeginTransaction(). Принимается транзакция методом , а отменяется, если возникло исключение, методом .
Приведем пример, в котором в таблицу Employees Nordhwind добавляется две записи через механизм контроля транзакций. Эти записи можно было бы добавить и обычным способом, но в критических случаях делать это нужно под контролем транзакций.
Сделайте из страницы TestStoredProcedure.aspx копию с именем TestTransaction.aspx и определите ее стартовой
Заполните страницу следующим кодом(рис ) Код страницы TestTransaction.aspx<%@ Page Language="C#" %>
<script runat="server">
// Объявляем переменные-ссылки как члены класса страницы
string sql1 = "INSERT INTO Employees (LastName, FirstName, HireDate) "
+ "VALUES ('Зиборов', 'Алексей', GETDATE())";
string sql2 = "INSERT INTO Employees (LastName, FirstName, HireDate) "
+ "VALUES ('Погодаева', 'Татьяна', GETDATE())";
System.Data.SqlClient.SqlConnection con;
System.Data.SqlClient.SqlCommand cmd1, cmd2;
System.Data.SqlClient.SqlTransaction tran = null;
/**** Для отладки ****/
string sql3 = "DELETE FROM Employees WHERE EmployeeID > 9";
System.Data.SqlClient.SqlCommand cmd3;
/*********************/
protected void Page_Load(object sender, EventArgs e)
{
// Извлечь строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создать объект соединения
con = new System.Data.SqlClient.SqlConnection(connectString);
// Создать объекты команд
cmd1 = new System.Data.SqlClient.SqlCommand(sql1, con);
cmd2 = new System.Data.SqlClient.SqlCommand(sql2, con);
/**** Для отладки ****/
cmd3 = new System.Data.SqlClient.SqlCommand(sql3, con);
/*********************/
}
protected void AddEmployee_Click(object sender, EventArgs e)
{
try // Попытка
{
// Открыть соединение
con.Open();
/**** Для отладки ****/
cmd3.ExecuteNonQuery(); // Удалить последние записи
/*********************/
// Начать транзакцию
tran = con.BeginTransaction();
// Пометить команды включенными в транзакцию
cmd1.Transaction = tran;
cmd2.Transaction = tran;
// Выполнить обе команды
// Должны выполняться только команды транзакции!!!
cmd1.ExecuteNonQuery();
cmd2.ExecuteNonQuery();
// Искусственная генерация исключения
// для тестирования отмены транзакции
//throw new ApplicationException();
// Подтвердить и закончить транзакцию
tran.Commit();
// Посчитать количество записей таблицы
// Теперь можно выполнять другие команды
cmd1.CommandText = "SELECT COUNT(*) FROM Employees";
string recCount = cmd1.ExecuteScalar().ToString();
// Выдать результат
lblInfo.Text = "Добавлены две записи!<br />";
lblInfo.Text += "Общее число записей " + recCount;
}
catch // Откат при любом исключении
{
lblInfo.Text = "Транзакция отменена!";
tran.Rollback();
}
finally // Обязательное завершение
{
// Закрыть соединение по-любому
con.Close();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<asp:Label ID="lblInfo" runat="server" />
<br />
<br />
<asp:Button ID="AddEmployee" runat="server"
Text="Выполнить транзакцию"
OnClick="AddEmployee_Click" />
<asp:LinkButton ID="btnResult" runat="server"
PostBackUrl="~/TestDataReader.aspx">
Показать список</asp:LinkButton>
</form>
</body>
</html>
Исполните страницу и получите следующий отклик при выполнении транзакции
Просмотрите таблицу Employees Nordhwind по Обратите внимание, что пока транзакция находится в процессе работы, должны выполняться только команды транзакции. Область работы транзакции определяется ;
Чтобы протестировать свойство tran. строку генерации исключения
throw new ApplicationException();
Иногда может возникнуть необходимость
Немного изменим предыдущий пример, введя возможность частичного
Скопируйте страницу TestTransaction.aspx в TestTransactionSave.aspx и назначьте ее стартовой
Измените код новой страницы, чтобы он был таким(рис ) Код страницы TestTransactionSave.aspx частичного отката транзакции<%@ Page Language="C#" %>
<script runat="server">
// Объявляем переменные-ссылки как члены класса страницы
string sql1 = "INSERT INTO Employees (LastName, FirstName, HireDate) "
+ "VALUES ('Зиборов', 'Алексей', GETDATE())";
string sql2 = "INSERT INTO Employees (LastName, FirstName, HireDate) "
+ "VALUES ('Погодаева', 'Татьяна', GETDATE())";
System.Data.SqlClient.SqlConnection con;
System.Data.SqlClient.SqlCommand cmd1, cmd2;
System.Data.SqlClient.SqlTransaction tran = null;
/**** Для отладки ****/
string sql3 = "DELETE FROM Employees WHERE EmployeeID > 9";
System.Data.SqlClient.SqlCommand cmd3;
/*********************/
protected void Page_Load(object sender, EventArgs e)
{
// Извлечь строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создать объект соединения
con = new System.Data.SqlClient.SqlConnection(connectString);
// Создать объекты команд
cmd1 = new System.Data.SqlClient.SqlCommand(sql1, con);
cmd2 = new System.Data.SqlClient.SqlCommand(sql2, con);
/**** Для отладки ****/
cmd3 = new System.Data.SqlClient.SqlCommand(sql3, con);
/*********************/
}
protected void AddEmployee_Click(object sender, EventArgs e)
{
try // Попытка
{
// Открыть соединение
con.Open();
/**** Для отладки ****/
cmd3.ExecuteNonQuery(); // Удалить последние записи
/*********************/
// Начать транзакцию
tran = con.BeginTransaction();
// Пометить команды включенными в транзакцию
cmd1.Transaction = tran;
cmd2.Transaction = tran;
// Выполнить первую команду транзакции
cmd1.ExecuteNonQuery();
// Пометить точку отката
tran.Save("Ziborov");
// Выполнить вторую команду транзакции
cmd2.ExecuteNonQuery();
// Выбросить исключение
throw new ApplicationException();
}
catch // Откат
{
// Откатить частично
tran.Rollback("Ziborov");
// Подтвердить и закончить транзакцию
tran.Commit();
lblInfo.Text = "Транзакция частично отменена!";
// Посчитать количество записей таблицы
cmd1.CommandText = "SELECT COUNT(*) FROM Employees";
string recCount = cmd1.ExecuteScalar().ToString();
// Выдать результат
lblInfo.Text = "Добавлена одна запись!<br />";
lblInfo.Text += "Общее число записей " + recCount;
}
finally // Обязательное завершение
{
// Закрыть соединение по-любому
con.Close();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<asp:Label ID="lblInfo" runat="server" />
<br />
<br />
<asp:Button ID="AddEmployee" runat="server"
Text="Выполнить транзакцию"
OnClick="AddEmployee_Click" />
<asp:LinkButton ID="btnResult" runat="server"
PostBackUrl="~/TestDataReader.aspx">
Показать список</asp:LinkButton>
</form>
</body>
</html>
Выполните страницу и убедитесь, что Классы поставщиков оптимизированы под конкретный тип
Основная идея модели фабрик заключается в том, что можно использовать единственный объект-фабрику для создания всех прочих необходимых для конкретного поставщика объектов. Но это можно выполнить и созданием конкретных объектов поставщика. Самое главное удобство заключается в том, что конкретную фабрику поставщика можно создавать из обобщенного класса фабрик System.Data.
<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="Northwind" connectionString="Data Source=localhost;
Initial Catalog=Northwind; user id=sa; password=;"/>
</connectionStrings>
<system.data>
<DbProviderFactories>
<add name="SqlClient Data Provider" invariant="System.Data.SqlClient"
type="System.Data.SqlClient.SqlClientFactory" />
<add name="Odbc Data Provider" invariant="System.Data.Odbc"
type="System.Data.Odbc.OdbcFactory" />
<add name="OleDb Data Provider" invariant="System.Data.OleDb"
type="System.Data.OleDb.OleDbFactory" />
<add name="OracleClient Data Provider" invariant="System.Data.OracleClient"
type="System.Data.OracleClient.OracleClientFactory" />
</DbProviderFactories>
</system.data>
<system.web>
<compilation debug="true"/>
</system.web>
</configuration>
Этот шаг регистрации идентифицирует коллекцию фабрик с идентификатором name, значение которого удобно принять подобным имени поставщика. Конкретную фабрику можно прочитать из кода по ее идентификатору.
Обобщенный класс System.Data. имеет GetFactory(string), который возвращает объект конкретной фабрики, указанной в строке аргумента. Например, для случая явного указания в коде конкретной фабрики это будет выглядеть так
string factory = "System.Data.SqlClient";
System.Data.Common.DbProviderFactory provider =
System.Data.Common.DbProviderFactories.GetFactory(factory);
Получив конкретную фабрику из строки аргумента или provider одноименной фабрики, устанавливая соединение с Connection и используются методы объекта (например, provider ) фабрики CreateXXX(), которые возвращают эти объекты

Для создания независимого от поставщика универсального кода мы должны исходить из того, что не знаем заранее тип поставщика. Поэтому взаимодействовать с Connection, , , DataAdapter DbConnection, DbCommand, DbParameter, DbDataAdapter.
Чтобы лучше понять, как все вышесказанное работает, рассмотрим пример
Откорректируйте файл web.config, чтобы он окончательно выглядел следующим образом(рис ) Дополнение файла web.config для применения обобщенного кода<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="Northwind" connectionString="Data Source=localhost;
Initial Catalog=Northwind; user id=sa; password=;"/>
</connectionStrings>
<appSettings>
<add key="factory" value="System.Data.SqlClient" />
<add key="employeeQuery" value="SELECT * FROM Employees" />
</appSettings>
<system.web>
<compilation debug="true"/>
</system.web>
</configuration>
Создайте из копии TestDataReader.aspx новую страницу с именем TestFactory.aspx, назначьте ее стартовой и заполните следующим кодом(рис ) Страница TestFactory.aspx для реализации независимого от поставщика кода<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Получить по ключу фабрику из файла web.config
string factory = System.Web.Configuration.
WebConfigurationManager.AppSettings["factory"];
System.Data.Common.DbProviderFactory provider =
System.Data.Common.DbProviderFactories.GetFactory(factory);
// Использовать фабрику для получения объекта соединения
// и извлечь строку соединения из файла web.config
System.Data.Common.DbConnection con =
provider.CreateConnection();
con.ConnectionString = System.Web.Configuration.
WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
// Создать базовый объект DbCommand. Для его настройки
// и извлечь по ключу команду из файла web.config
System.Data.Common.DbCommand cmd = provider.CreateCommand();
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = System.Web.Configuration.
WebConfigurationManager.AppSettings["employeeQuery"];
// Создаем ссылку на вспомогательную строку
System.Text.StringBuilder htmlStr = new StringBuilder("");
using (con)
{
// Открыли соединение
con.Open();
// Выполняем обобщенную команду и получаем результирующий набор данных
System.Data.Common.DbDataReader reader =
cmd.ExecuteReader();
// Перебираем все записи результирующего набора
// и строим HTML-строку
while (reader.Read())
{
htmlStr.Append("<li>Служащий ");
htmlStr.Append(reader["TitleOfCourtesy"]);
htmlStr.Append(" <b>");
htmlStr.Append(reader.GetString(1));
htmlStr.Append("</b>, ");
htmlStr.Append(reader.GetString(2));
htmlStr.Append(" - работает с ");
htmlStr.Append(reader.GetDateTime(6).ToString("d"));
htmlStr.Append("</li>");
}
// Закрываем набор данных
reader.Close();
}
// Выдаем результаты запроса
lblInfo.Text = htmlStr.ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
</form>
</body>
</html>
Выполните страницу TestFactory.aspx. Должен получиться такой же результат, что и на странице TestDataReader.aspx, а именноЧтобы убедиться, что код получился действительно универсальным и настройка на конкретного поставщика осуществляется только в файле web.config, поменяем поставщика в этом файле без изменения кода самой страницы.
Откорректируйте файл web.config, чтобы он окончательно выглядел следующим образом(рис ) Изменение поставщика в файле web.config для проверки обобщенного кода<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="Northwind" connectionString="Provider=SQLOLEDB; Data Source=localhost;
Initial Catalog=Northwind; user id=sa; password=;"/>
</connectionStrings>
<appSettings>
<add key="factory" value="System.Data.OleDb" />
<add key="employeeQuery" value="SELECT * FROM Employees" />
</appSettings>
<system.web>
<compilation debug="true"/>
</system.web>
</configuration>
Исполните страницу TestFactory.aspx для нового поставщика, указанного в файле web.config, и убедитесь, что прежний код страницы генерирует тот же самый результат, но уже с новым поставщикомМеханизм применения фабрик поставщиков не решает всех проблем, связанных с разработкой независимого от поставщика кода. Например, не существует обобщенных объектов исключений базы данных, а только специфичные для конкретных поставщиков. Поставщики могут различаться некоторыми специфическими параметрами или поддерживать специальные средства, недоступные для общих
Базы данных - это хранилища структурированных данных, с которыми удобно работать. СУБД - это программное приложение, извлекающее, отображающее в удобном виде и модифицирующее структурированные данные. Чаще всего применяются
Важное место в архитектуре ADO.NET занимают
Connection - используется для установки соединения с Command - используется для выполнения команд SQL и DataReader - предоставляет быстрый доступ только для чтения к DataAdapter - этот класс решает две задачи:DataSet ( набор данных ) информацией, извлеченной из DataSet ADO.NET содержит набор специализированных поставщиков для различных источников данных. Каждый поставщик данных имеет специфическую реализацию классов Connection, , DataReader, DataAdapter, оптимизированных для конкретных баз данных. Например, если нужно подключиться к базе данных SQL Server, то используется класс .
Библиотека .NET Framework содержит четыре типа готовых
Для других типов данных разработчики могут либо создавать свои поставщики, либо приобретать их у сторонних организаций. Выбирая класс поставщика, нужно сначала попытаться найти родного поставщика для данного
Технология OLE DB существует уже много лет как часть ADO, поэтому большинство распространенных источников данных ( SQL Server, Oracle, Access, MySQL и т.д.) предусматривают драйверы OLE DB. Наконец, можно использовать поставщик ODBC в сочетании с драйвером ODBC, поскольку это самая старая технология доступа к данным, но производительность такого подключения может быть очень низкой.
Применение специализированных поставщиков может потребовать изменения кода приложения при переходе к другому типу базы данных. Но эти изменения будут незначительными, поскольку поставщики различаются только до уровня набора данных

Классы ADO.NET группируются в нескольких пространствах имен:
System.Data - содержит ключевые System.Data.Common - содержит System.Data.OleDb - включают поставщиков для источников данных OLE DBSystem.Data.SqlClient - включают поставщиков для источников данных SQL ServerSystem.Data.OracleClient - включают поставщиков для источников данных OracleSystem.Data.SqlTypes - содержит структуры, соответствующие родным типам данных SQL Server. Эти классы не являются необходимыми, но в силу их специфичности оптимизируют доступ к данным SQL ServerSystem.Data.Odbc - включают поставщиков для источников данных ODBC. Операционная система Windows имеет драйверы ODBC для всех видов источников данных, которые конфигурируются в панели управления Пуск/Настройка/Панель управления/Администрирование/Источники данных (ODBC)
Этот класс устанавливает соединение с Connection реализует от наследуемого интерфейса IDbConnection.
При установке соединения требуется строка соединения, которая представляет собой список пар имя=значение, разделенных точкой с запятой. За исключением небольших различий,
localhostNorthwind, которая инсталируется по умолчанию с установкой SQL Server 2005Ниже представлены примеры Northwind на текущем компьютере
| Источник данных | |
|---|---|
| SQL Server с использованием |
string connectionString = " |
SQL Server с использованием учетной записи ( id= ) |
string connectionString = " |
Используется поставщик OLE DB для подключения к Sales типа Oracle через драйвер MSDAORA с правами администратора и пустым паролем |
string connectionString = " |
Подключение к Access. Символ "@" требует от компилятора интерпретировать строку буквально |
string connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;" + @" |
Connection или в методе Connection.Open().
<?xml version="1.0"?> <configuration> <connectionStrings> <add name="Northwind" connectionString="Data Source=localhost; Initial Catalog=Northwind; Integrated Security=SSPI" /> </connectionStrings> <system.web> </system.web> </configuration>
В любое время в коде страницы можно извлечь эту
string connectionString = WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
После установки SQL Server 2005 нужно убедиться, что служба SQLServerAgent включена в режим Auto. Это можно сделать через Пуск/Настройка/Панель управления/Администрирование/Службы

Приведем простой пример установки соединения с Northwind.
Создайте командой
Переведите страницу в режим Design и добавьте из вкладки Standard элемент Label с именем lblInfo
В режиме Design Page_Load() в блоке скриптов, который заполните так, чтобы общий код страницы был следующим(рис ) Код страницы TestConnection.aspx<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
try
{
con.Open();
lblInfo.Text = "<b>Версия сервера:</b> " + con.ServerVersion;
lblInfo.Text += "<br /><b>Соединение:</b> " + con.State.ToString();
}
catch(Exception err)
{
lblInfo.Text = "<b>Ошибка чтения базы данных.</b>";
lblInfo.Text += err.Message;
}
finally
{
con.Close();
lblInfo.Text += "<br /><b>Теперь соединение:</b> ";
lblInfo.Text += con.State.ToString();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
</form>
</body>
</html>
Поместите в файл web.config (рис ) Строка соединения в файле web.config<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="Northwind" connectionString =
"Data Source=localhost;
Initial Catalog=Northwind;
user id=sa; password=;" />
</connectionStrings>
<system.web>
<compilation debug="true"/>
</system.web>
</configuration>
Выполните страницу, сделав ее стартовой, результат должен быть примерно следующим
Соединение как объект занимает определенные ресурсы, поэтому открывать соединение нужно как можно позже, и освобождать как можно быстрее. В примере предусмотрен блок на тот случай, что даже при возникновении исключения соединение все равно будет закрыто. Если это не предусмотреть, то в случае необработанного исключения соединение останется открытым до тех пор, пока сборщик мусора не уничтожит объект типа .
Другой подход гарантированного закрытия соединения - использовать блок using. Оператор using декларирует, что мы используем уничтожаемый объект в течение краткого периода времени. Как только выполнение блока using завершится, среда выполнения немедленно освободит соответствующий объект, вызвав метод . Вызов этого метода эквивалентен вызову метода Close() для объекта Connection.
Создайте копию страницы TestConnection.aspx и назначьте ей имя TestConnection1.aspx
Назначьте новую страницу стартовой и заполните ее следующим кодом(рис ) Код страницы TestConnection1.aspx<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
using (con)
{
con.Open();
lblInfo.Text = "<b>Версия сервера:</b> " + con.ServerVersion;
lblInfo.Text += "<br /><b>Соединение:</b> " + con.State.ToString();
}
lblInfo.Text += "<br /><b>Теперь соединение:</b> ";
lblInfo.Text += con.State.ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
</form>
</body>
</html>
Выполните страницу, результат должен быть прежним
При запросе любой страницы, использующей
Пул соединений - это практика хранения приложением постоянного набора открытых соединений, разделяемых между сеансами при использовании одного и того же Open() обслуживается непосредственно из доступного пула без повторного открытия соединения. Когда клиент освобождает соединение методом Close() или , оно не закрывается, а возвращается в пул, чтобы обслужить следующий запрос.
Пулы автоматически организуются самими
Пулы соединений SQL Server и Oracle используют механизм полнотекстового сравнения. Это значит, что любое минимальное изменение в
Пулы соединений можно настраивать, добавляя соответствующие атрибуты в
| Параметр | Описание |
|---|---|
Max |
Максимальное количество соединений, разрешенных для хранения в пуле (по умолчанию 100). Если достигается этот максимальный размер пула, любые последующие попытки соединения становятся в очередь. Если время жизни соединения, установленное параметром Connection., истечет раньше, чем подошла очередь в пуле, возникает исключение. |
|
Минимальное количество соединений, которое должно оставаться в пуле (по умолчанию 0). Это число соединений будет создано в пуле при открытии первого соединения. |
Pooling |
По умолчанию true. При значении false отключается механизм пула |
Connection Lifetime |
Устанавливает время хранения соединения в пуле (в секундах). По умолчанию значение 0 устанавливает неограниченное время жизни |
Некоторые поставщики имеют методы очистки пула соединений. Например, поставщик имеет - очистка пула конкретного соединения и - очистка пулов всех соединений в текущем
Класс позволяет выполнить любой CommandType, CommandText и Connection, либо передать необходимую информацию в аргументах .
Текстом команды может быть SQL-оператор, ) или CommandType, присваиваемого свойству .
| Значение | Описание |
|---|---|
System.Data.CommandType.Text |
Требование выполнить SQL-оператор, указанный в свойстве . Установлено по умолчанию в |
System.Data.CommandType.StoredProcedure |
Требование выполнить |
System.Data.CommandType.TableDirect |
Требование извлечь все записи из таблицы, указанной в свойстве .Не поддерживается поставщиком данных SQL Server |
Расширим наш пример, чтобы применить код создания объекта .
Создайте из копии файла TestConnection1.aspx файл с именем TestConCommand.aspx и установите его стартовым
Дополните код страницы TestConCommand.aspx так(рис ) Страница TestConCommand.aspx создания объектов соединения и команды<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Установили минимальный размер пула соединения равный 10
connectString += "Min Pool Size=10;";
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
using (con)
{
// Получить соединение из пула (если есть)
// или создать пул с 10 соединениями (если нет)
con.Open();
lblInfo.Text = "<b>Соединение:</b> " + con.State.ToString();
// Создали объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand();
// Настроили объект Command
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "SELECT * FROM Employees";
}
// Вернуть соединение в пул
lblInfo.Text += "<br /><b>Соединение:</b> ";
lblInfo.Text += con.State.ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
</form>
</body>
</html>
Исполните страницу, чтобы получить такой результат
Учитывая, что по умолчанию установлено CommandType = CommandType.Text, команду можно сразу указать в перегруженном конструкторе при создании объекта команд. Например, код
// Создали объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand();
// Настроили объект Command
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "SELECT * FROM Employees";
можно заменить кодом
// Создали и настроили объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand("SELECT * FROM Employees", con);
В данном примере мы просто настроили, но не выполнили объект класса , поэтому никакой имеет три метода выполнения команды, приведенные в таблице на примере класса SqlCommand
| Метод | Описание |
|---|---|
System.Data.SqlClient.SqlCommand.ExecuteNonQuery() |
Выполняет команды, отличные от SELECT, такие как SQL-операторы вставки, удаления или обновления записей. Возвращает количество строк, обработанное командой |
System.Data.SqlClient.SqlCommand.ExecuteScalar() |
Выполняет запрос SELECT и возвращает первое поле первой строки результирующего набора, сгенерированного командой. Обычно применяется в запросах с функциями или COUNT() для вычисления единственного значения |
System.Data.SqlClient.SqlCommand.ExecuteReader() |
Выполняет запрос SELECT и возвращает объект DataReader - результирующий набор данных, доступный только для чтения |
Объект класса DataReader представляет собой результирующий набор данных только для чтения и является самым простым и быстрым средством доступа к данным. Он является выборкой данных по SELECT и состоит из строк и колонок, которые можно анализировать и отображать. Но в нем нет возможностей сортировки, как в более развитых объектах. Ниже приведены несколько методов DataReader на примере класса System.Data.SqlClient.SqlDataReader
| Метод | Описание |
|---|---|
SqlDataReader.Read() |
Перемещает курсор строки на следующую строку в потоке. Его нужно также вызывать первым после создания объекта DataReader, поскольку начальный курсор позиционируется перед первой строкой. Возвращает false при чтении последней строки набора данных |
SqlDataReader.GetValue(int) |
Читает значение поля текущей строки по указанному индексу (индекс начинается с нуля) |
SqlDataReader.GetValues(object[ ]) |
Сохраняет значение текущей строки в массиве. Количество сохраняемых полей зависит от размера массива, переданного этому методу. Чтобы сохранить все поля текущей строки, нужно создать массив SqlDataReader. |
SqlDataReader.GetInt32(int) |
Возвращает значение поля |
SqlDataReader.GetDateTime(int) |
Возвращает значение поля |
SqlDataReader.NextResult() |
Если команда, которая сгенерировала DataReader, возвратила более одного результирующего набора строк, то этот метод перемещает текущий курсор данных на первую строку следующего набора |
SqlDataReader.Close() |
Закрывает модуль чтения |
Приведем пример, в котором создается и выполняется простая команда запроса для выборки всех записей из таблицы Employees учебной базы данных Northwind. Мы помним, что
Создайте копию страницы TestConCommand.aspx с именем TestDataReader.aspx
Заполните страницу приведенным кодом(рис ) Код страницы TestDataReader.aspx с чтением из базы Northwind<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
// Создали объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand();
// Настроили объект Command
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "SELECT * FROM Employees";
// Создаем ссылку на вспомогательную строку
System.Text.StringBuilder htmlStr = new StringBuilder("");
using (con)
{
// Открыли соединение
con.Open();
// Выполняем команду и получаем результирующий набор данных
System.Data.SqlClient.SqlDataReader reader =
cmd.ExecuteReader();
// Перебираем все записи результирующего набора
// и строим HTML-строку
while (reader.Read())
{
htmlStr.Append("<li>Служащий ");
htmlStr.Append(reader["TitleOfCourtesy"]);
htmlStr.Append(" <b>");
htmlStr.Append(reader.GetString(1));
htmlStr.Append("</b>, ");
htmlStr.Append(reader.GetString(2));
htmlStr.Append(" - работает с ");
htmlStr.Append(reader.GetDateTime(6).ToString("d"));
htmlStr.Append("</li>");
}
// Закрываем набор данных
reader.Close();
}
// Выдаем результаты запроса
lblInfo.Text = htmlStr.ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
</form>
</body>
</html>
Исполните страницу TestDataReader.aspx, должен получиться такой клиентский результатВ этом примере объект класса StringBuilder существенно увеличивает производительность за счет выделения буфера памяти для символов. Если использовать объект класса String, то при каждой операции
В большинстве случаев нам такой способ выборки информации из базы в будущем не понадобится, поскольку существуют библиотечные элементы управления для
Команда SqlCommand.ExecuteReader() может возвращать множественный результирующий набор данных в двух случаях:
SELECTВот пример строки SELECT:
string sql = "SELECT TOP 5 * FROM Employees;" + "SELECT TOP 5 * FROM Customers;" + "SELECT TOP 5 * FROM Suppliers";
Приведенная строка содержит три запроса, каждый из которых возвращает в результирующий набор данных по пять первых записей из трех таблиц базы Northwind. Ниже приведен код страницы, в котором выполняется последовательная NextResult(), который возвратит false, когда больше не останется результирующих наборов.
Создайте копию страницы TestDataReader.aspx с именем TestMultiDataReader.aspx и определите ее стартовой
Заполните страницу приведенным кодом(рис ) Код страницы TestMultiDataReader.aspx для нескольких таблиц базы Northwind<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
// Создали объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand();
// Настроили объект Command
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "SELECT TOP 5 * FROM Employees;"
+ "SELECT TOP 5 * FROM Customers;"
+ "SELECT TOP 5 * FROM Suppliers";
System.Text.StringBuilder htmlStr = new StringBuilder("");
int i = 0;
using (con)
{
// Открыли соединение
con.Open();
// Выполняем команду и получаем результирующий набор данных
System.Data.SqlClient.SqlDataReader reader =
cmd.ExecuteReader();
// Перебираем частные наборы в результирующем наборе данных
do
{
htmlStr.Append("<h2>Набор: ");// Заголовок h2 HTML
htmlStr.Append(i.ToString());
htmlStr.Append("</h2>");
htmlStr.Append("<ul>"); // Начало маркированного списка
// Перебираем все записи текущего набора
// (их должно по команде быть 5 первых)
// в результирующем наборе и строим HTML-строку
while (reader.Read())
{
htmlStr.Append("<li>");// Строка списка HTML
// Перебираем все поля строки набора
for (int field = 0; field < reader.FieldCount; field++)
{
if (field >= 3) // Ограничимся тремя первыми полями
continue;
htmlStr.Append("<b>");
htmlStr.Append(reader.GetName(field).ToString());// Имя поля
htmlStr.Append(": </b>");
htmlStr.Append(reader.GetValue(field).ToString());// Значение поля
htmlStr.Append("nbsp;nbsp;nbsp;nbsp;nbsp;");// Жесткие пробелы HTML
}
htmlStr.Append("</li>");
}
htmlStr.Append("</ul>"); // Конец маркированного списка
i++;
} while (reader.NextResult());
// Закрываем набор данных
reader.Close();
}
// Показываем пользователю результаты запроса
lblInfo.Text = htmlStr.ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
</form>
</body>
</html>
Исполните страницу, должен получиться такой результат
Мы искусственно ограничили количество отображаемых полей тремя. Если этот оператор убрать, то на пользователя обрушится
Если нам наплевать на пользователя и мы хотим показать ему . В этом случае необходимую страницу можно получить так:
Создайте копию страницы TestMultiDataReader.aspx с именем GridViewDataReader.aspx и определите ее стартовой
Поместите на страницу элемент управления GridView из вкладки Data панели Toolbox
Заполните страницу приведенным кодом(рис ) Код страницы GridViewDataReader.aspx с неформатированным просмотром <%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
// Создали объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand();
// Настроили объект Command
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "SELECT TOP 5 * FROM Suppliers";
using (con)
{
// Открыли соединение
con.Open();
// Выполняем команду и получаем результирующий набор данных
System.Data.SqlClient.SqlDataReader reader =
cmd.ExecuteReader();
// Подключаем элемент управления GridView
// к полученному результирующему набору
GridView1.DataSource = reader;
// Наполняем элемент управления GridView
// всеми извлеченными записями DataReader
GridView1.DataBind();
// Закрываем набор данных
reader.Close();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="GridView1" runat="server">
</asp:GridView>
</div>
</form>
</body>
</html>
Исполните страницу, должен получиться такой результат| SupplierID | CompanyName | ContactName | ContactTitle | Address | City | PostalCode | Country | HomePage | |||
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | Exotic Liquids | Charlotte Cooper | Purchasing Manager | 49 Gilbert |
London | EC1 4SD | UK | (171) 555-2222 | |||
| 2 | New Orleans Cajun |
Shelley Burke | P.O. |
New Orleans | LA | 70117 | USA | (100) 555-4822 | #CAJUN. |
||
| 3 | Grandma Kelly's Homestead | Regina Murphy | Sales Representative | 707 Oxford Rd. | MI | 48104 | USA | (313) 555-5735 | (313) 555-3349 | ||
| 4 | Tokyo Traders | Yoshi Nagase | 9-8 Sekimai Musashino-shi | Tokyo | 100 | Japan | (03) 3555-5011 | ||||
| 5 | Cooperativa de Quesos 'Las Cabras' | Antonio |
Calle |
Oviedo | Asturias | 33007 | Spain | (98) 598 76 54 |
Этот метод объекта возвращает Object, которая является результатом работы COUNT() или
Создайте копию страницы TestConCommand.aspx с именем TestExecuteScalar.aspx и сделайте ее стартовой
Скорректируйте страницу TestExecuteScalar.aspx так(рис ) Страница TestExecuteScalar.aspx<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
// Создали объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand();
// Настроили объект Command
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = "SELECT COUNT(*) FROM Employees";
using (con)
{
// Открыли соединение
con.Open();
// Выполнили команду подсчета записей таблицы Employees
// с применением явного преобразования типа
int recordCount = (int)cmd.ExecuteScalar();
lblInfo.Text = "<b>Количество записей:</b> " + recordCount.ToString();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
</form>
</body>
</html>
Постройте страницу, результат будет таким
Этот метод напоминает консольную утилиту, которая работает в пакетном режиме, выполняет какие-либо действия, но не возвращает результатов. Метод ExecuteNonQuery() выполняет такие SQL-команды, как , DELETE, UPDATE, а возвращает только число, равное количеству обработанных записей.
В качестве примера приведем код страницы, удаляющий запись с определенным значением поля EmployeeID в таблице Employees Northwind. Поскольку эта база учебная, не будем ее портить и зададим заведомо несуществующее значение поля EmployeeID.
<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Формируем соединение
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
// Формируем команду в конструкторе класса
int empID = 111; // Заведомо несуществующее значение
string sql = "DELETE FROM Employees WHERE EmployeeID = " + empID.ToString();
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand(sql, con);
try
{
con.Open();
int recCount = cmd.ExecuteNonQuery();
lblInfo.Text = string.Format("<b>Удалено записей:</b> {0}", recCount);
}
catch(System.Data.SqlClient.SqlException err)
{
lblInfo.Text = string.Format("<b>Ошибка удаления:</b> {0}", err.Message);
}
finally
{
con.Close();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
</form>
</body>
</html>
Очень часто возникает необходимость дать пользователю возможность самому сформировать строку
Рассмотрим это на примере.
Сделайте копию с именем UserSqlGridData.aspx из файла GridViewDataReader.aspx и назначьте ее стартовой
Разместите выше элемента
Откорректируйте дескрипторный и встроенный коды страницы так(рис ) Код страницы UserSqlGridData.aspx<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
// Создали строку SQL-запроса
string sql =
"SELECT Orders.CustomerID, Orders.OrderID, COUNT(UnitPrice) AS Items, "
+ "SUM(UnitPrice * Quantity) AS Total FROM Orders "
+ "INNER JOIN[Order Details] "
+ "ON Orders.OrderID = [Order Details].OrderID "
+ "WHERE Orders.CustomerID = '" + TextBox1.Text + "' "
+ "GROUP BY Orders.OrderID, Orders.CustomerID";
// Создали объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand(sql, con);
using (con)
{
// Открыли соединение
con.Open();
// Выполняем команду и получаем результирующий набор данных
System.Data.SqlClient.SqlDataReader reader =
cmd.ExecuteReader();
// Подключаем элемент управления GridView
// к полученному результирующему набору
GridView1.DataSource = reader;
// Наполняем элемент управления GridView
// всеми извлеченными записями DataReader
GridView1.DataBind();
// Закрываем набор данных
reader.Close();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Введите ID пользователя:<br />
<asp:TextBox ID="TextBox1" runat="server" Width="248px" Font-Bold="True">ALFKI</asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Получить" /><br />
<br />
<asp:GridView ID="GridView1" runat="server">
</asp:GridView>
</div>
</form>
</body>
</html>
Исполните страницу и должен получиться такой результат
| CustomerID | OrderID | Items | |
|---|---|---|---|
| ALFKI | 10643 | 3 | 1086,0000 |
| ALFKI | 10692 | 1 | 878,0000 |
| ALFKI | 10702 | 2 | 330,0000 |
| ALFKI | 10835 | 2 | 851,0000 |
| ALFKI | 10952 | 2 | 491,2000 |
| ALFKI | 11011 | 2 | 960,0000 |
При значении текстового поля ALFKI вычисленный SQL-оператор будет иметь такой вид:
string sql =
"SELECT Orders.CustomerID, Orders.OrderID, COUNT(UnitPrice) AS Items, "
+ "SUM(UnitPrice * Quantity) AS Total FROM Orders "
+ "INNER JOIN[Order Details] "
+ "ON Orders.OrderID = [Order Details].OrderID "
+ "WHERE Orders.CustomerID = 'ALFKI' "
+ "GROUP BY Orders.OrderID, Orders.CustomerID";
Злоумышленник может попытаться подделать
Предположим, что злоумышленник введет в текстовое поле следующий текст
ALFKI' OR '1' = '1
В этом случае
string sql =
"SELECT Orders.CustomerID, Orders.OrderID, COUNT(UnitPrice) AS Items, "
+ "SUM(UnitPrice * Quantity) AS Total FROM Orders "
+ "INNER JOIN[Order Details] "
+ "ON Orders.OrderID = [Order Details].OrderID "
+ "WHERE Orders.CustomerID = 'ALFKI' OR '1' = '1' "
+ "GROUP BY Orders.OrderID, Orders.CustomerID";
Этот оператор вернет все записи о заказах, поскольку условие 1=1 истинно для всех строк
Исполните страницу UserSqlGridData.aspx и введите как злоумышленник в текстовое поле строкуALFKI' OR '1' = '1
Должен получиться результат, малая часть которого приведена ниже

| CustomerID | OrderID | Items | |
|---|---|---|---|
| VINET | 10248 | 3 | 440,0000 |
| TOMSP | 10249 | 2 | 1863,4000 |
| HANAR | 10250 | 3 | 1813,0000 |
| VICTE | 10251 | 3 | 670,8000 |
| SUPRD | 10252 | 3 | 3730,0000 |
| HANAR | 10253 | 3 | 1444,8000 |
| CHOPS | 10254 | 3 | 625,2000 |
| RICSU | 10255 | 4 | 2490,5000 |
| WELLI | 10256 | 2 | 517,8000 |
| HILAA | 10257 | 3 | 1119,9000 |
| ERNSH | 10258 | 3 | 2018,6000 |
| CENTC | 10259 | 2 | 100,8000 |
В результате злоумышленнику передается вся информация о сделанных заказах. Если информация содержит номера кредитных карточек, карточек социального страхования или дату рождения заказчиков, то репутация такого сайта будет с треском провалена и никто пользоваться им не будет.
Возможны и более сложные атаки. Например, злонамеренный пользователь может просто закомментировать
Злоумышленник может применить пакетную команду, чтобы выполнить произвольный оператор SQL. При использовании поставщика данных SQL Server достаточно просто добавить точку с запятой, за которой передать дополнительную команду. Таким образом можно, например, удалить содержимое другой таблицы, или даже вызвать системную xp_cmdshell, чтобы запустить произвольную программу из командной строки.
Вот что злоумышленник может ввести в текстовое поле, чтобы осуществить более изощренную атаку внедрением SQL, удалив все строки из таблицы :
ALFKI'; DELETE * FROM Customers --
Не делайте этого с нашей учебной базой!!!
Для того, чтобы противостоять атаке внедрением, следует помнить несколько правил:
TextBox.MaxLength, чтобы предотвратить длинный ввод, когда в этом нет необходимостиException.Messagestring ID = TextBox1.Text.Replace("'", "''");
определенные параметры-заполнители SQL через коллекцию . Параметры-заполнители прописываются отдельно и автоматически кодируются.
Синтаксис
Приведем пример, использующий параметризованную команду, в котором исключена возможность атаки внедрением SQL.
Сделайте из страницы UserSqlGridData.aspx копию с именем UserSqlParamCommand.aspx и назначьте ее стартовой
Откорректируйте страницу UserSqlParamCommand.aspx, чтобы она имела следующий код(рис ) Код страницы UserSqlParamCommand.aspx<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
// Создали строку SQL-запроса
string sql =
"SELECT Orders.CustomerID, Orders.OrderID, COUNT(UnitPrice) AS Items, "
+ "SUM(UnitPrice * Quantity) AS Total FROM Orders "
+ "INNER JOIN[Order Details] "
+ "ON Orders.OrderID = [Order Details].OrderID "
+ "WHERE Orders.CustomerID = @CustomID "
+ "GROUP BY Orders.OrderID, Orders.CustomerID";
// Создали объект Command
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand(sql, con);
// Дополнили коллекцию Parameters
cmd.Parameters.Add("@CustomID", TextBox1.Text);
using (con)
{
// Открыли соединение
con.Open();
// Выполняем команду и получаем результирующий набор данных
System.Data.SqlClient.SqlDataReader reader =
cmd.ExecuteReader();
// Подключаем элемент управления GridView
// к полученному результирующему набору
GridView1.DataSource = reader;
// Наполняем элемент управления GridView
// всеми извлеченными записями DataReader
GridView1.DataBind();
// Закрываем набор данных
reader.Close();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Введите ID пользователя:<br />
<asp:TextBox ID="TextBox1" runat="server" Width="248px" Font-Bold="True">ALFKI</asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Получить" /><br />
<br />
<asp:GridView ID="GridView1" runat="server">
</asp:GridView>
</div>
</form>
</body>
</html>
Код страницы остался почти таким же, за исключением двух строк, вводящих команду с именованным параметром @CustomID.
Попробуйте исполнить атаку внедрением с содержимым ALFKI' OR '1' = '1Мы видим, что страница не возвращает никаких данных, поскольку ни одна запись таблицы в поле CustomerID не имеет значение, равное введенной строке ALFKI' OR '1' = '1. Таким простым способом мы
Рассмотрим пример SQL-кода, необходимого при Employees. Этой Northwind.
CREATE PROCEDURE InsertEmployee @TitleOfCourtesy varchar(25), @LastName varchar(20), @FirstName varchar(10), @EmployeeID int OUTPUT AS INSERT INTO Employees (TitleOfCourtesy, LastName, FirstName, HireDate) VALUES(@TitleOfCourtesy, @LastName, @FirstName, GETDATE()); SET @EmployeeID = @@IDENTITY
Эту Nordhwind. Но я не смог это сделать, поскольку оболочка не видит эту базу из-за неправильно установленного пакета SQL Server 2005 (или потому, что у меня руки кривые). Чтобы как-то вывернуться, в приведенном ниже коде мы добавим ее программно.
Создайте новую страницу с именем TestStoredProcedure.aspx и назначьте ее стартовой
Поместите на страницу из вкладки Standard элементы управления согласно дескрипторному коду, приведенному ниже (устал писать, да и студент Зиборов ругается, но зато все работает!)
Настройте код страницы, чтобы в итоге он был таким<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
try
{
string sql =
"CREATE PROCEDURE InsertEmployee "
+ "@TitleOfCourtesy varchar(25),"
+ "@LastName varchar(20),"
+ "@FirstName varchar(10),"
+ "@EmployeeID int OUTPUT "
+ "AS "
+ "INSERT INTO Employees "
+ "(TitleOfCourtesy, LastName, FirstName, HireDate) "
+ "VALUES(@TitleOfCourtesy, @LastName, @FirstName, GETDATE());"
+ "SET @EmployeeID = @@IDENTITY";
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand(sql, con);
con.Open();
cmd.ExecuteNonQuery();
}
catch (System.Data.SqlClient.SqlException error)
{
}
finally
{
con.Close();
}
}
protected void AddEmployee_Click(object sender, EventArgs e)
{
// Извлекли строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создали объект соединения
System.Data.SqlClient.SqlConnection con =
new System.Data.SqlClient.SqlConnection(connectString);
// Создать и настроить объект Command
// для вызова хранимой процедуры InsertEmployee
System.Data.SqlClient.SqlCommand cmd =
new System.Data.SqlClient.SqlCommand("InsertEmployee", con);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
// Добавить входные параметры хранимой процедуры
// в коллекцию параметров Command.Parameters
// Добавляем первый входной параметр
cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter(
"@TitleOfCourtesy", System.Data.SqlDbType.NVarChar, 25));
cmd.Parameters["@TitleOfCourtesy"].Value = title.Text;
// Добавляем второй входной параметр
cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter(
"@LastName", System.Data.SqlDbType.NVarChar, 20));
cmd.Parameters["@LastName"].Value = lastName.Text;
// Добавляем третий входной параметр
cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter(
"@FirstName", System.Data.SqlDbType.NVarChar, 10));
cmd.Parameters["@FirstName"].Value = firstName.Text;
// Добавить выходной параметр хранимой процедуры
// в коллекцию параметров Command.Parameters
cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter(
"@EmployeeID", System.Data.SqlDbType.Int, 4));
cmd.Parameters["@EmployeeID"].Direction = System.Data.ParameterDirection.Output;
using (con)
{
// Открыли соединение
con.Open();
// Выполняем команду
int recCount = cmd.ExecuteNonQuery();
lblInfo.Text = string.Format("<b>Вставлено записей:</b> {0}<br />", recCount);
// Получить вновь сгенерированный идентификатор
int empID = (int)cmd.Parameters["@EmployeeID"].Value;
lblInfo.Text += "Новый идентификатор: " + empID.ToString();
}
// Соединение закрылось автоматически
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
Введите титул:<asp:TextBox ID="title" runat="server">студент</asp:TextBox><br />
Введите имя:<asp:TextBox ID="firstName" runat="server">Иван</asp:TextBox><br />
Введите фамилию:<asp:TextBox ID="lastName" runat="server">Петров</asp:TextBox><br />
<asp:Label ID="lblInfo" runat="server" />
<br />
<br />
<asp:Button ID="AddEmployee" runat="server" Text="Добавить служащего"
OnClick="AddEmployee_Click" />
<asp:LinkButton ID="btnResult" runat="server"
PostBackUrl="~/TestDataReader.aspx">Показать список</asp:LinkButton>
</form>
</body>
</html>
Интерфейс страницы будет таким

Добавьте служащего щелчком по кнопке, после чего выведите список. Результат будет примерно такимВ обработчике Page_Load() мы намеренно применили пустой блок
Транзакция, это набор операций SQL, которые либо должны быть успешно выполнены все, либо должны быть все отменены, если хоть одна операция дала сбой. Такой механизм гарантирует, что данные всегда находятся в корректном состоянии.
Connection.BeginTransaction(). Принимается транзакция методом , а отменяется, если возникло исключение, методом .
Приведем пример, в котором в таблицу Employees Nordhwind добавляется две записи через механизм контроля транзакций. Эти записи можно было бы добавить и обычным способом, но в критических случаях делать это нужно под контролем транзакций.
Сделайте из страницы TestStoredProcedure.aspx копию с именем TestTransaction.aspx и определите ее стартовой
Заполните страницу следующим кодом(рис ) Код страницы TestTransaction.aspx<%@ Page Language="C#" %>
<script runat="server">
// Объявляем переменные-ссылки как члены класса страницы
string sql1 = "INSERT INTO Employees (LastName, FirstName, HireDate) "
+ "VALUES ('Зиборов', 'Алексей', GETDATE())";
string sql2 = "INSERT INTO Employees (LastName, FirstName, HireDate) "
+ "VALUES ('Погодаева', 'Татьяна', GETDATE())";
System.Data.SqlClient.SqlConnection con;
System.Data.SqlClient.SqlCommand cmd1, cmd2;
System.Data.SqlClient.SqlTransaction tran = null;
/**** Для отладки ****/
string sql3 = "DELETE FROM Employees WHERE EmployeeID > 9";
System.Data.SqlClient.SqlCommand cmd3;
/*********************/
protected void Page_Load(object sender, EventArgs e)
{
// Извлечь строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создать объект соединения
con = new System.Data.SqlClient.SqlConnection(connectString);
// Создать объекты команд
cmd1 = new System.Data.SqlClient.SqlCommand(sql1, con);
cmd2 = new System.Data.SqlClient.SqlCommand(sql2, con);
/**** Для отладки ****/
cmd3 = new System.Data.SqlClient.SqlCommand(sql3, con);
/*********************/
}
protected void AddEmployee_Click(object sender, EventArgs e)
{
try // Попытка
{
// Открыть соединение
con.Open();
/**** Для отладки ****/
cmd3.ExecuteNonQuery(); // Удалить последние записи
/*********************/
// Начать транзакцию
tran = con.BeginTransaction();
// Пометить команды включенными в транзакцию
cmd1.Transaction = tran;
cmd2.Transaction = tran;
// Выполнить обе команды
// Должны выполняться только команды транзакции!!!
cmd1.ExecuteNonQuery();
cmd2.ExecuteNonQuery();
// Искусственная генерация исключения
// для тестирования отмены транзакции
//throw new ApplicationException();
// Подтвердить и закончить транзакцию
tran.Commit();
// Посчитать количество записей таблицы
// Теперь можно выполнять другие команды
cmd1.CommandText = "SELECT COUNT(*) FROM Employees";
string recCount = cmd1.ExecuteScalar().ToString();
// Выдать результат
lblInfo.Text = "Добавлены две записи!<br />";
lblInfo.Text += "Общее число записей " + recCount;
}
catch // Откат при любом исключении
{
lblInfo.Text = "Транзакция отменена!";
tran.Rollback();
}
finally // Обязательное завершение
{
// Закрыть соединение по-любому
con.Close();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<asp:Label ID="lblInfo" runat="server" />
<br />
<br />
<asp:Button ID="AddEmployee" runat="server"
Text="Выполнить транзакцию"
OnClick="AddEmployee_Click" />
<asp:LinkButton ID="btnResult" runat="server"
PostBackUrl="~/TestDataReader.aspx">
Показать список</asp:LinkButton>
</form>
</body>
</html>
Исполните страницу и получите следующий отклик при выполнении транзакции
Просмотрите таблицу Employees Nordhwind по Обратите внимание, что пока транзакция находится в процессе работы, должны выполняться только команды транзакции. Область работы транзакции определяется ;
Чтобы протестировать свойство tran. строку генерации исключения
throw new ApplicationException();
Иногда может возникнуть необходимость
Немного изменим предыдущий пример, введя возможность частичного
Скопируйте страницу TestTransaction.aspx в TestTransactionSave.aspx и назначьте ее стартовой
Измените код новой страницы, чтобы он был таким(рис ) Код страницы TestTransactionSave.aspx частичного отката транзакции<%@ Page Language="C#" %>
<script runat="server">
// Объявляем переменные-ссылки как члены класса страницы
string sql1 = "INSERT INTO Employees (LastName, FirstName, HireDate) "
+ "VALUES ('Зиборов', 'Алексей', GETDATE())";
string sql2 = "INSERT INTO Employees (LastName, FirstName, HireDate) "
+ "VALUES ('Погодаева', 'Татьяна', GETDATE())";
System.Data.SqlClient.SqlConnection con;
System.Data.SqlClient.SqlCommand cmd1, cmd2;
System.Data.SqlClient.SqlTransaction tran = null;
/**** Для отладки ****/
string sql3 = "DELETE FROM Employees WHERE EmployeeID > 9";
System.Data.SqlClient.SqlCommand cmd3;
/*********************/
protected void Page_Load(object sender, EventArgs e)
{
// Извлечь строку соединения из web.config
string connectString = System.Web.Configuration.WebConfigurationManager.
ConnectionStrings["Northwind"].ConnectionString;
// Создать объект соединения
con = new System.Data.SqlClient.SqlConnection(connectString);
// Создать объекты команд
cmd1 = new System.Data.SqlClient.SqlCommand(sql1, con);
cmd2 = new System.Data.SqlClient.SqlCommand(sql2, con);
/**** Для отладки ****/
cmd3 = new System.Data.SqlClient.SqlCommand(sql3, con);
/*********************/
}
protected void AddEmployee_Click(object sender, EventArgs e)
{
try // Попытка
{
// Открыть соединение
con.Open();
/**** Для отладки ****/
cmd3.ExecuteNonQuery(); // Удалить последние записи
/*********************/
// Начать транзакцию
tran = con.BeginTransaction();
// Пометить команды включенными в транзакцию
cmd1.Transaction = tran;
cmd2.Transaction = tran;
// Выполнить первую команду транзакции
cmd1.ExecuteNonQuery();
// Пометить точку отката
tran.Save("Ziborov");
// Выполнить вторую команду транзакции
cmd2.ExecuteNonQuery();
// Выбросить исключение
throw new ApplicationException();
}
catch // Откат
{
// Откатить частично
tran.Rollback("Ziborov");
// Подтвердить и закончить транзакцию
tran.Commit();
lblInfo.Text = "Транзакция частично отменена!";
// Посчитать количество записей таблицы
cmd1.CommandText = "SELECT COUNT(*) FROM Employees";
string recCount = cmd1.ExecuteScalar().ToString();
// Выдать результат
lblInfo.Text = "Добавлена одна запись!<br />";
lblInfo.Text += "Общее число записей " + recCount;
}
finally // Обязательное завершение
{
// Закрыть соединение по-любому
con.Close();
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<asp:Label ID="lblInfo" runat="server" />
<br />
<br />
<asp:Button ID="AddEmployee" runat="server"
Text="Выполнить транзакцию"
OnClick="AddEmployee_Click" />
<asp:LinkButton ID="btnResult" runat="server"
PostBackUrl="~/TestDataReader.aspx">
Показать список</asp:LinkButton>
</form>
</body>
</html>
Выполните страницу и убедитесь, что Классы поставщиков оптимизированы под конкретный тип
Основная идея модели фабрик заключается в том, что можно использовать единственный объект-фабрику для создания всех прочих необходимых для конкретного поставщика объектов. Но это можно выполнить и созданием конкретных объектов поставщика. Самое главное удобство заключается в том, что конкретную фабрику поставщика можно создавать из обобщенного класса фабрик System.Data.
<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="Northwind" connectionString="Data Source=localhost;
Initial Catalog=Northwind; user id=sa; password=;"/>
</connectionStrings>
<system.data>
<DbProviderFactories>
<add name="SqlClient Data Provider" invariant="System.Data.SqlClient"
type="System.Data.SqlClient.SqlClientFactory" />
<add name="Odbc Data Provider" invariant="System.Data.Odbc"
type="System.Data.Odbc.OdbcFactory" />
<add name="OleDb Data Provider" invariant="System.Data.OleDb"
type="System.Data.OleDb.OleDbFactory" />
<add name="OracleClient Data Provider" invariant="System.Data.OracleClient"
type="System.Data.OracleClient.OracleClientFactory" />
</DbProviderFactories>
</system.data>
<system.web>
<compilation debug="true"/>
</system.web>
</configuration>
Этот шаг регистрации идентифицирует коллекцию фабрик с идентификатором name, значение которого удобно принять подобным имени поставщика. Конкретную фабрику можно прочитать из кода по ее идентификатору.
Обобщенный класс System.Data. имеет GetFactory(string), который возвращает объект конкретной фабрики, указанной в строке аргумента. Например, для случая явного указания в коде конкретной фабрики это будет выглядеть так
string factory = "System.Data.SqlClient";
System.Data.Common.DbProviderFactory provider =
System.Data.Common.DbProviderFactories.GetFactory(factory);
Получив конкретную фабрику из строки аргумента или provider одноименной фабрики, устанавливая соединение с Connection и используются методы объекта (например, provider ) фабрики CreateXXX(), которые возвращают эти объекты

Для создания независимого от поставщика универсального кода мы должны исходить из того, что не знаем заранее тип поставщика. Поэтому взаимодействовать с Connection, , , DataAdapter DbConnection, DbCommand, DbParameter, DbDataAdapter.
Чтобы лучше понять, как все вышесказанное работает, рассмотрим пример
Откорректируйте файл web.config, чтобы он окончательно выглядел следующим образом(рис ) Дополнение файла web.config для применения обобщенного кода<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="Northwind" connectionString="Data Source=localhost;
Initial Catalog=Northwind; user id=sa; password=;"/>
</connectionStrings>
<appSettings>
<add key="factory" value="System.Data.SqlClient" />
<add key="employeeQuery" value="SELECT * FROM Employees" />
</appSettings>
<system.web>
<compilation debug="true"/>
</system.web>
</configuration>
Создайте из копии TestDataReader.aspx новую страницу с именем TestFactory.aspx, назначьте ее стартовой и заполните следующим кодом(рис ) Страница TestFactory.aspx для реализации независимого от поставщика кода<%@ Page Language="C#" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
// Получить по ключу фабрику из файла web.config
string factory = System.Web.Configuration.
WebConfigurationManager.AppSettings["factory"];
System.Data.Common.DbProviderFactory provider =
System.Data.Common.DbProviderFactories.GetFactory(factory);
// Использовать фабрику для получения объекта соединения
// и извлечь строку соединения из файла web.config
System.Data.Common.DbConnection con =
provider.CreateConnection();
con.ConnectionString = System.Web.Configuration.
WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
// Создать базовый объект DbCommand. Для его настройки
// и извлечь по ключу команду из файла web.config
System.Data.Common.DbCommand cmd = provider.CreateCommand();
cmd.Connection = con;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = System.Web.Configuration.
WebConfigurationManager.AppSettings["employeeQuery"];
// Создаем ссылку на вспомогательную строку
System.Text.StringBuilder htmlStr = new StringBuilder("");
using (con)
{
// Открыли соединение
con.Open();
// Выполняем обобщенную команду и получаем результирующий набор данных
System.Data.Common.DbDataReader reader =
cmd.ExecuteReader();
// Перебираем все записи результирующего набора
// и строим HTML-строку
while (reader.Read())
{
htmlStr.Append("<li>Служащий ");
htmlStr.Append(reader["TitleOfCourtesy"]);
htmlStr.Append(" <b>");
htmlStr.Append(reader.GetString(1));
htmlStr.Append("</b>, ");
htmlStr.Append(reader.GetString(2));
htmlStr.Append(" - работает с ");
htmlStr.Append(reader.GetDateTime(6).ToString("d"));
htmlStr.Append("</li>");
}
// Закрываем набор данных
reader.Close();
}
// Выдаем результаты запроса
lblInfo.Text = htmlStr.ToString();
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="lblInfo" runat="server" Text="Label"></asp:Label></div>
</form>
</body>
</html>
Выполните страницу TestFactory.aspx. Должен получиться такой же результат, что и на странице TestDataReader.aspx, а именноЧтобы убедиться, что код получился действительно универсальным и настройка на конкретного поставщика осуществляется только в файле web.config, поменяем поставщика в этом файле без изменения кода самой страницы.
Откорректируйте файл web.config, чтобы он окончательно выглядел следующим образом(рис ) Изменение поставщика в файле web.config для проверки обобщенного кода<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="Northwind" connectionString="Provider=SQLOLEDB; Data Source=localhost;
Initial Catalog=Northwind; user id=sa; password=;"/>
</connectionStrings>
<appSettings>
<add key="factory" value="System.Data.OleDb" />
<add key="employeeQuery" value="SELECT * FROM Employees" />
</appSettings>
<system.web>
<compilation debug="true"/>
</system.web>
</configuration>
Исполните страницу TestFactory.aspx для нового поставщика, указанного в файле web.config, и убедитесь, что прежний код страницы генерирует тот же самый результат, но уже с новым поставщикомМеханизм применения фабрик поставщиков не решает всех проблем, связанных с разработкой независимого от поставщика кода. Например, не существует обобщенных объектов исключений базы данных, а только специфичные для конкретных поставщиков. Поставщики могут различаться некоторыми специфическими параметрами или поддерживать специальные средства, недоступные для общих
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.