Использование баз данных в приложениях ASP.NET
При создании методов, извлекающих данные из БД, необходимо учитывать, как и для чего будут применяться данные методы. Дело в том, что может существовать множество различных потребностей в извлекаемых из БД данных. Отличия в них сводятся к представлению извлеченных данных в различных форматах. Например, если необходимо организовывать вывод информации с применением таких элементов, как GridView, удобнее всего сделать так, чтобы метод вернул такую структуру, которую можно использовать для привязки данных к этому элементу. К таким структурам, как уже было сказано выше, относятся DataReader, DataTable, DataSet и т. д. Если нужно извлекать данные об одном товаре, необходимо, чтобы метод возвращал объект Product, если же желательно, чтобы метод извлекал список существующих товаров, представленных в БД и необходимых для их обработки, удобнее, чтобы метод вернул массив объектов Product. В реальных приложениях может потребоваться несколько методов, предназначенных для реализации различных режимов работы с БД. Например, для отображения вызывается один метод, извлекающий данные из БД, а для их редактирования - другой.
Создадим метод GetProductsTable(), предназначенный для извлечения данных о товарах из таблицы "Товары" и помещения их в объект DataTable. Исходный код этого метода приведен ниже.
public DataTable GetProductsTable() { SqlConnection con = new SqlConnection(connectionString); string query = "SELECT КодТовара,НаименованиеТовара,Цена FROM Товары"; SqlCommand cmd = new SqlCommand(query, con); cmd.CommandType = CommandType.Text; SqlDataAdapter da = new SqlDataAdapter(query, con); DataTable dt = new DataTable("Product"); try { con.Open(); da.Fill(dt); return dt; } catch (SqlException e) { throw new ApplicationException("Ошибка чтения списка товаров из таблицы Товары"); } finally { con.Close(); } }
В дальнейшем полученный в результате такой операции результат может быть использован для отображения данных в объекте GridView.
В качестве примера создадим также метод для извлечения данных из таблицы "Товары" и предоставления вызывающей программе объекта DataReader, с помощью которого осуществляется чтение данных из БД. Исходный код метода GetProductsReader(), реализующего данную операцию, представлен ниже.
public SqlDataReader GetProductsReader() { SqlConnection con = new SqlConnection(connectionString); string query = "SELECT КодТовара,НаименованиеТовара,Цена FROM Товары"; SqlCommand cmd = new SqlCommand(query, con); cmd.CommandType = CommandType.Text; try { con.Open(); return cmd.ExecuteReader(); } catch (SqlException e) { throw new ApplicationException("Ошибка чтения списка товаров из таблицы Товары"); } }
Особенностью метода является то, что в нем открывается соединение с базой данных, но не закрывается. Это объясняется тем, что основная программа, которой передается объект DataReader, должна осуществлять чтение данных из него, а для этого необходимо открытое соединение.
Описанные выше методы позволяют получать данные о товарах из БД в формате, лучше всего приспособленном для их отображения на экране, но плохо подходящем для редактирования данных. Для получения данных о товарах в удобном для программной работы с ними формате создадим еще два метода. Первый предназначен для извлечения из БД информации об одном товаре и передаче ее в вызывающую программу в виде объекта Product. Второй - для формирования массива объектов Product. Оба метода используют объект DataReader для чтения информации из БД.
public Product GetProduct(int productID) { SqlConnection con = new SqlConnection(connectionString); string query = "SELECT КодТовара,НаименованиеТовара,Цена FROM Товары WHERE КодТовара=@ID"; SqlCommand cmd = new SqlCommand(query, con); cmd.CommandType = CommandType.Text; cmd.Parameters.Add("@ID", SqlDbType.Int, 4); cmd.Parameters["@ID"].Value = productID; try { con.Open(); SqlDataReader rdr = cmd.ExecuteReader (CommandBehavior.SingleRow); rdr.Read(); int pID=(int)rdr["КодТовара"]; string pName = rdr["НаименованиеТовара"].ToString(); double pCost = Convert. ToDouble(rdr["Цена"]); Product prod = new Product(pID,pName,pCost); rdr.Close(); return prod; } catch (SqlException e) { throw new ApplicationException("Ошибка извлечения товара из таблицы Товары"); } finally { con.Close(); } } public List<Product> GetProducts() { SqlConnection con = new SqlConnection(connectionString); string query = "SELECT КодТовара,НаименованиеТовара,Цена FROM Товары"; SqlCommand cmd = new SqlCommand(query, con); cmd.CommandType = CommandType.Text; List<Product> products = new List<Product>(); try { con.Open(); SqlDataReader rdr = cmd.ExecuteReader(); while (rdr.Read()) { Product prod = new Product((int) rdr["КодТовара"], rdr["Наименование Товара"].ToString(), (double) rdr["Цена"]); products.Add(prod); } rdr.Close(); return products; } catch (SqlException e) { throw new ApplicationException("Ошибка извлечения списка товаров из таблицы Товары"); } finally { con.Close(); } }
Для демонстрации использования созданного слоя доступа к данным поместим на форму объект ProductsView класса GridView, а также создадим следующий код, использующий возможности класса ProductsDB.
protected void Page_Load(object sender, EventArgs e) { //Создание нового объекта доступа к данным ProductsDB prodDB = new ProductsDB("TEST_DB"); if (!Page.IsPostBack) { //Создание нового объекта Product Product prod = new Product(31, "Товар31", 99); //Добавление созданного объекта Product в таблицу БД prodDB.AddProduct(prod); //Получение из БД информации о товаре с кодом 5 Product product = prodDB.GetProduct(5); //Изменение наименования полученного товара product.ProductName = "Товар1000"; //Изменение цены полученного товара product.ProductCost = 10.5; //Обновление информации о товаре в БД prodDB.UpdateProduct(product); //Получение массива объектов Product. Количество объектов //равно количеству записей в таблице Товары List<Product> products = prodDB.GetProducts(); } //Установить источник данных и осуществить их привязку //для элемента GridView ProductsView.DataSource = prodDB.GetProductsTable(); Page.DataBind(); }
Для демонстрации возможности удаления данных из таблицы "Товары" поместим на форму кнопку и создадим следующий обработчик события нажатия на нее:
protected void Button1_Click(object sender, EventArgs e) { ProductsDB prodDB = new ProductsDB("TEST_DB"); prodDB.DeleteProduct(31); ProductsView.DataSource = prodDB.GetProductsReader(); Page.DataBind(); }
В этом методе вновь осуществляется установка источника данных для объекта GridView, а также их привязка. Это необходимо для того, чтобы отобразить изменения в данных, произведенных ранее выполненными действиями.
Результат работы программы представлен на рис. 10.30.
Использование объекта ObjectDataSource
Создание пользовательского кода, реализующего возможность взаимодействия с базой данных, — достаточно трудоемкий процесс. К тому же такой способ позволяет связывать визуальные элементы с данными только в программном коде. Для получения возможности создания такой связи в режиме редактирования страницы можно использовать объект ObjectDataSource. Этот объект позволяет создавать связь между элементами управления, расположенными на Web-странице, и компонентами доступа к данным, реализованным в виде пользовательских классов. Но для этого необходимо, чтобы пользовательский класс доступа к данным подчинялся следующим правилам:
- Он не должен сохранять состояние.
- Он должен иметь конструктор по умолчанию, без аргументов.
- Вся логика должна быть сосредоточена в единственном классе.
- Он не должен содержать статических методов, предназначенных для извлечения и обновления записей.
- Он должен предоставлять результаты запроса при вызове единственного метода.
- Результатом запроса должна быть одна или несколько записей, которые могут быть представлены в виде коллекции, массива либо спискового объекта. Главное, чтобы он реализовывал интерфейс IEnumerable.
Использование ObjetDataSource в ряде случаев бывает гораздо удобнее применения таких элементов доступа к данным, как SqlDataSource или AccessDataSource. Скажем, в предыдущем примере для доступа к таблице "Товары" можно воспользоваться пользовательским классом и объектом ObjectDataSource. Для этого необходимо выполнить нижеследующие шаги.
Поместить на форму объект ObjectDataSource, перетащив его с панели Toolbox, после чего прикрепить к нему класс, отвечающий за извлечение данных, - в нашем случае это ProductsDB. Для этого достаточно установить значение свойства TypeName равным ProductsDB. После этого необходимо определить свойства SelectMethod, DeleteMethod, UpdateMethod и InsertMethod, используемые для выполнения соответствующих операций над данными в БД. В качестве значений этих свойств нужно установить имя метода, выполняющего соответствующие операции. Так, в качестве значения свойства SelectMethod в данном примере установим имя метода, извлекающего данные из БД, - GetProducts. Этот метод удовлетворяет всем критериям ObjectDataSource - он возвращает объект, представляющий все данные через общедоступные свойства. Имена этих свойств и будут использованы в качестве имен столбцов при выводе информации на экран в табличной форме. После того как связь ObjectDataSource с классом, извлекающим данные из базы данных, установлена, необходимо добавить на Web-форму элемент GridView и связать их, установив в свойстве DataSourceID этого элемента имя объекта ObjectDataSource. Определение ObjectDataSource и GridView тогда будет выглядеть следующим образом:
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="GetProducts" TypeName="ProductsDB"></asp:ObjectDataSource> <br /> <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="ObjectDataSource1"> <Columns> <asp:BoundField DataField="ProductID" HeaderText="ProductID" SortExpression="ProductID" /> <asp:BoundField DataField="ProductName" HeaderText="ProductName" SortExpression="ProductName" /> <asp:BoundField DataField="ProductCost" HeaderText="ProductCost" SortExpression="ProductCost" /> </Columns> </asp:GridView>
Результат работы программы показан на рис. 10.31.
Как видно из этого примера, с точки зрения пользователя использование ObjectDataSource и принципов трехуровневой архитектуры построения приложений доступа к данным аналогично использованию объекта SqlDataSource. Однако это сходство скрывает целый ряд деталей, сильно отличающих принципы построения трехуровневых приложений от обычной архитектуры. Наиболее значимый эффект от использования этих принципов заключается в том, что при трехуровневой архитектуре организации доступа к данным Web-страница не содержит никакого кода SQL. Вместо этого вся работа выполняется классом ProductsDB. За счет этого приложение оказывается более гибким и легко модифицируемым при необходимости изменения механизмов доступа к данным. Более подробную информацию об использовании принципов построения трехуровневой архитектуры доступа к данным в приложении можно найти в справочной системе MSDN, а также в [ 1 ] .