Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом руководстве мы начнем с самого начала и создадим уровень доступа к данным (DAL), используя типизированные наборы данных для доступа к данным в базе данных.
Введение
Как веб-разработчики, наша жизнь вращается вокруг работы с данными. Мы создаем базы данных для хранения данных, код для их извлечения и изменения, а веб-страницы для их сбора и обобщения. Это первое руководство в длинной серии, которая изучит методы реализации этих распространенных шаблонов в ASP.NET 2.0. Начнем с создания архитектуры программного обеспечения, состоящей из уровня доступа к данным (DAL) с помощью типизированных наборов данных, уровня бизнес-логики (BLL), который применяет пользовательские бизнес-правила, и слой презентации, состоящий из ASP.NET страниц, которые совместно используют общий макет страницы. После того как эта серверная основа будет создана, мы перейдем в отчеты, показывая, как отображать, суммировать, собирать и проверять данные из веб-приложения. Эти учебники предназначены для того, чтобы быть краткими и предоставить пошаговые инструкции с большим количеством снимков экрана, чтобы провести процесс визуально. Каждое руководство доступно в версиях C# и Visual Basic и включает скачивание всего используемого кода. (Это первое руководство довольно длинное, но остальные представлены в гораздо более дайджестируемых фрагментах.)
В этих руководствах мы будем использовать версию Microsoft SQL Server 2005 Express Edition базы данных Northwind, размещенную в каталоге App_Data . Помимо файла базы данных, папка App_Data также содержит скрипты SQL для создания базы данных, если вы хотите использовать другую версию базы данных. Если вы используете другую версию базы данных Northwind, необходимо обновить параметр NORTHWNDConnectionString в файле web.config приложения. Веб-приложение было создано с помощью Visual Studio 2005 Professional Edition в качестве проекта веб-сайта на основе файловой системы. Однако все учебники будут работать одинаково хорошо с бесплатной версией Visual Studio 2005, Visual Web Developer.
В этом руководстве мы начнем с самого начала и создадим уровень доступа к данным (DAL), а затем создадим уровень бизнес-логики (BLL) во втором руководстве, а также работаем над макетом страницы и навигацией в третьем. Учебники после третьего будут опираться на основу, заложенную в первых трех. У нас много чего нужно изучить в этом первом уроке, так что запустите Visual Studio и начнем!
Шаг 1. Создание веб-проекта и подключение к базе данных
Прежде чем создать уровень доступа к данным (DAL), сначала необходимо создать веб-сайт и настроить базу данных. Начните с создания нового веб-сайта на основе файловой системы ASP.NET. Для этого перейдите в меню "Файл" и выберите "Новый веб-сайт", отображая диалоговое окно "Новый веб-сайт". Выберите шаблон веб-сайта ASP.NET, установите раскрывающийся список "Расположение" в файловой системе, выберите папку для размещения веб-сайта и задайте язык на C#.
Рис. 1. Создание веб-сайта на основе файловой системы (щелкните, чтобы просмотреть изображение полного размера)
При этом будет создан новый веб-сайт с Default.aspx ASP.NET страницей и папкой App_Data.
После создания веб-сайта необходимо добавить ссылку на базу данных в обозревателе серверов Visual Studio. Добавив базу данных в обозреватель серверов, вы можете добавлять таблицы, хранимые процедуры, представления и т. д. из Visual Studio. Вы также можете просматривать данные таблицы или создавать собственные запросы вручную или графически с помощью построителя запросов. Кроме того, при сборке типизированных наборов данных для DAL необходимо указать Visual Studio на базу данных, на основе которой будут создаваться типизированные наборы данных. Хотя мы можем предоставить эти сведения о подключении на данный момент времени, Visual Studio автоматически заполняет раскрывающийся список баз данных, уже зарегистрированных в обозревателе серверов.
Действия по добавлению базы данных Northwind в обозреватель серверов зависят от того, следует ли использовать базу данных SQL Server 2005 Express Edition в папке App_Data, или у вас настроен сервер базы данных Microsoft SQL Server 2000 или 2005, который вы хотите использовать.
Использование базы данных в папке App_Data
Если у вас нет сервера базы данных SQL Server 2000 или 2005 для подключения, или если вы просто хотите избежать необходимости добавлять базу данных на сервер базы данных, можно использовать версию SQL Server 2005 Express Edition базы данных Northwind, расположенную в папке App_Data веб-сайта (NORTHWND.MDF).
База данных, размещенная в папке App_Data, автоматически добавляется в обозреватель серверов. Предположим, что на вашем компьютере установлена редакция SQL Server 2005 Express Edition; в обозревателе серверов должен появиться узел с именем NORTHWND.MDF, который можно развернуть, чтобы просмотреть таблицы, представления, хранимые процедуры и т. д. (см. рис. 2).
Папка App_Data также может содержать файлы Microsoft Access .mdb , которые, как и их коллеги SQL Server, автоматически добавляются в обозреватель серверов. Если вы не хотите использовать какие-либо параметры SQL Server, вы всегда можете установить базу данных и приложения Northwind Traders и перейти в каталог App_Data . Имейте в виду, однако, что базы данных Access не являются столь же функциональными, как SQL Server, и не предназначены для использования в сценариях веб-сайта. Кроме того, некоторые из более чем 35 руководств будут использовать функции уровня базы данных, которые не поддерживаются в "Access".
Подключение к базе данных в Сервере базы данных Microsoft SQL Server 2000 или 2005
Кроме того, вы можете подключиться к базе данных Northwind, установленной на сервере базы данных. Если на сервере базы данных еще не установлена база данных Northwind, сначала необходимо добавить ее на сервер, выполнив скрипт установки, включенный в скачанный пакет этого руководства.
После установки базы данных перейдите в обозреватель серверов в Visual Studio, щелкните правой кнопкой мыши узел "Подключения к данным" и нажмите кнопку "Добавить подключение". Если обозреватель сервера не отображается, перейдите в представление или обозреватель сервера или нажмите клавиши CTRL+ALT+S. Откроется диалоговое окно "Добавление подключения", где можно указать сервер для подключения, сведения о проверке подлинности и имя базы данных. После успешной настройки сведений о подключении к базе данных и нажатия кнопки "ОК" база данных будет добавлена в качестве узла под узлом подключения к данным. Узел базы данных можно развернуть для изучения таблиц, представлений, хранимых процедур и т. д.
Рис. 2: Добавьте подключение к базе данных Northwind вашего сервера базы данных
Шаг 2. Создание уровня доступа к данным
При работе с данными одним из вариантов является внедрение логики для конкретных данных непосредственно в слой презентации (в веб-приложении ASP.NET страницы составляют слой презентации). Это может иметь форму написания кода ADO.NET в части кода ASP.NET страницы или с помощью элемента управления SqlDataSource из части разметки. В любом случае этот подход тесно связывает логику доступа к данным с уровнем представления. Однако рекомендуемый подход заключается в том, чтобы разделить логику доступа к данным от слоя презентации. Этот отдельный слой называется уровнем доступа к данным, сокращённо DAL, и обычно реализуется как отдельный проект классовой библиотеки. Преимущества этой многоуровневой архитектуры хорошо описаны (см. раздел "Дополнительные чтения" в конце этого руководства, чтобы получить информацию об этих преимуществах) и является подходом, который мы рассмотрим в этой серии.
Весь код, зависящий от базового источника данных, например создание подключения к базе данных, выдача команд SELECT, INSERT, UPDATE и DELETE и т. д. должна находиться в DAL. Слой презентации не должен содержать ссылки на такой код доступа к данным, но вместо этого следует выполнять вызовы в DAL для всех и всех запросов данных. Уровни доступа к данным обычно содержат методы для доступа к базовым данным базы данных. База данных Northwind, например, содержит таблицы "Продукты и категории ", которые записывают продукты для продажи и категории, к которым они относятся. В нашем DAL у нас будут такие методы:
- GetCategories(), который возвращает сведения обо всех категориях
- GetProducts(), который возвращает сведения обо всех продуктах
- GetProductsByCategoryID(categoryID), который возвращает все продукты, принадлежащие указанной категории.
- GetProductByProductID(productID), который возвращает сведения о конкретном продукте.
Эти методы при вызове подключаются к базе данных, выдают соответствующий запрос и возвращают результаты. Важно то, как мы возвращаем эти результаты. Эти методы могут просто возвращать набор данных или DataReader, заполненный запросом базы данных, но в идеале эти результаты должны быть возвращены с помощью строго типизированных объектов. Строго типизированный объект — это объект, схема которого жестко определена во время компиляции, в то время как наоборот, слабо типизированный объект, является одним, схема которого не известна до времени выполнения.
Например, DataReader и DataSet (по умолчанию) являются слабо типизированными объектами, так как их схема определяется столбцами, возвращаемыми запросом базы данных, используемым для их заполнения. Чтобы получить доступ к конкретному столбцу из свободно типизированной DataTable, необходимо использовать синтаксис DataTable.Rows[index]["columnName"]. Свободная типизация в DataTable в этом примере проявляется в том, что необходимо обращаться к имени столбца с помощью строкового или числового индекса. С другой стороны, строго типизированный DataTable будет иметь каждый столбец, реализованный как свойство, что приводит к тому, что код выглядит следующим образом: DataTable.Rows[индекс].columnName.
Чтобы возвращать строго типизированные объекты, разработчики могут создавать собственные бизнес-объекты или использовать типизированные наборы данных. Бизнес-объект реализуется разработчиком как класс, свойства которого обычно отражают столбцы базовой таблицы базы данных, представляющей бизнес-объект. Типизированный набор данных — это класс, созданный Visual Studio на основе схемы базы данных и члены которого строго типизированы в соответствии с этой схемой. Типизированный набор данных состоит из классов, расширяющих классы ADO.NET DataSet, DataTable и DataRow. Помимо строго типизированных таблиц DataTable, Типизированные наборы данных теперь также включают TableAdapters, которые являются классами с методами для заполнения таблиц DataTables и распространения изменений в DataTables обратно в базу данных.
Примечание.
Дополнительные сведения о преимуществах и недостатках использования типизированных наборов данных и пользовательских бизнес-объектов см. в статье "Проектирование компонентов уровня данных" и передача данных по уровням.
Мы будем использовать строго типизированные наборы данных для архитектуры этих учебников. На рисунке 3 показан рабочий процесс между различными уровнями приложения, использующего типизированные наборы данных.
Рис. 3. Весь код доступа к данным переключен на DAL (щелкните, чтобы просмотреть полноразмерное изображение)
Создание типизированного набора данных и адаптера таблицы
Чтобы начать создание DAL, мы начнем с добавления типизированного набора данных в наш проект. Для этого щелкните правой кнопкой мыши на узле проекта в Обозревателе решений и выберите "Добавить новый элемент". Выберите параметр DataSet из списка шаблонов и назовите его Northwind.xsd.
Рис. 4. Выберите добавить новый набор данных в проект (щелкните, чтобы просмотреть изображение полного размера)
После нажатия кнопки "Добавить" при появлении запроса на добавление набора данных в папку App_Code нажмите кнопку "Да". Затем откроется конструктор для типизированного набора данных, и откроется мастер настройки TableAdapter, позволяющий добавить первый tableAdapter в типизированный набор данных.
Типизированный набор данных служит строго типизированной коллекцией данных; Он состоит из строго типизированных экземпляров DataTable, каждый из которых, в свою очередь, состоит из строго типизированных экземпляров DataRow. Мы создадим строго типизированный набор данных для каждой из базовых таблиц базы данных, с которыми мы должны работать в этой серии руководств. Начнем с создания DataTable для таблицы Products.
Помните, что строго типизированные таблицы DataTable не содержат никаких сведений о том, как получить доступ к данным из базовой таблицы базы данных. Чтобы получить данные для заполнения DataTable, мы используем класс TableAdapter, который функционирует как наш уровень доступа к данным. Для нашей таблицы данных Products TableAdapter будет содержать методы GetProducts(), GetProductByCategoryID(categoryID) и т. д., которые мы будем вызывать с уровня представления. Роль DataTable заключается в том, чтобы служить строго типизированными объектами, используемыми для передачи данных между слоями.
Мастер настройки TableAdapter начинается с запроса на выбор базы данных для работы. В раскрывающемся списке показаны эти базы данных в обозревателе серверов. Если база данных Northwind не добавлена в обозреватель серверов, вы можете нажать кнопку "Создать подключение" в это время.
Рис. 5. Выберите базу данных Northwind из раскрывающегося списка (щелкните, чтобы просмотреть изображение полного размера)
После выбора базы данных и нажатия кнопки "Далее" вам будет предложено сохранить строка подключения в файле web.config. Сохранив строку подключения, вы избежите его жесткого кодирования в классах TableAdapter, что упростит работу, если информация о строке подключения изменится в будущем. Если вы решили сохранить строку подключения в файле конфигурации, она помещается в <раздел connectionStrings>, который может быть при необходимости зашифрован для повышения безопасности или изменён позже с помощью новой страницы свойств ASP.NET 2.0 в средстве администрирования IIS GUI, что является более идеальным для администраторов.
Рис. 6. Сохранение строки подключения к Web.config (щелкните, чтобы просмотреть изображение полного размера)
Затем необходимо определить схему для первой строго типизированной таблицы DataTable и предоставить первый метод для нашего TableAdapter, который будет использоваться при заполнении строго типизированного набора данных. Эти два шага выполняются одновременно путем создания запроса, который возвращает столбцы из таблицы, которую мы хотим отразить в таблице DataTable. В конце работы мастера мы дадим этому запросу имя метода. После этого этот метод можно вызвать из нашего слоя презентации. Метод выполнит определенный запрос и заполняет строго типизированный dataTable.
Чтобы приступить к определению SQL-запроса, сначала необходимо указать, как нужно, чтобы TableAdapter выдает запрос. Мы можем использовать разовый SQL-запрос, создать новую хранимую процедуру или использовать существующую хранимую процедуру. В этих руководствах мы будем использовать временные SQL-запросы.
Рис. 7. Запрос данных с помощью инструкции AD-Hoc SQL (щелкните, чтобы просмотреть изображение полного размера)
На этом этапе мы можем ввести sql-запрос вручную. При создании первого метода в TableAdapter обычно требуется, чтобы запрос возвращал эти столбцы, которые должны быть выражены в соответствующей таблице DataTable. Это можно сделать, создав запрос, который возвращает все столбцы и все строки из таблицы Products :
Рис. 8. Введите SQL-запрос в текстовое поле (щелкните, чтобы просмотреть изображение полного размера)
Кроме того, используйте построитель запросов и графически создайте запрос, как показано на рис. 9.
Рис. 9. Создание графического запроса с помощью Редактор запросов (щелкните, чтобы просмотреть полноразмерное изображение)
После создания запроса, но перед переходом на следующий экран нажмите кнопку "Дополнительные параметры". В проектах веб-сайтов инструкции "Создать инструкции Insert, Update и Delete" — это единственный расширенный параметр, выбран по умолчанию; если запустить этот мастер из библиотеки классов или проекта Windows, также будет выбран параметр "Использовать оптимистическую конкурентность". Оставьте опцию "Использовать оптимистическую конкуренцию" неотмеченной на данный момент. Мы рассмотрим оптимистичную конкуренцию в будущих руководствах.
Рис. 10: Выберите только параметр "Создать инструкции для вставки, обновления и удаления" (щелкните, чтобы просмотреть изображение полного размера)
После проверки дополнительных параметров нажмите кнопку "Далее", чтобы перейти к последнему экрану. Здесь мы просим выбрать методы для добавления в TableAdapter. Существует два шаблона для заполнения данных:
- Заполните DataTable с помощью этого подхода: создаётся метод, который принимает DataTable в качестве параметра и заполняет его данными, полученными по запросу. Например, класс DataAdapter ADO.NET реализует этот шаблон с помощью метода Fill( ).
- Верните DataTable — при таком подходе метод сам создает и заполняет DataTable и возвращает его в качестве возвращаемого значения метода.
Вы можете реализовать один или оба этих шаблона в TableAdapter. Вы также можете переименовать указанные здесь методы. Давайте оставим оба флажка отмеченными, хотя в этих руководствах мы будем использовать только последний шаблон. Кроме того, давайте переименуем довольно универсальный метод GetData в GetProducts.
Если установлен последний флажок, "GenerateDBDirectMethods", создаются методы Insert(), Update() и Delete() для TableAdapter. Если оставить этот параметр неустановленным, все обновления необходимо выполнять через единственный метод Update() TableAdapter, который принимает типизированный набор данных, DataTable, один объект DataRow или массив объектов DataRow. (Если вы сняли флажок "Создать инструкции Insert, Update и Delete" в дополнительных свойствах на рисунке 9, настройка этого флажка не будет иметь эффекта.) Давайте оставим этот флажок включенным.
Рис. 11. Изменение имени метода с GetData на GetProducts (щелкните, чтобы просмотреть изображение полного размера)
Завершите работу мастера, нажав кнопку "Готово". После закрытия мастера мы возвращаемся к Дизайнеру наборов данных, который отображает таблицу данных, которую мы только что создали. Вы можете увидеть список столбцов в Products DataTable (ProductID, ProductName и так далее), а также методы ProductsTableAdapter (Fill() и GetProducts()).
Рис. 12: Products DataTable и ProductsTableAdapter добавлены в типизированный DataSet (щелкните, чтобы просмотреть изображение в полном размере)
На этом этапе у нас есть типизированный набор данных с одним классом DataTable (Northwind.Products) и строго типизированным классом DataAdapter (NorthwindTableAdapters.ProductsTableAdapter) с методом GetProducts(). Эти объекты можно использовать для доступа к списку всех продуктов из кода, например:
NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
new NorthwindTableAdapters.ProductsTableAdapter();
Northwind.ProductsDataTable products;
products = productsAdapter.GetProducts();
foreach (Northwind.ProductsRow productRow in products)
Response.Write("Product: " + productRow.ProductName + "<br />");
Этот код не требует написания одного бита кода для доступа к данным. Нам не нужно создавать экземпляры классов ADO.NET, нам не нужно ссылаться на какие-либо строка подключения, запросы SQL или хранимые процедуры. Вместо этого TableAdapter предоставляет код доступа к данным низкого уровня для нас.
Каждый объект, используемый в этом примере, также строго типизирован, что позволяет Visual Studio предоставлять IntelliSense и проверку типов во время компиляции. И лучше всего данные DataTable, возвращаемые TableAdapter, можно привязать к ASP.NET веб-элементам управления данными, таким как GridView, DetailsView, DropDownList, CheckBoxList и несколько других. В следующем примере показана привязка DataTable, который возвращает метод GetProducts(), к GridView, используя всего три строки кода в обработчике событий Page_Load.
AllProducts.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="AllProducts.aspx.cs"
Inherits="AllProducts" %>
<!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>View All Products in a GridView</title>
<link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
<form id="form1" runat="server">
<div>
<h2>
All Products</h2>
<p>
<asp:GridView ID="GridView1" runat="server"
CssClass="DataWebControlStyle">
<HeaderStyle CssClass="HeaderStyle" />
<AlternatingRowStyle CssClass="AlternatingRowStyle" />
</asp:GridView>
</p>
</div>
</form>
</body>
</html>
AllProducts.aspx.cs
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;
public partial class AllProducts : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ProductsTableAdapter productsAdapter = new
ProductsTableAdapter();
GridView1.DataSource = productsAdapter.GetProducts();
GridView1.DataBind();
}
}
Рис. 13. Список продуктов отображается в GridView (щелкните, чтобы просмотреть изображение полного размера)
Хотя в этом примере необходимо написать три строки кода в обработчике событий Page_Load страницы ASP.NET , в будущих руководствах мы рассмотрим, как использовать ObjectDataSource для декларативного извлечения данных из DAL. При использовании ObjectDataSource нам не придется писать код и получать поддержку разбиения по страницам и сортировки!
Шаг 3. Добавление параметризованных методов в уровень доступа к данным
На этом этапе класс ProductsTableAdapter имеет только один метод GetProducts(), который возвращает все продукты в базе данных. Хотя возможность работать со всеми продуктами определенно полезна, есть времена, когда мы хотим получить информацию о конкретном продукте или все продукты, принадлежащие определенной категории. Чтобы добавить такие функции в уровень доступа к данным, можно добавить параметризованные методы в TableAdapter.
Добавим метод GetProductsByCategoryID(categoryID). Чтобы добавить новый метод в DAL, вернитесь к конструктору наборов данных, щелкните правой кнопкой мыши раздел ProductsTableAdapter и нажмите кнопку "Добавить запрос".
Рис. 14. Щелкните правой кнопкой мыши tableAdapter и выберите команду "Добавить запрос"
Сначала нас спрашивают, хотим ли мы получить доступ к базе данных, используя разовый SQL-запрос или новую или существующую хранимую процедуру. Давайте снова будем использовать разовый оператор SQL. Затем мы спросили, какой тип SQL-запроса мы хотели бы использовать. Так как мы хотим вернуть все продукты, принадлежащие указанной категории, мы хотим написать инструкцию SELECT , которая возвращает строки.
Рис. 15. Выбор инструкции SELECT , которая возвращает строки (щелкните, чтобы просмотреть изображение полного размера)
Следующим шагом является определение SQL-запроса, используемого для доступа к данным. Так как мы хотим вернуть только те продукты, которые относятся к определенной категории, я использую ту же инструкцию SELECT из GetProducts(), но добавьте следующее предложение WHERE: WHERE CategoryID = @CategoryID. Параметр @CategoryID указывает мастеру TableAdapter, что создаваемому методу потребуется входной параметр соответствующего типа (а именно— целое число, допускающее значение NULL).
Рис. 16. Введите запрос только для возврата продуктов в указанной категории (щелкните, чтобы просмотреть изображение полного размера)
На последнем шаге можно выбрать используемые шаблоны доступа к данным, а также настроить имена созданных методов. Для шаблона заполнения давайте изменим имя на FillByCategoryID, а для шаблона возвращаемого значения типа DataTable давайте использовать GetProductsByCategoryID.
Рис. 17. Выберите имена для методов TableAdapter (щелкните, чтобы просмотреть изображение полного размера)
После завершения работы мастера конструктор наборов данных включает новые методы TableAdapter.
Рис. 18. Теперь продукты можно запрашивать по категориям
Добавьте метод GetProductByProductID(productID) с использованием той же техники.
Эти параметризованные запросы можно протестировать непосредственно из конструктора наборов данных. Щелкните правой кнопкой мыши метод в TableAdapter и выберите пункт "Предварительный просмотр данных". Затем введите значения, используемые для параметров, и нажмите кнопку "Предварительный просмотр".
Рис. 19. Эти продукты, принадлежащие категории напитков, показаны (щелкните, чтобы просмотреть изображение полного размера)
С помощью метода GetProductsByCategoryID(categoryID) в DAL теперь можно создать страницу ASP.NET, отображающую только те продукты в указанной категории. В следующем примере показаны все продукты, которые находятся в категории "Напитки", которые имеют идентификатор категории 1.
Напитки.asp
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Beverages.aspx.cs"
Inherits="Beverages" %>
<!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>Untitled Page</title>
<link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
<form id="form1" runat="server">
<div>
<h2>Beverages</h2>
<p>
<asp:GridView ID="GridView1" runat="server"
CssClass="DataWebControlStyle">
<HeaderStyle CssClass="HeaderStyle" />
<AlternatingRowStyle CssClass="AlternatingRowStyle" />
</asp:GridView>
</p>
</div>
</form>
</body>
</html>
Beverages.aspx.cs
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;
public partial class Beverages : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ProductsTableAdapter productsAdapter = new
ProductsTableAdapter();
GridView1.DataSource =
productsAdapter.GetProductsByCategoryID(1);
GridView1.DataBind();
}
}
Рис. 20. Эти продукты в категории напитков отображаются (щелкните, чтобы просмотреть изображение полного размера)
Шаг 4. Вставка, обновление и удаление данных
Существует два шаблона, которые часто используются для вставки, обновления и удаления данных. Первый шаблон, который я назову "прямой шаблон базы данных", предполагает создание методов, которые при вызове выполняют команды INSERT, UPDATE или DELETE в базе данных, и эти команды работают с одной записью базы данных. Такие методы обычно передаются в ряд скалярных значений (целые числа, строки, логические значения, DateTimes и т. д.), соответствующие значениям для вставки, обновления или удаления. Например, при использовании этого шаблона для таблицы Products метод удаления принимает целочисленный параметр, указывающий ProductID записи для удаления, а метод вставки будет принимать строку для ProductName, десятичное значение для UnitPrice, целое число для UnitOnStock и т. д.
Рис. 21. Каждый запрос вставки, обновления и удаления отправляется в базу данных немедленно (щелкните, чтобы просмотреть изображение полного размера)
Другой шаблон, который я буду называть шаблоном пакетного обновления, заключается в обновлении всего набора данных, DataTable или коллекции DataRows в одном вызове метода. С помощью этого шаблона разработчик удаляет, вставляет и изменяет DataRows в DataTable, а затем передает эти DataRows или DataTable в метод обновления. Затем этот метод перечисляет передаваемые dataRows, определяет, были ли они изменены, добавлены или удалены (с помощью значения свойства RowState DataRow) и выдает соответствующий запрос базы данных для каждой записи.
Рис. 22. Все изменения синхронизированы с базой данных при вызове метода обновления (щелкните, чтобы просмотреть изображение полного размера)
TableAdapter использует шаблон пакетного обновления по умолчанию, но также поддерживает прямой шаблон базы данных. Поскольку мы выбрали параметр "Создать инструкции Insert, Update и Delete" в дополнительных свойствах при создании TableAdapter, ProductsTableAdapter содержит метод Update(), который реализует шаблон пакетного обновления. В частности, TableAdapter содержит метод Update(), который можно использовать с типизированным DataSet, строго типизированным DataTable или с одним или несколькими строками данных (DataRows). Если вы оставили флажок "GenerateDBDirectMethods" при первом создании Adapter таблицы, прямой доступ к базе данных также будет реализован с помощью методов Insert(), Update() и Delete().
Оба шаблона изменения данных используют свойства TableAdapter InsertCommand, UpdateCommand и DeleteCommand для выдачи команд INSERT, UPDATE и DELETE в базу данных. Вы можете проверить и изменить свойства InsertCommand, UpdateCommand и DeleteCommand, щелкнув TableAdapter в конструкторе наборов данных, а затем перейдя к окно свойств. (Убедитесь, что выбран TableAdapter и что ProductsTableAdapter — это тот, который выбран в раскрывающемся списке в окне свойств.)
Рис. 23. TableAdapter содержит свойства InsertCommand, UpdateCommand и DeleteCommand (щелкните, чтобы просмотреть изображение полного размера)
Чтобы проверить или изменить любое из свойств команд в базе данных, щелкните на подсвойство CommandText, который откроет Построитель запросов.
Рис. 24. Настройка инструкций INSERT, UPDATE и DELETE в построителе запросов (щелкните, чтобы просмотреть изображение полного размера)
В следующем примере кода показано, как использовать шаблон пакетного обновления, чтобы удвоить цену всех продуктов, которые не сняты с производства и имеют на складе 25 единиц или меньше.
NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
new NorthwindTableAdapters.ProductsTableAdapter();
// For each product, double its price if it is not discontinued and
// there are 25 items in stock or less
Northwind.ProductsDataTable products = productsAdapter.GetProducts();
foreach (Northwind.ProductsRow product in products)
if (!product.Discontinued && product.UnitsInStock <= 25)
product.UnitPrice *= 2;
// Update the products
productsAdapter.Update(products);
В приведенном ниже коде показано, как использовать прямой шаблон базы данных для программного удаления определенного продукта, а затем обновить его, а затем добавить новый:
NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
new NorthwindTableAdapters.ProductsTableAdapter();
// Delete the product with ProductID 3
productsAdapter.Delete(3);
// Update Chai (ProductID of 1), setting the UnitsOnOrder to 15
productsAdapter.Update("Chai", 1, 1, "10 boxes x 20 bags",
18.0m, 39, 15, 10, false, 1);
// Add a new product
productsAdapter.Insert("New Product", 1, 1,
"12 tins per carton", 14.95m, 15, 0, 10, false);
Создание пользовательских методов вставки, обновления и удаления
Методы Insert(), Update()и Delete(), созданные прямым методом базы данных, могут быть немного громоздкими, особенно для таблиц со многими столбцами. Глядя на предыдущий пример кода, без справки IntelliSense не особенно ясно, какой столбец таблицы Products соответствует каждому входному параметру методов Update() и Insert(). Может возникать время, когда требуется обновить только один столбец или два или требуется настраиваемый метод Insert( ), который, возможно, вернет значение только что вставленного поля IDENTITY (автоматическое увеличение).
Чтобы создать такой настраиваемый метод, вернитесь в конструктор dataSet. Щелкните правой кнопкой мыши на TableAdapter и выберите "Добавить запрос", чтобы вернуться в мастер TableAdapter. На втором экране можно указать тип создаваемого запроса. Давайте создадим метод, который добавляет новый продукт, а затем возвращает значение только что добавленной записи ProductID. Поэтому вы можете создать запрос INSERT .
Рис. 25. Создание метода для добавления новой строки в таблицу продуктов (щелкните, чтобы просмотреть изображение полного размера)
На следующем экране появится InsertCommandCommandText. Дополните этот запрос, добавив SELECT SCOPE_IDENTITY() в конце, что вернет последнее значение идентификатора, вставленное в столбец IDENTITY в той же области. (Дополнительные сведения о SCOPE_IDENTITY() см. в технической документации и о том, почему вы, вероятно, хотите использовать SCOPE_IDENTITY() вместо @@IDENTITY.) Перед добавлением инструкции SELECT убедитесь, что инструкция INSERT завершается точкой с запятой.
Рис. 26. Расширение запроса для возврата значения SCOPE_IDENTITY() (щелкните, чтобы просмотреть изображение полного размера)
Наконец, назовите новый метод InsertProduct.
Рис. 27. Задание имени нового метода insertProduct (щелкните, чтобы просмотреть изображение полного размера)
Когда вы вернетесь к конструктору наборов данных, вы увидите, что ProductsTableAdapter содержит новый метод InsertProduct. Если этот новый метод не имеет параметра для каждого столбца в таблице Products , вероятность того, что вы забыли завершить инструкцию INSERT с запятой. Настройте метод InsertProduct и убедитесь, что у вас есть разделители с запятой инструкции INSERT и SELECT.
По умолчанию методы вставки выдают методы, отличные от запросов, что означает, что они возвращают количество затронутых строк. Однако мы хотим , чтобы метод InsertProduct возвращал значение, возвращаемое запросом, а не количество затронутых строк. Для этого измените свойство ExecuteMode метода InsertProduct на Scalar.
Рис. 28. Изменение свойства ExecuteMode на Скаляр (щелкните, чтобы просмотреть изображение полного размера)
В следующем коде показан новый метод InsertProduct в действии:
NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
new NorthwindTableAdapters.ProductsTableAdapter();
// Add a new product
int new_productID = Convert.ToInt32(productsAdapter.InsertProduct
("New Product", 1, 1, "12 tins per carton", 14.95m, 10, 0, 10, false));
// On second thought, delete the product
productsAdapter.Delete(new_productID);
Шаг 5. Завершение уровня доступа к данным
Обратите внимание, что класс ProductsTableAdapters возвращает значения CategoryID и SupplierID из таблицы Products, но не включает столбец CategoryName из таблицы "Категории" или столбец CompanyName из таблицы "Поставщики", хотя это, скорее всего, столбцы, которые мы хотим отобразить при отображении сведений о продукте. Мы можем расширить начальный метод TableAdapter GetProducts(), чтобы включить значения столбцов CategoryName и CompanyName, что также обновит строго типизированный DataTable, добавив в него эти новые столбцы.
Однако это может представлять проблему, так как методы TableAdapter для вставки, обновления и удаления данных основаны на этом первоначальном методе. К счастью, автоматически созданные методы для вставки, обновления и удаления не влияют на вложенные запросы в предложении SELECT . Обеспечив добавление наших запросов в категории и в поставщики в качестве подзапросов, а не JOIN , мы избежим необходимости перерабатывать эти методы для изменения данных. Щелкните правой кнопкой мыши метод GetProducts() в ProductsTableAdapter и выберите "Настроить". Затем измените предложение SELECT таким образом, чтобы он выглядел следующим образом:
SELECT ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued,
(SELECT CategoryName FROM Categories
WHERE Categories.CategoryID = Products.CategoryID) as CategoryName,
(SELECT CompanyName FROM Suppliers
WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName
FROM Products
Рис. 29. Обновление инструкции SELECT для метода GetProducts() (щелкните, чтобы просмотреть изображение полного размера)
После обновления метода GetProducts() для использования этого нового запроса DataTable будет содержать два новых столбца: CategoryName и SupplierName.
Рис. 30. DataTable products имеет два новых столбца
Уделите момент, чтобы обновить предложение SELECT в методе GetProductsByCategoryID(categoryID).
Если вы обновляете GetProducts()SELECT с помощью синтаксиса JOIN , конструктор наборов данных не сможет автоматически создавать методы для вставки, обновления и удаления данных базы данных с помощью прямого шаблона базы данных. Вместо этого вам придется вручную создавать их так же, как и с методом InsertProduct ранее в этом руководстве. Кроме того, необходимо вручную указать значения свойств InsertCommand, UpdateCommand и DeleteCommand, если вы хотите использовать шаблон пакетного обновления.
Добавление оставшихся TableAdapters
До сих пор мы рассмотрели только работу с одним TableAdapter для одной таблицы базы данных. Однако база данных Northwind содержит несколько связанных таблиц, с которыми мы должны работать в нашем веб-приложении. Типизированный набор данных может содержать несколько связанных наборов данных. Поэтому, чтобы завершить наш DAL, необходимо добавить объекты DataTable для других таблиц, которые мы будем использовать в этих руководствах. Чтобы добавить новый TableAdapter в типизированный набор данных, откройте конструктор наборов данных, щелкните правой кнопкой мыши конструктор и нажмите кнопку "Добавить / TableAdapter". В результате будет создана новая таблица DataTable и TableAdapter, и вы пройдёте через мастер, который мы рассматривали ранее в этом руководстве.
Уделите несколько минут на создание следующих адаптеров таблиц и методов, используя следующие запросы. Обратите внимание, что запросы в ProductsTableAdapter включают вложенные запросы, чтобы получить имена категорий и поставщиков каждого продукта. Кроме того, если вы следовали вместе, вы уже добавили методы GetProducts() и GetProductsByCategoryID(categoryID) класса ProductsTableAdapter.
ПродуктыТаблицаАдаптер
GetProducts:
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT CategoryName FROM Categories WHERE Categories.CategoryID = Products.CategoryID) as CategoryName, (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName FROM ProductsGetProductsByCategoryID:
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT CategoryName FROM Categories WHERE Categories.CategoryID = Products.CategoryID) as CategoryName, (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName FROM Products WHERE CategoryID = @CategoryIDGetProductsBySupplierID:
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT CategoryName FROM Categories WHERE Categories.CategoryID = Products.CategoryID) as CategoryName, (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName FROM Products WHERE SupplierID = @SupplierIDGetProductByProductID:
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT CategoryName FROM Categories WHERE Categories.CategoryID = Products.CategoryID) as CategoryName, (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName FROM Products WHERE ProductID = @ProductID
CategoriesTableAdapter
GetCategories:
SELECT CategoryID, CategoryName, Description FROM CategoriesGetCategoryByCategoryID:
SELECT CategoryID, CategoryName, Description FROM Categories WHERE CategoryID = @CategoryID
ПоставщикиTableAdapter
GetSuppliers:
SELECT SupplierID, CompanyName, Address, City, Country, Phone FROM SuppliersGetSuppliersByCountry:
SELECT SupplierID, CompanyName, Address, City, Country, Phone FROM Suppliers WHERE Country = @CountryGetSupplierBySupplierID:
SELECT SupplierID, CompanyName, Address, City, Country, Phone FROM Suppliers WHERE SupplierID = @SupplierID
АдаптерТаблицыСотрудников
GetEmployees:
SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country FROM EmployeesGetEmployeesByManager:
SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country FROM Employees WHERE ReportsTo = @ManagerIDGetEmployeeByEmployeeID:
SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country FROM Employees WHERE EmployeeID = @EmployeeID
Рис. 31: Конструктор DataSet после добавления четырех адаптеров таблиц (щелкните, чтобы просмотреть изображение полного размера)
Добавление пользовательского кода в DAL
TableAdapters и DataTables, добавленные в типизированный DataSet, представлены в виде файла определения схемы XML (Northwind.xsd). Эти сведения схемы можно просмотреть, щелкнув правой кнопкой мыши на файле Northwind.xsd в Обозревателе решений и выбрав Просмотреть код.
Рис. 32: Файл определения схемы XML (XSD) для типизированного набора данных Northwinds (нажмите, чтобы просмотреть изображение полного размера)
Информация схемы преобразуется в код C# или Visual Basic на этапе разработки при компиляции или, при необходимости, во время выполнения, на этом этапе можно пошагово просмотреть его с помощью отладчика. Чтобы просмотреть этот автоматически созданный код, перейдите в представление классов и выполните детализацию к классам TableAdapter или Typed DataSet. Если вы не видите представление классов на экране, перейдите в меню "Вид" и выберите его там или нажмите клавиши CTRL+SHIFT+C. В представлении классов можно просмотреть свойства, методы и события классов Typed DataSet и TableAdapter. Чтобы просмотреть код для определенного метода, дважды щелкните имя метода в представлении классов или щелкните его правой кнопкой мыши и выберите команду "Перейти к определению".
Рис. 33. Проверка автогенерированного кода путем выбора "Перейти к определению" в представлении классов
Хотя автоматически созданный код может быть большим экономией времени, код часто очень универсальный и должен быть настроен для удовлетворения уникальных потребностей приложения. Однако риск расширения автоматически созданного кода заключается в том, что средство, создающее код, может решить, что пришло время повторно создать и перезаписать настройки. Благодаря новой концепции частичного класса .NET 2.0 легко разделить класс по нескольким файлам. Это позволяет добавлять собственные методы, свойства и события в автоматически созданные классы, не беспокоясь о перезаписи наших настроек Visual Studio.
Чтобы продемонстрировать, как настроить DAL, давайте добавим метод GetProducts() в класс SuppliersRow . Класс ProviderRow представляет одну запись в таблице "Поставщики", каждый поставщик может использовать ноль для многих продуктов, поэтому GetProducts() вернет эти продукты указанного поставщика. Для этого создайте файл класса в папке App_Code с именем SuppliersRow.cs и добавьте следующий код:
using System;
using System.Data;
using NorthwindTableAdapters;
public partial class Northwind
{
public partial class SuppliersRow
{
public Northwind.ProductsDataTable GetProducts()
{
ProductsTableAdapter productsAdapter =
new ProductsTableAdapter();
return
productsAdapter.GetProductsBySupplierID(this.SupplierID);
}
}
}
Этот частичный класс указывает компилятору, что при создании класса Northwind.SuppliersRow необходимо включить только что определенный метод GetProducts( ). Если вы создадите проект, а затем вернетесь к представлению классов, теперь вы увидите GetProducts() в качестве метода Northwind.SuppliersRow.
Рис. 34. Метод GetProducts() теперь является частью класса Northwind.SuppliersRow
Теперь метод GetProducts() можно использовать для перечисления набора продуктов для определенного поставщика, как показано в следующем коде:
NorthwindTableAdapters.SuppliersTableAdapter suppliersAdapter =
new NorthwindTableAdapters.SuppliersTableAdapter();
// Get all of the suppliers
Northwind.SuppliersDataTable suppliers =
suppliersAdapter.GetSuppliers();
// Enumerate the suppliers
foreach (Northwind.SuppliersRow supplier in suppliers)
{
Response.Write("Supplier: " + supplier.CompanyName);
Response.Write("<ul>");
// List the products for this supplier
Northwind.ProductsDataTable products = supplier.GetProducts();
foreach (Northwind.ProductsRow product in products)
Response.Write("<li>" + product.ProductName + "</li>");
Response.Write("</ul><p> </p>");
}
Эти данные также можно отобразить в любом из веб-контролов данных ASP.NET. На следующей странице используется элемент управления GridView с двумя полями:
- BoundField, отображающий имя каждого поставщика и
- TemplateField, содержащий элемент управления BulletedList, привязанный к результатам, возвращаемым методом GetProducts() для каждого поставщика.
Мы рассмотрим, как отобразить такие отчеты с основной и детализированной информацией в последующих уроках. Теперь этот пример предназначен для иллюстрации использования пользовательского метода, добавленного в класс Northwind.SuppliersRow .
SuppliersAndProducts.aspx
<%@ Page Language="C#" CodeFile="SuppliersAndProducts.aspx.cs"
AutoEventWireup="true" Inherits="SuppliersAndProducts" %>
<!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>Untitled Page</title>
<link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
<form id="form1" runat="server">
<div>
<h2>
Suppliers and Their Products</h2>
<p>
<asp:GridView ID="GridView1" runat="server"
AutoGenerateColumns="False"
CssClass="DataWebControlStyle">
<HeaderStyle CssClass="HeaderStyle" />
<AlternatingRowStyle CssClass="AlternatingRowStyle" />
<Columns>
<asp:BoundField DataField="CompanyName"
HeaderText="Supplier" />
<asp:TemplateField HeaderText="Products">
<ItemTemplate>
<asp:BulletedList ID="BulletedList1"
runat="server" DataSource="<%# ((Northwind.SuppliersRow) ((System.Data.DataRowView) Container.DataItem).Row).GetProducts() %>"
DataTextField="ProductName">
</asp:BulletedList>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</p>
</div>
</form>
</body>
</html>
SuppliersAndProducts.aspx.cs
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;
public partial class SuppliersAndProducts : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
SuppliersTableAdapter suppliersAdapter = new
SuppliersTableAdapter();
GridView1.DataSource = suppliersAdapter.GetSuppliers();
GridView1.DataBind();
}
}
Рис. 35. Имя компании поставщика отображается в левом столбце, их продукты в правой части (щелкните, чтобы просмотреть изображение полного размера)
Итоги
При создании веб-приложения создание DAL следует выполнить одним из первых шагов, прежде чем приступить к созданию презентационного уровня. С помощью Visual Studio создание DAL на основе типизированных наборов данных — это задача, которую можно выполнить за 10–15 минут без написания строки кода. Руководства в дальнейшем будут опираться на этот DAL. В следующем руководстве мы определим ряд бизнес-правил и посмотрим, как реализовать их в отдельном уровне бизнес-логики.
Счастливое программирование!
Дополнительные материалы
Дополнительные сведения о разделах, описанных в этом руководстве, см. в следующих ресурсах:
- Создание DAL с использованием строго типизированных TableAdapters и DataTables в VS 2005 и ASP.NET 2.0
- Проектирование компонентов уровня данных и передача данных по уровням
- Шифрование сведений о конфигурации в приложениях ASP.NET 2.0
- Общие сведения об адаптере таблиц
- Работа с типизированным набором данных
- Использование строго типизированного доступа к данным в Visual Studio 2005 и ASP.NET 2.0
- Расширение методов TableAdapter
Учебный видеоролик по темам, содержащимся в этом руководстве
- Уровни доступа к данным в приложениях ASP.NET
- Как вручную привязать набор данных к Datagrid
- Как работать с наборами данных и фильтрами из приложения ASP
Об авторе
Скотт Митчелл, автор семи книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с технологиями Microsoft Web с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его последняя книга
Особое спасибо кому
Эта серия учебников была проверена многими полезными рецензентами. Ведущие рецензенты для этого руководства были Рон Грин, Хилтон Гизенау, Деннис Паттерсон, Лиз Шулок, Абель Гомес и Карлос Сантос. Хотите просмотреть мои предстоящие статьи MSDN? Если да, напишите мне на mitchell@4GuysFromRolla.com.