Основы ASP.NET 2.0

         

это набор классов для работы


ADO .NET — это набор классов для работы с внешними данными. В новой версии .NET 2.0 он был расширен новыми свойствами и тоже получил номер 2.0.

Соединение в ADO.NET может происходить с помощью различных провайдеров. В настоящее время рекомендуется работать с помощью провайдера MS SQL или Oracle. Эти провайдеры сами написаны на управляемом коде .NET. Еще один провайдер, OLEDB, позволяет получить доступ к другим источникам данных — Access, Excel, MySql, SAP. Провайдер OLEDB написан на неуправляемом коде, но может работать вместе с .NET.

Классы ADO .NET объединены в несколько пространств имен.

System.Data — это ядро ADO .NET. Оно содержит классы, необходимые для связи посредством любых провайдеров данных. Эти классы представляют таблицы, строки, столбцы, DataSet (множество взаимосвязанных таблиц). Там определены интерфейсы (в смысле языка C#) соединений с базами данных, команд, адаптеров данных.

System.Data.Common — базовые классы для всех провайдеров данных — DbConnection, DbCommand, DbDataAdapter.

В System.Data.OleDb находятся классы, позволяющие работать с источниками данных OleDb, в том числе с MS SQL версии 6.0 и ниже. Там находятся такие классы, как OleDbConnection, OleDbDataAdapter и OleDbCommand.

System.Data.Odbc содержит классы, которые работают с источниками данных ODBC посредством провайдера .NET ODBC. Классы имеют аналогичные имена с префиксом Odbc.

System.Data.SqlClient. Здесь определен провайдер данных для СУБД SQL Server версии 7.0 и выше. Содержатся классы SqlConnection, SqlTransaction, SqlCommand и другие.

В System.Data.SqlTypes находятся классы, представляющие типы данных СУБД SQL Server.

Классы ADO .NET делятся на 3 типа. Классы типа Disconnected определяют базовую структуру данных, например, DataTable. Они независимы от каких-либо провайдеров данных и могут создаваться и заселяться данными непосредственно в программе. Классы Shared — базовые и общие для всех провайдеров. Классы Data Provider — специфические для разных провайдеров.


DataAdapter


DbDataAdapter является родительским классом для SqlDataAdapter, OleDbDataAdapter, OdbcDataAdapter. Этот класс содержит 4 объекта типа Command. Классы DataAdapter обеспечивают двусторонний обмен информацией.

SelectCommand — эта команда используется для выборки данных из базы. При этом класс DataTable заполняется данными.

UpdateCommand — обновляет данные (редактирование записей).

InsertCommand — добавление новых записей.

DeleteCommand — команда для удаления записей.

Метод Fill класса DbDataAdapter заполняет объекты DataSet или DataTable данными, прочитанными в результате выполнения команды SelectCommand. Эта команда должна быть запросом SQL типа Select. Если таблицы уже существуют, в него добавляются новые таблицы. Вообще метод Fill перегружен 8 раз. Например, DbDataAdapter.Fill Method (DataSet, String) добавляет в DataSet таблицу с именем, указанным во втором параметре. Если такая таблица уже есть, она обновляется. Доступ к таблице можно получить с помощью ее имени индексатором:

DataTable tblProducts = data.Tables["Products"];

Метод DbDataAdapter.Update записывает в базу данных все изменения, которые произошли в связанном с ним объекте DataSet.



DataSet


DataSet — это класс, содержащий в себе одну или несколько таблиц DataTable и связи между ними. Класс DataSet — это представление в памяти информации, считанной через ADO из баз данных или XML. Он позволяет манипулировать данными после отключения от источника данных.

Коллекция таблиц хранится в свойстве Tables, а отношений — в свойстве Relations.

Основываясь на таблицах DataSet, можно создавать представления — DataView.

Напишем страницу, в которой будут использоваться представленные классы.

База Northwind входит в комплект SDK. Ее можно установить на сервере, запустив командную строку SQLExpress:

sqlcmd -E -S (local)\SQLExpress -i InstNwnd.sql

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Работа с базой</title> </head> <body> <form id="Form1" runat="server"> <asp:DropDownList ID="DropDownList1" runat="server"> </asp:DropDownList> <asp:DataGrid id="DataGrid1" runat="server"></asp:DataGrid> </form> </body> </html>

Файл с кодом:

using System; using System.Data; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Data.SqlClient;


public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { String strConnect; String strCommand; strConnect = @"Data Source=.\SQLExpress;Initial Catalog=Northwind;Integrated Security=True"; SqlConnection myConn = new SqlConnection(strConnect); strCommand = "SELECT CategoryName, CategoryID FROM Categories"; SqlDataAdapter myData = new SqlDataAdapter(strCommand, myConn);

DataSet DataSet1 = new DataSet(); myData.Fill(DataSet1, "Categories"); strCommand = "SELECT ProductName, UnitPrice, CategoryID FROM Products"; myData.SelectCommand.CommandText = strCommand; myData.Fill(DataSet1, "Products"); DataSet1.Relations.Add(DataSet1.Tables[0].Columns["CategoryID"], DataSet1.Tables[1].Columns["CategoryID"]);

DataView myView = new DataView(DataSet1.Tables["Products"], "", "ProductName", DataViewRowState.CurrentRows); DataGrid1.DataSource = myView; DataGrid1.DataBind(); DropDownList1.DataSource = DataSet1.Tables[0]; DropDownList1.DataTextField = "CategoryName"; DropDownList1.DataValueField = "CategoryID"; DropDownList1.DataBind(); myConn.Close();



} }


Использование объекта Command


Объект Command исполняет запрос SQL, который может быть в форме встроенного текста, процедуры сервера или прямого доступа к таблице. Если это запрос на выборку данных SELECT, то данные обычно помещаются в DataSet или в DataReader. Методы и свойства определены в абстрактном классе DbCommand (через интерфейс IDbCommand), и их реализуют частные ненаследуемые классы OleDbCommand, SqlCommand, OdbcCommand.

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

Свойство CommandText хранит текст запроса SQL или имя серверной процедуры.

CommandTimeout задает время ожидания ответа, по умолчанию равное 30 секундам. Если команда не выполнится в течение этого времени, будет выброшено исключение.

Процедуры сервера нуждаются в параметрах. Они хранятся в коллекции Parameters и имеют тип SqlParameter. Текстовые команды также могут получать параметры, перед которыми ставится префикс @. Например:

" SELECT * FROM CUSTOMERS WHERE CITY = @CITY AND CONTACTNAME = @CONTACT "

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


Она возвращает количество измененных записей в том случае, если выполняются команды Select, Update, Delete.

ExecuteScalar возвращает результат запроса в случае, если это одно-единственное значение. Например, нужно узнать количество заказов конкретного покупателя. Запрос выполняется с помощью команды "Select count * where customerid=1". Ее результат — выборка из одной строки и одного столбца. Ее можно выполнить и с помощью метода ExecuteReader, но ExecuteScalar будет выполняться быстрее. Если запрос возвратит большее количество строк или столбцов, они будут проигнорированы.

ExecuteRow возвращает единственную запись.

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

В результате выполнения метода ExecuteReader объекта Command создается объект DataReader. Всегда закрывайте соединения после использования, иначе оно останется активным и будет занимать ресурсы. Это можно сделать двумя способами. Первый — вызвать перегруженный метод ExecuteReader, который принимает параметр типа CommandBehavior со значением CommandBehavior.CloseConnection. В таком случае необходимо перелистать полученную выборку от начала до конца, и соединение закроется, когда будет достигнут конец. Если вы не хотите прочитать все данные, можете самостоятельно закрыть соединение методом Close:

public void CreateMySqlDataReader(string mySelectQuery, string myConnectionString) { SqlConnection myConnection = new SqlConnection(myConnectionString); SqlCommand myCommand = new SqlCommand(mySelectQuery, myConnection); myCommand.CommandType = CommandType.Text; myCommand.Connection.Open(); SqlDataReader myReader = myCommand.ExecuteReader(CommandBehavior.CloseConnection); while (myReader.Read()) { Response.Write(myReader.GetString(0) + "<br>"); } myReader.Close(); myConnection.Close(); }



Развитые СУБД (теперь и MS Access) поддерживают транзакции. Транзакция — это последовательность команд, которая выполняется как одно целое. Например, при переводе денег сумма вычитается с одного счета и добавляется к другому. Если произойдет только одна из этих операций, банк или его клиенты понесут потери, поэтому важно, чтобы произошли обе операции либо ни одна не произошла. Если на одном из этапов транзакции допущена ошибка, происходит откат (Rollback), то есть отменяются все ранее сделанные операции и база возвращается к состоянию до начала транзакции. Если все успешно, транзакция подтверждается операцией Commit.

Для поддержки транзакций введен класс SqlTransaction и ему подобные. У объекта Command есть свойство Transaction. Метод BeginTransaction объекта Connection заставляет базу данных перейти в режим транзакции.

Кроме того, необходимо всегда заключать программный код, работающий с базами данных, в блоки try/catch, так как работа часто идет с удаленными серверами и могут происходить самые разные ошибки как в сети, так и при работе самого сервера.

При этом выбрасывается исключение SqlException или OleDbException:

public void RunTransaction(string[] Queries, string myConnectionString) { SqlConnection conn = null; SqlTransaction trans = null; try { conn = new SqlConnection(myConnectionString); conn.Open();

trans = conn.BeginTransaction();

SqlCommand cmd = new SqlCommand(); cmd.Connection = conn; cmd.Transaction = trans; foreach (string Query in Queries) { cmd.CommandText = Query; cmd.ExecuteNonQuery(); } trans.Commit(); } catch (SqlException SqlEx) { if (trans != null) { trans.Rollback(); }

throw new Exception("An error occurred while transaction", SqlEx); return; } finally { if (conn != null) { conn.Close(); } } }


Объект Connection


Объект Connection для соединения с базой данных нуждается в строке соединения для указания пути к СУБД и входа в систему. Свойства класса Connection показаны в таблице. OleDbConnection, SqlConnection, OdbcConnection — наследники класса Connection, специфические для провайдеров OleDb, MS SQL ODBC соответственно.

СвойствоОписание
DataSourceПуть к базе данных в файловой системе при использовании Oledb, имя экземпляра базы сервера при использовании SqlConnection
DatabaseВозвращает имя базы данных, используемой в объекте Connection после открытия
StateВозвращает текущее состояние соединения. Возможные значения — Broken, Closed, Connecting, Executing, Fetching и Open
ConnectionStringСтрока соединения с СУБД

Все свойства, кроме ConnectionString, — только для чтения.



Окно внешних источников данных


В Visual Studio 2005 существует 3 вкладки просмотра проектов: Solution Explorer, Class Explorer, Server Explorer. Первыми двумя активно пользовались все, кто писал программы на C# (или другом языке), а третий по умолчанию не виден, откройте его из меню View

—> Server Explorer. Это окно позволяет работать с соединениями баз данных, просматривать статистику работы сервера (в VWD Express нет пункта Servers).


Рис. 6.1. 

Соединение можно установить как с MS SQL, так и с файлом Access и любым источником ODBC, а также Oracle. Можно также создать новую базу данных MS SQL.


Рис. 6.2. 

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

В лекции 3 мы создали страницы с голосованиями, но никак не обрабатывали результаты. Сейчас мы будем сохранять результаты голосования.

1. Вначале надо создать базу данных. В окне Server Explorer нажмите правой клавишей мыши на пункт Data Connections, в контекстном меню выберите Create New Sql Database.


Рис. 6.3. 

.\SQLExpress равносильно (local) SQLExpress и означает сервер на локальной машине. Можно подключиться к серверу и по сети. Мы создали базу данных Polls, которая находится на сервере. Можно создать базу данных в отдельном файле .mdf, тогда ее можно будет переносить на другой компьютер.

2. Создание таблицы. Это можно сделать и программно, и через окно Server Explorer. Там уже появился узел созданной базы Polls.dbo. Раскройте его, правой клавишей мыши кликните на пункт Tables, в контекстном меню выберите Add New Table.


Заполните следующие значения:

Column NameData TypeAllow Nulls
Idint+
variantnvarchar(100)
voicesint
Поле id создается как уникальный идентификатор варианта, и оно должно быть первичным ключом таблицы. Кликните мышью на первой строке и выберите пункт Set Primary Key. Поле voices (количество голосов) при создании должно быть равно 0. В Columns Properties найдите строчку Default Value or Binding и впишите значение 0.

Создание той же самой таблицы в программном режиме:

protected void Page_Load(object sender, EventArgs e) { String strCreateTable = "create table poll(id int NOT NULL PRIMARY KEY, variant nvarchar(100), voices int DEFAULT 0)"; CreateSqlTable(strCreateTable, "Data Source=.\\SQLEXPRESS;Initial Catalog=Polls;Integrated Security=True"); }

public void CreateSqlTable(string SqlQuery, string myConnectionString) { SqlConnection myConnection = new SqlConnection(myConnectionString); SqlCommand myCommand = new SqlCommand(SqlQuery, myConnection); myCommand.Connection.Open(); myCommand.ExecuteNonQuery(); myConnection.Close(); }

3. Заполнение таблицы вариантами. Кликните мышью на таблице и выберите пункт Show Table Data. Значения id не должны повторяться.


Рис. 6.4. 

4. Создание серверной процедуры. При каждом голосовании значение поля voices одной из записей таблицы, соответствующей нужному пункту, должно возрастать. Это удобнее сделать с помощью процедуры, которая принимает аргумент id и обновляет нужное поле. Кликните мышью на узел Stored Procedures и выберите пункт Add New Stored Procedure:



CREATE PROCEDURE dbo.CountVote ( @choiceid int = 0 )

AS DECLARE @Count INT SELECT @Count = voices FROM poll WHERE id=@choiceid UPDATE poll SET voices=@Count+1 WHERE id=@choiceid RETURN

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

Оператор SELECT извлекает записи из таблицы poll, которые соответствуют условию после ключевого слова WHERE. Так как id — ключевое (уникальное) поле и выбирается одно поле voices, возвращается одно значение, которое можно записать в переменную. Затем в операторе UPDATE изменяется на увеличенное значение переменной @Count.

5. Привязка к данным. На новой странице создайте элементы Button и RadioButtonList (можно и CheckBoxList) перетаскиванием из Toolbox. На RadioButtonList имеется стрелка, открывающая Smart Tag. С помощью него можно сконфигурировать соединение с нужной таблицей или внести значения вручную. При нажатии на Configure Data Source появится мастер соединений. Выберите New Data Source. На втором шаге мастер предложит выбрать тип источника. Выберите Database. На следующем шаге из выпадающего списка выберите .\sqlexpress.Polls.dbo. На четвертом шаге мастер предложит сохранить строку соединения в конфигурационном файле. Сохраним, она может понадобиться. Для заполнения переключателей необходимы 2 поля: в variant содержится текст варианта, который будет виден в форме, а в id — номер варианта, который связан с DataValueField списка переключателей и будет передаваться в процедуру сервера как параметр.




Рис. 6.5. 

При желании на этом шаге можно отсортировать значения, например, по алфавитному порядку текстов нажатием на кнопку ORDER BY. На предпоследнем шаге можно протестировать полученный запрос, и если все в порядке, то на последнем шаге ставим variant как источник для показа и id для значений. На странице должно получиться примерно следующее:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Voting.aspx.cs" Inherits="Voting" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" > <head> <title>Голосование для программистов</title> </head> <body> <br /><br /> Какой язык программирования Вы предпочитаете?<br /> <form runat="server" id="voting"> <asp:RadioButtonList ID="RadioButtonList1" runat="server" DataSourceID="SqlDataSource1" DataTextField="variant" DataValueField="id"> </asp:RadioButtonList> <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:PollsConnectionString %>" SelectCommand="SELECT [id], [variant] FROM [poll] ORDER BY [variant] "></asp:SqlDataSource> <br /> <br /> <asp:Button ID="Button1" runat="server" Text="Button" /><br /> <br /> <br /><br /> </form> <asp:Label id="Message" runat="server" /> </body> </html>



6. Обработка результатов. Процедуру необходимо вызвать с параметром, взятым из свойства Value группы переключателей. Свойство Parameters SqlCommand является коллекцией, в данном случае в нее надо добавить один элемент:

public partial class Voting : System.Web.UI.Page { public void ExecuteStoredProcedure(string ProcedureName, string myConnectionString, int id) { SqlConnection myConnection = new SqlConnection(myConnectionString); SqlCommand myCommand = new SqlCommand(ProcedureName, myConnection); myCommand.CommandType = CommandType.StoredProcedure; SqlParameter myParm = myCommand.Parameters.Add("@choiceid", SqlDbType.Int, 4); myParm.Value = id; myCommand.Connection.Open(); myCommand.ExecuteNonQuery();

myConnection.Close(); }

protected void Page_Load(object sender, EventArgs e) { String strConnection = "Data Source=.\\SQLEXPRESS;Initial Catalog=Polls;Integrated Security=True"; if (Page.IsPostBack) { String strProc="CountVote"; Message.Text = RadioButtonList1.SelectedValue; ExecuteStoredProcedure(strProc, strConnection, Int32.Parse(RadioButtonList1.SelectedValue) ); } } }

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


Программирование ADO .NET


Все провайдеры данных содержат классы соединений, адаптеров, команд. Схема типичной программы в ADO .NET следующая:

1. Вначале создается соединение с базой данных — класс Connection, который обеспечивается необходимой информацией — строкой соединения.

2. Создается объект Command и задается команда, которую необходимо выполнить в данной СУБД. Эта команда может быть запросом SQL или исполняемой процедурой. Нужно задать параметры этой команды, если они имеются.

3. Если команда не возвращает данных, она просто выполняется с помощью одного из методов Execute. Например, это может быть удаление или обновление данных таблицы.

4. Если команда возвращает выборку данных, их необходимо куда-то поместить. Решите, нужно ли вам получить данные для последующего использования без связи с базой данных или же нужно просто быстро выполнить команду. В первом случае нужно создать класс DataAdapter и с его помощью сохранить данные в DataSet или в DataTable. Во втором случае создается класс DataReader, который требует сохранять соединение на все время работы, хранит выборку только для чтения и позволяет двигаться только вперед. Зато чтение с помощью DataReader выполняется в несколько раз быстрее, чем в DataAdapter.

5. Задать полученный DataSet или DataReader как источник данных элемента управления или вывести их на страницу другим способом.



настолько обширная тема, что ее


Работа с базами данных в ASP .NET — настолько обширная тема, что ее невозможно охватить в одной лекции. Хотя классы ADO .NET инкапсулированы в более удобные классы источников данных, их необходимо знать, чтобы лучше понять новую модель, принятую в ASP .NET 2.0.