Создание уровня доступа к данным (VB)

Скотт Митчелл

Скачать в формате PDF

В этом руководстве мы начнем с самого начала и создадим уровень доступа к данным (DAL), используя типизированные наборы данных для доступа к данным в базе данных.

Введение

Как веб-разработчики, наша жизнь вращается вокруг работы с данными. Мы создаем базы данных для хранения данных, код для их извлечения и изменения, а веб-страницы для их сбора и обобщения. Это первое руководство в длинной серии, которая изучит методы реализации этих распространенных шаблонов в ASP.NET 2.0. Начнем с создания архитектуры программного обеспечения, состоящей из уровня доступа к данным (DAL) с помощью типизированных наборов данных, уровня бизнес-логики (BLL), который применяет пользовательские бизнес-правила, и слой презентации, состоящий из ASP.NET страниц, которые совместно используют общий макет страницы. После того как эта серверная основа будет создана, мы перейдем в отчеты, показывая, как отображать, суммировать, собирать и проверять данные из веб-приложения. Эти учебники предназначены для того, чтобы быть краткими и предоставить пошаговые инструкции с большим количеством снимков экрана, чтобы провести процесс визуально. Каждое руководство доступно в версиях C# и Visual Basic и включает скачивание всего используемого кода. (Это первое руководство довольно длинное, но остальные представлены в гораздо более дайджестируемых фрагментах.)

В этих руководствах мы будем использовать версию Microsoft SQL Server 2005 Express Edition базы данных Northwind, размещенную в каталоге App_Data . Помимо файла App_Data базы данных, папка также содержит скрипты SQL для создания базы данных, если вы хотите использовать другую версию базы данных. Если вы используете другую версию SQL Server для базы данных 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, задайте раскрывающийся список "Расположение" файловой системе, выберите папку для размещения веб-сайта и задайте язык Visual Basic.

Создание нового веб-сайта на основе файловой системы

Рис. 1. Создание веб-сайта на основе файловой системы (щелкните, чтобы просмотреть изображение полного размера)

При этом будет создан новый веб-сайт с Default.aspx ASP.NET страницей, App_Data папкой и файлом Web.config .

После создания веб-сайта необходимо добавить ссылку на базу данных в обозревателе серверов Visual Studio. Добавив базу данных в обозреватель серверов, вы можете добавлять таблицы, хранимые процедуры, представления и т. д. из Visual Studio. Вы также можете просматривать данные таблицы или создавать собственные запросы вручную или графически с помощью построителя запросов. Кроме того, при сборке типизированных наборов данных для DAL необходимо указать Visual Studio на базу данных, на основе которой будут создаваться типизированные наборы данных. Хотя мы можем предоставить эти сведения о подключении на данный момент времени, Visual Studio автоматически заполняет раскрывающийся список баз данных, уже зарегистрированных в обозревателе серверов.

Действия по добавлению базы данных Northwind в Обозреватель серверов зависят от того, хотите ли вы использовать базу данных из папки App_Data с SQL Server 2005 Express Edition, или если у вас есть сервер базы данных 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. Откроется диалоговое окно "Добавление подключения", где можно указать сервер для подключения, сведения о проверке подлинности и имя базы данных. После успешной настройки сведений о подключении к базе данных и нажатия кнопки "ОК" база данных будет добавлена в качестве узла под узлом подключения к данным. Узел базы данных можно развернуть для изучения таблиц, представлений, хранимых процедур и т. д.

Добавить подключение к базе данных Northwind на сервере базы данных

Рис. 2: Добавьте подключение к базе данных Northwind вашего сервера базы данных

Шаг 2. Создание уровня доступа к данным

При работе с данными одним из вариантов является внедрение логики для конкретных данных непосредственно в слой презентации (в веб-приложении ASP.NET страницы составляют слой презентации). Это может иметь форму написания кода ADO.NET в части кода ASP.NET страницы или с помощью элемента управления SqlDataSource из части разметки. В любом случае этот подход тесно связывает логику доступа к данным с уровнем представления. Однако рекомендуемый подход заключается в том, чтобы разделить логику доступа к данным от слоя презентации. Этот отдельный слой называется уровнем доступа к данным, сокращённо DAL, и обычно реализуется как отдельный проект классовой библиотеки. Преимущества этой многоуровневой архитектуры хорошо описаны (см. раздел "Дополнительные чтения" в конце этого руководства, чтобы получить информацию об этих преимуществах) и является подходом, который мы рассмотрим в этой серии.

Весь код, относящийся к базовому источнику данных, например, создание подключения к базе данных, выдача SELECT, INSERT, UPDATE команд и DELETE и т. д., должны содержаться в DAL. Слой презентации не должен содержать ссылки на такой код доступа к данным, но вместо этого следует выполнять вызовы в DAL для всех и всех запросов данных. Уровни доступа к данным обычно содержат методы для доступа к базовым данным базы данных. База данных Northwind, например, содержит Products и Categories таблицы, которые записывают продукты для продажи и категории, к которым они относятся. В нашем DAL у нас будут такие методы:

  • GetCategories(), которые будут возвращать сведения обо всех категориях
  • GetProducts(), который вернет сведения обо всех продуктах
  • GetProductsByCategoryID(categoryID), который вернет все продукты, принадлежащие указанной категории
  • GetProductByProductID(productID), который вернет сведения о конкретном продукте

Эти методы при вызове подключаются к базе данных, выдают соответствующий запрос и возвращают результаты. Важно то, как мы возвращаем эти результаты. Эти методы могут просто возвращать набор данных или DataReader, заполненный запросом базы данных, но в идеале эти результаты должны быть возвращены с помощью строго типизированных объектов. Строго типизированный объект — это объект, схема которого жестко определена во время компиляции, в то время как наоборот, слабо типизированный объект, является одним, схема которого не известна до времени выполнения.

Например, DataReader и DataSet (по умолчанию) являются слабо типизированными объектами, так как их схема определяется столбцами, возвращаемыми запросом базы данных, используемым для их заполнения. Чтобы получить доступ к конкретному столбцу из свободно типизированной dataTable, необходимо использовать синтаксис, например: DataTable.Rows(index)("columnName") Свободная типизация в DataTable в этом примере проявляется в том, что необходимо обращаться к имени столбца с помощью строкового или числового индекса. С другой стороны, строго типизированный dataTable будет иметь каждый из его столбцов, реализованных как свойства, что приводит к коду, который выглядит следующим образом: DataTable.Rows(index).columnName

Чтобы возвращать строго типизированные объекты, разработчики могут создавать собственные бизнес-объекты или использовать типизированные наборы данных. Бизнес-объект реализуется разработчиком как класс, свойства которого обычно отражают столбцы базовой таблицы базы данных, представляющей бизнес-объект. Типизированный набор данных — это класс, созданный Visual Studio на основе схемы базы данных и члены которого строго типизированы в соответствии с этой схемой. Типизированный набор данных состоит из классов, расширяющих классы ADO.NET DataSet, DataTable и DataRow. Помимо строго типизированных таблиц DataTable, Типизированные наборы данных теперь также включают TableAdapters, которые являются классами с методами для заполнения таблиц DataTables и распространения изменений в DataTables обратно в базу данных.

Примечание.

Дополнительные сведения о преимуществах и недостатках использования типизированных наборов данных и пользовательских бизнес-объектов см. в статье "Проектирование компонентов уровня данных" и передача данных по уровням.

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

Весь код доступа к данным перенесен в DAL

Рис. 3. Весь код доступа к данным переключен на DAL (щелкните, чтобы просмотреть полноразмерное изображение)

Создание типизированного набора данных и адаптера таблицы

Чтобы начать создание DAL, мы начнем с добавления типизированного набора данных в наш проект. Для этого щелкните правой кнопкой мыши на узле проекта в Обозревателе решений и выберите "Добавить новый элемент". Выберите параметр DataSet из списка шаблонов и назовите его Northwind.xsd.

Выбор добавления нового набора данных в проект

Рис. 4. Выберите добавить новый набор данных в проект (щелкните, чтобы просмотреть изображение полного размера)

После нажатия кнопки "Добавить" при появлении запроса на добавление набора данных в App_Code папку нажмите кнопку "Да". Затем откроется конструктор для типизированного набора данных, и откроется мастер настройки TableAdapter, позволяющий добавить первый tableAdapter в типизированный набор данных.

Типизированный набор данных служит строго типизированной коллекцией данных; Он состоит из строго типизированных экземпляров DataTable, каждый из которых, в свою очередь, состоит из строго типизированных экземпляров DataRow. Мы создадим строго типизированный набор данных для каждой из базовых таблиц базы данных, с которыми мы должны работать в этой серии руководств. Начнем с создания таблицы данных для таблицы DataTable Products.

Помните, что строго типизированные таблицы DataTable не содержат никаких сведений о том, как получить доступ к данным из базовой таблицы базы данных. Чтобы получить данные для заполнения DataTable, мы используем класс TableAdapter, который функционирует как наш уровень доступа к данным. Для нашего Products DataTable адаптер таблицы будет содержать методы GetProducts(), GetProductByCategoryID(categoryID) и так далее, которые будут вызываться из слоя представления. Роль DataTable заключается в том, чтобы служить строго типизированными объектами, используемыми для передачи данных между слоями.

Мастер настройки TableAdapter начинается с запроса на выбор базы данных для работы. В раскрывающемся списке показаны эти базы данных в обозревателе серверов. Если база данных Northwind не добавлена в обозреватель серверов, вы можете нажать кнопку "Создать подключение" в это время.

Выберите базу данных Northwind из раскрывающегося списка

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

После выбора базы данных и нажатия кнопки Далее вам будет предложено сохранить строку подключения в Web.config файле. Сохранив строку подключения, вы избежите его жесткого кодирования в классах TableAdapter, что упростит работу, если информация о строке подключения изменится в будущем. Если вы решили сохранить строку подключения в файле конфигурации, она помещается в раздел <connectionStrings>, который может быть дополнительно зашифрован для повышения безопасности или изменен позже с помощью новой страницы свойств ASP.NET 2.0 в графическом интерфейсе администратора IIS, что более удобно для администраторов.

Сохранение строки подключения в Web.config

Рис. 6: Сохраните строку подключения Web.config (щелкните, чтобы просмотреть изображение полного размера)

Затем необходимо определить схему для первой строго типизированной таблицы DataTable и предоставить первый метод для нашего TableAdapter, который будет использоваться при заполнении строго типизированного набора данных. Эти два шага выполняются одновременно путем создания запроса, который возвращает столбцы из таблицы, которую мы хотим отразить в таблице DataTable. В конце работы мастера мы дадим этому запросу имя метода. После этого этот метод можно вызвать из нашего слоя презентации. Метод выполнит определенный запрос и заполняет строго типизированный dataTable.

Чтобы приступить к определению SQL-запроса, сначала необходимо указать, как нужно, чтобы TableAdapter выдает запрос. Мы можем использовать разовый SQL-запрос, создать новую хранимую процедуру или использовать существующую хранимую процедуру. В этих руководствах мы будем использовать временные SQL-запросы.

Запрос данных с помощью инструкции AD-Hoc SQL

Рис. 7. Запрос данных с помощью инструкции AD-Hoc SQL (щелкните, чтобы просмотреть изображение полного размера)

На этом этапе мы можем ввести sql-запрос вручную. При создании первого метода в TableAdapter обычно требуется, чтобы запрос возвращал эти столбцы, которые должны быть выражены в соответствующей таблице DataTable. Для этого можно создать запрос, возвращающий все столбцы и все строки из Products таблицы:

Введите SQL-запрос в текстовое поле

Рис. 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 или массив DataRows. (Если вы сняли флажок "Создать инструкции Insert, Update и Delete" в дополнительных свойствах на рисунке 9, настройка этого флажка не будет иметь эффекта.) Давайте оставим этот флажок включенным.

Изменение имени метода с GetData на GetProducts

Рис. 11. Изменение имени метода на GetDataGetProducts (щелкните, чтобы просмотреть изображение полного размера)

Завершите работу мастера, нажав кнопку "Готово". После закрытия мастера мы возвращаемся к Дизайнеру наборов данных, который отображает таблицу данных, которую мы только что создали. Список столбцов можно просмотреть в Products DataTable (ProductID, ProductNameи т. д.), а также методы (ProductsTableAdapterFill()иGetProducts()).

Продукты DataTable и ProductsTableAdapter добавлены в типизированный набор данных

Рис. 12: Products DataTable и ProductsTableAdapter были добавлены в типизированный набор данных (нажмите, чтобы просмотреть изображение полного размера)

На этом этапе у нас есть типизированный набор данных с одним классом DataTable (Northwind.Products) и строго типизированным классом DataAdapter (NorthwindTableAdapters.ProductsTableAdapter) с методом GetProducts() . Эти объекты можно использовать для доступа к списку всех продуктов из кода, например:

Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
Dim products as Northwind.ProductsDataTable
products = productsAdapter.GetProducts()
For Each productRow As Northwind.ProductsRow In products
    Response.Write("Product: " & productRow.ProductName & "<br />")
Next

Этот код не требует написания одного бита кода для доступа к данным. Нам не нужно создавать экземпляры классов ADO.NET, нам не нужно ссылаться на какие-либо строка подключения, запросы SQL или хранимые процедуры. Вместо этого TableAdapter предоставляет код доступа к данным низкого уровня для нас.

Каждый объект, используемый в этом примере, также строго типизирован, что позволяет Visual Studio предоставлять IntelliSense и проверку типов во время компиляции. И лучше всего данные DataTable, возвращаемые TableAdapter, можно привязать к ASP.NET веб-элементам управления данными, таким как GridView, DetailsView, DropDownList, CheckBoxList и несколько других. В следующем примере показано, как привязать возвращаемую методом DataTable к GridView, используя всего три строки кода в обработчике событий Page_Load.

AllProducts.aspx

<%@ Page Language="VB" AutoEventWireup="true" CodeFile="AllProducts.aspx.vb"
    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>
        <h1>
            All Products</h1>
        <p>
            <asp:GridView ID="GridView1" runat="server"
             CssClass="DataWebControlStyle">
               <HeaderStyle CssClass="HeaderStyle" />
               <AlternatingRowStyle CssClass="AlternatingRowStyle" />
            </asp:GridView>
             </p>
    </div>
    </form>
</body>
</html>

AllProducts.aspx.vb

Imports NorthwindTableAdapters
Partial Class AllProducts
    Inherits System.Web.UI.Page
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles Me.Load
        Dim productsAdapter As New ProductsTableAdapter
        GridView1.DataSource = productsAdapter.GetProducts()
        GridView1.DataBind()
    End Sub
End Class

Список продуктов отображается в GridView

Рис. 13. Список продуктов отображается в GridView (щелкните, чтобы просмотреть изображение полного размера)

Хотя в этом примере необходимо написать три строки кода в обработчике событий ASP.NET страницы Page_Load , в будущих руководствах мы рассмотрим, как использовать ObjectDataSource для декларативного извлечения данных из DAL. При использовании ObjectDataSource нам не придется писать код и получать поддержку разбиения по страницам и сортировки!

Шаг 3. Добавление параметризованных методов в уровень доступа к данным

На этом этапе наш ProductsTableAdapter класс имеет только один метод, GetProducts()который возвращает все продукты в базе данных. Хотя возможность работать со всеми продуктами определенно полезна, есть времена, когда мы хотим получить информацию о конкретном продукте или все продукты, принадлежащие определенной категории. Чтобы добавить такие функции в уровень доступа к данным, можно добавить параметризованные методы в TableAdapter.

Добавим метод GetProductsByCategoryID(categoryID). Чтобы добавить новый метод в DAL, вернитесь в конструктор наборов данных, щелкните правой кнопкой мыши раздел ProductsTableAdapter и выберите команду "Добавить запрос".

Щелкните правой кнопкой мыши tableAdapter и выберите команду

Рис. 14. Щелкните правой кнопкой мыши tableAdapter и выберите команду "Добавить запрос"

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

Выберите создать инструкцию SELECT, которая возвращает строки

Рис. 15: Выберите создать SELECT инструкцию, которая возвращает строки (Щелкните, чтобы просмотреть изображение в полном размере)

Следующим шагом является определение SQL-запроса, используемого для доступа к данным. Так как мы хотим вернуть только те продукты, которые относятся к определенной категории, я использую ту же SELECT инструкцию из GetProducts(), но я добавляю следующее WHERE предложение: WHERE CategoryID = @CategoryID. Параметр @CategoryID указывает мастеру TableAdapter, что создаваемому методу потребуется входной параметр соответствующего типа (а именно— целое число, допускающее значение NULL).

Введите запрос только для возврата продуктов в указанной категории

Рис. 16. Введите запрос только для возврата продуктов в указанной категории (щелкните, чтобы просмотреть изображение полного размера)

На последнем шаге можно выбрать используемые шаблоны доступа к данным, а также настроить имена созданных методов. Для шаблона заполнения давайте изменим имя на FillByCategoryID, а для паттерна возврата DataTable (методы GetX), давайте использовать GetProductsByCategoryID.

Выберите имена для методов TableAdapter

Рис. 17. Выберите имена для методов TableAdapter (щелкните, чтобы просмотреть изображение полного размера)

После завершения работы мастера конструктор наборов данных включает новые методы TableAdapter.

Теперь продукты можно запрашивать по категориям

Рис. 18. Теперь продукты можно запрашивать по категориям

Найдите момент, чтобы добавить метод GetProductByProductID(productID), используя тот же подход.

Эти параметризованные запросы можно протестировать непосредственно из конструктора наборов данных. Щелкните правой кнопкой мыши метод в TableAdapter и выберите пункт "Предварительный просмотр данных". Затем введите значения, используемые для параметров, и нажмите кнопку "Предварительный просмотр".

Эти продукты, принадлежащие категории напитков, показаны

Рис. 19. Эти продукты, принадлежащие категории напитков, показаны (щелкните, чтобы просмотреть изображение полного размера)

GetProductsByCategoryID(categoryID) С помощью метода в нашем DAL теперь можно создать страницу ASP.NET, отображающую только те продукты в указанной категории. В следующем примере показаны все продукты, которые находятся в категории "Напитки", которые имеют CategoryID значение 1.

Beverages.aspx

<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Beverages.aspx.vb"
    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>
        <h1>Beverages</h1>
        <p>
            <asp:GridView ID="GridView1" runat="server"
             CssClass="DataWebControlStyle">
               <HeaderStyle CssClass="HeaderStyle" />
               <AlternatingRowStyle CssClass="AlternatingRowStyle" />
            </asp:GridView>
             </p>
    </div>
    </form>
</body>
</html>

Beverages.aspx.vb

Imports NorthwindTableAdapters
Partial Class Beverages
    Inherits System.Web.UI.Page
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles Me.Load
        Dim productsAdapter As New ProductsTableAdapter
        GridView1.DataSource =
         productsAdapter.GetProductsByCategoryID(1)
        GridView1.DataBind()
    End Sub
End Class

Эти продукты в категории напитков отображаются

Рис. 20. Эти продукты в категории напитков отображаются (щелкните, чтобы просмотреть изображение полного размера)

Шаг 4. Вставка, обновление и удаление данных

Существует два шаблона, которые часто используются для вставки, обновления и удаления данных. Первый шаблон, который я назову шаблоном прямого обращения к базе данных, предполагает создание методов, которые при вызове отправляют команду INSERT, UPDATE или DELETE в базу данных, обрабатывающую одну запись в базе данных. Такие методы обычно передаются в ряд скалярных значений (целые числа, строки, логические значения, DateTimes и т. д.), соответствующие значениям для вставки, обновления или удаления. Например, при использовании этого шаблона для Products таблицы метод удаления принимает целочисленный параметр, указывающий ProductID на запись для удаления, а метод вставки принимает строку для ProductName, десятичное значение для UnitPrice, целое число для UnitsOnStockи т. д.

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

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

Другой шаблон, который я буду называть шаблоном пакетного обновления, заключается в обновлении всего набора данных, DataTable или коллекции DataRows в одном вызове метода. С помощью этого шаблона разработчик удаляет, вставляет и изменяет DataRows в DataTable, а затем передает эти DataRows или DataTable в метод обновления. Затем этот метод перечисляет передаваемые dataRows, определяет, были ли они изменены, добавлены или удалены (с помощью значения свойства RowState DataRow) и выдает соответствующий запрос базы данных для каждой записи.

Все изменения синхронизированы с базой данных при вызове метода обновления

Рис. 22. Все изменения синхронизированы с базой данных при вызове метода обновления (щелкните, чтобы просмотреть изображение полного размера)

TableAdapter использует шаблон пакетного обновления по умолчанию, но также поддерживает прямой шаблон базы данных. Так как мы выбрали параметр "Создать инструкции Insert, Update и Delete" из расширенных свойств при создании TableAdapter, ProductsTableAdapter содержит метод Update(), который реализует шаблон пакетного обновления. В частности, TableAdapter содержит метод Update(), в который можно передать типизированный DataSet, строго типизированный DataTable или одну или несколько DataRows. Если флажок "GenerateDBDirectMethods" оставлен установленным при первом создании TableAdapter, паттерн прямого доступа к базе данных также будет реализован с помощью методов Insert(), Update() и Delete().

Оба шаблона изменения данных используют свойства TableAdapter InsertCommand, UpdateCommand и DeleteCommand для выдачи их команд INSERT, UPDATE и DELETE в базу данных. Вы можете проверить и изменить свойства InsertCommand, UpdateCommand и DeleteCommand, щелкнув на TableAdapter в конструкторе наборов данных и перейдя к окну свойств. (Убедитесь, что выбран TableAdapter и ProductsTableAdapter, и что объект выбран в раскрывающемся списке в окне свойств.)

TableAdapter содержит свойства InsertCommand, UpdateCommand и DeleteCommand

Рис. 23. TableAdapter содержит InsertCommand, UpdateCommand, и DeleteCommand свойства (нажмите, чтобы увидеть изображение в полном размере)

Чтобы изучить или изменить любое из этих свойств команды базы данных, щелкните CommandText подсвойство, которое откроет построитель запросов.

Настройка инструкций INSERT, UPDATE и DELETE в построителе запросов

Рис. 24: Настройка INSERT, UPDATE и DELETE операторов в построителе запросов (Кликните для просмотра изображения в полном размере)

В следующем примере кода показано, как использовать шаблон пакетного обновления, чтобы удвоить цену всех продуктов, которые не сняты с производства и имеют на складе 25 единиц или меньше.

Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
Dim products As Northwind.ProductsDataTable = productsAdapter.GetProducts()
For Each product As Northwind.ProductsRow In products
   If Not product.Discontinued AndAlso product.UnitsInStock <= 25 Then
      product.UnitPrice *= 2
   End if
Next
productsAdapter.Update(products)

В приведенном ниже коде показано, как использовать прямой шаблон базы данных для программного удаления определенного продукта, а затем обновить его, а затем добавить новый:

Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
productsAdapter.Delete(3)
productsAdapter.Update( _
    "Chai", 1, 1, "10 boxes x 20 bags", 18.0, 39, 15, 10, false, 1)
productsAdapter.Insert( _
    "New Product", 1, 1, "12 tins per carton", 14.95, 15, 0, 10, false)

Создание пользовательских методов вставки, обновления и удаления

Методы Insert(), Update() и Delete(), созданные с помощью прямого метода работы с базой данных, могут быть немного громоздкими, особенно для таблиц со многими столбцами. Глядя на предыдущий пример кода, без помощи IntelliSense не особенно ясно, как столбец таблицы Products сопоставляется с каждым входным параметром методов Update() и Insert(). Может возникать время, когда требуется обновить только один столбец или два или требуется настраиваемый Insert() метод, который, возможно, вернет значение только что вставленного поля записи IDENTITY (автоматическое увеличение).

Чтобы создать такой настраиваемый метод, вернитесь в конструктор dataSet. Щелкните правой кнопкой мыши на TableAdapter и выберите "Добавить запрос", чтобы вернуться в мастер TableAdapter. На втором экране можно указать тип создаваемого запроса. Давайте создадим метод, который добавляет новый продукт, а затем возвращает значение только что добавленной записи ProductID. Поэтому решите создать INSERT запрос.

Создание метода для добавления новой строки в таблицу products

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

На следующем экране InsertCommand появится CommandText. Расширьте этот запрос, добавив SELECT SCOPE_IDENTITY() в конец запроса, что вернет последнее значение удостоверения, вставленное в столбец IDENTITY в том же контексте. (См . техническую документацию для получения дополнительных сведений о SCOPE_IDENTITY() том, почему вы, вероятно, хотите использовать SCOPE_IDENTITY() вместо @@IDENTITY.) Убедитесь, что инструкция INSERT заканчивается точкой с запятой, прежде чем добавлять инструкцию SELECT.

Расширение запроса для возврата значения SCOPE_IDENTITY()

Рис. 26. Расширение запроса для возврата SCOPE_IDENTITY() значения (щелкните, чтобы просмотреть изображение полного размера)

Наконец, назовите новый метод InsertProduct.

Задайте для имени нового метода значение InsertProduct

Рис. 27: Установите новое имя метода InsertProduct (щелкните, чтобы просмотреть изображение в полном размере)

Когда вы вернетесь в конструктор наборов данных, вы увидите, что ProductsTableAdapter содержит новый метод InsertProduct. Если этот новый метод не имеет параметра для каждого столбца в Products таблице, вероятность того, что вы забыли завершить INSERT инструкцию с запятой. Настройте метод InsertProduct и убедитесь, что у вас используется точка с запятой для разделения операторов INSERT и SELECT.

По умолчанию методы вставки выдают методы, отличные от запросов, что означает, что они возвращают количество затронутых строк. Однако мы хотим InsertProduct , чтобы метод возвращал значение, возвращаемое запросом, а не количество затронутых строк. Для этого измените свойство ExecuteMode метода InsertProduct на Scalar.

Измените свойство ExecuteMode на Скаляр

Рис. 28. Измените свойство ExecuteMode на Scalar (щелкните для просмотра изображения в полном размере)

В следующем коде показан этот новый InsertProduct метод в действии:

Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
Dim new_productID As Integer = Convert.ToInt32(productsAdapter.InsertProduct( _
    "New Product", 1, 1, "12 tins per carton", 14.95, 10, 0, 10, false))
productsAdapter.Delete(new_productID)

Шаг 5. Завершение уровня доступа к данным

Обратите внимание, что ProductsTableAdapters класс возвращает CategoryID и SupplierID значения из таблицы, но не включает Products столбец из CategoryNameCategories таблицы или CompanyName столбца из Suppliers таблицы, хотя это, скорее всего, столбцы, которые мы хотим отобразить при отображении сведений о продукте. Мы можем расширить начальный метод TableAdapter, GetProducts(), чтобы включить как значения столбцов CategoryName, так и CompanyName, что обновит строго типизированную таблицу DataTable, включая эти новые столбцы.

Однако это может представлять проблему, так как методы TableAdapter для вставки, обновления и удаления данных основаны на этом первоначальном методе. К счастью, автоматически созданные методы для вставки, обновления и удаления не затрагиваются вложенными запросами в разделе SELECT. Заботясь о том, чтобы добавлять наши запросы к Categories и Suppliers как подзапросы, а не JOIN, мы избежим необходимости перерабатывать эти методы для изменения данных. Щелкните правой кнопкой мыши на методе в 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

Обновление инструкции SELECT для метода GetProducts()

Рис. 29. Обновление SELECT инструкции GetProducts() метода (щелкните, чтобы просмотреть изображение полного размера)

После обновления GetProducts() метода, чтобы использовать этот новый запрос, DataTable будет содержать два новых столбца: CategoryName и SupplierName.

DataTable для продуктов содержит два новых столбца

Рис. 30. Products В dataTable есть два новых столбца

Чтобы обновить оператор SELECT в методе GetProductsByCategoryID(categoryID), выделите немного времени.

При обновлении GetProducts()SELECTJOIN синтаксиса конструктор наборов данных не сможет автоматически создавать методы для вставки, обновления и удаления данных базы данных с помощью прямого шаблона базы данных. Вместо этого вам придется создавать их вручную, так как мы это делали с методом 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         Products
      
    • GetProductsByCategoryID:

      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 = @CategoryID
      
    • GetProductsBySupplierID:

      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 = @SupplierID
      
    • GetProductByProductID:

      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         Categories
      
    • GetCategoryByCategoryID:

      SELECT     CategoryID, CategoryName, Description
      FROM         Categories
      WHERE CategoryID = @CategoryID
      
  • ПоставщикиTableAdapter

    • GetSuppliers:

      SELECT     SupplierID, CompanyName, Address,
      City, Country, Phone
      FROM         Suppliers
      
    • GetSuppliersByCountry:

      SELECT     SupplierID, CompanyName, Address,
      City, Country, Phone
      FROM         Suppliers
      WHERE Country = @Country
      
    • GetSupplierBySupplierID:

      SELECT     SupplierID, CompanyName, Address,
      City, Country, Phone
      FROM         Suppliers
      WHERE SupplierID = @SupplierID
      
  • АдаптерТаблицыСотрудников

    • GetEmployees:

      SELECT     EmployeeID, LastName, FirstName, Title,
      HireDate, ReportsTo, Country
      FROM         Employees
      
    • GetEmployeesByManager:

      SELECT     EmployeeID, LastName, FirstName, Title, 
      HireDate, ReportsTo, Country
      FROM         Employees
      WHERE ReportsTo = @ManagerID
      
    • GetEmployeeByEmployeeID:

      SELECT     EmployeeID, LastName, FirstName, Title,
      HireDate, ReportsTo, Country
      FROM         Employees
      WHERE EmployeeID = @EmployeeID
      

Конструктор наборов данных после добавления четырех адаптеров таблиц

Рис. 31: Конструктор DataSet после добавления четырех адаптеров таблиц (щелкните, чтобы просмотреть изображение полного размера)

Добавление пользовательского кода в DAL

TableAdapters и DataTables, добавленные в типизированный DataSet, выражаются как файл определения схемы XML (Northwind.xsd). Эти сведения схемы можно просмотреть, щелкнув правой кнопкой мыши на файле Northwind.xsd в Обозревателе решений и выбрав "Просмотреть код".

Файл определения схемы XML (XSD) для типизированного набора данных Northwinds

Рис. 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 класс. Класс SuppliersRow представляет одну запись в таблице Suppliers. Каждый поставщик может поставить от нуля до многих продуктов, поэтому GetProducts() вернёт эти продукты указанного поставщика. Для этого создайте файл класса в папке App_Code с именем SuppliersRow.vb и добавьте следующий код:

Imports NorthwindTableAdapters
Partial Public Class Northwind
    Partial Public Class SuppliersRow
        Public Function GetProducts() As Northwind.ProductsDataTable
            Dim productsAdapter As New ProductsTableAdapter
            Return productsAdapter.GetProductsBySupplierID(Me.SupplierID)
        End Function
    End Class
End Class

Этот частичный класс указывает компилятору, что при создании Northwind.SuppliersRow класса необходимо включить только что определенный GetProducts() метод. Если вы создадите проект, а затем вернетесь в представление классов, вы увидите GetProducts() , как метод Northwind.SuppliersRow.

Метод GetProducts() теперь является частью класса Northwind.SuppliersRow

Рис. 34. Метод GetProducts() теперь является частью Northwind.SuppliersRow класса

Теперь GetProducts() этот метод можно использовать для перечисления набора продуктов для конкретного поставщика, как показано в следующем коде:

Dim suppliersAdapter As New NorthwindTableAdapters.SuppliersTableAdapter()
Dim suppliers As Northwind.SuppliersDataTable = suppliersAdapter.GetSuppliers()
For Each supplier As Northwind.SuppliersRow In suppliers
    Response.Write("Supplier: " & supplier.CompanyName)
    Response.Write("<ul>")
    Dim products As Northwind.ProductsDataTable = supplier.GetProducts()
    For Each product As Northwind.ProductsRow In products
        Response.Write("<li>" & product.ProductName & "</li>")
    Next
    Response.Write("</ul><p> </p>")
Next

Эти данные также можно отобразить в любом из веб-контролов данных ASP.NET. На следующей странице используется элемент управления GridView с двумя полями:

  • BoundField, отображающий имя каждого поставщика и
  • TemplateField, содержащий элемент управления BulletedList, привязанный к результатам, возвращаемым GetProducts() методом для каждого поставщика.

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

SuppliersAndProducts.aspx

<%@ Page Language="VB" CodeFile="SuppliersAndProducts.aspx.vb"
    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>
        <h1>
            Suppliers and Their Products</h1>
        <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="<%# CType(CType(Container.DataItem, System.Data.DataRowView).Row, Northwind.SuppliersRow).GetProducts() %>"
                                 DataTextField="ProductName">
                            </asp:BulletedList>
                        </ItemTemplate>
                    </asp:TemplateField>
                </Columns>
            </asp:GridView>
             </p>
    </div>
    </form>
</body>
</html>

SuppliersAndProducts.aspx.vb

Imports NorthwindTableAdapters
Partial Class SuppliersAndProducts
    Inherits System.Web.UI.Page
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles Me.Load
        Dim suppliersAdapter As New SuppliersTableAdapter
        GridView1.DataSource = suppliersAdapter.GetSuppliers()
        GridView1.DataBind()
    End Sub
End Class

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

Рис. 35. Имя компании поставщика отображается в левом столбце, их продукты в правой части (щелкните, чтобы просмотреть изображение полного размера)

Итоги

При создании веб-приложения создание DAL следует выполнить одним из первых шагов, прежде чем приступить к созданию презентационного уровня. С помощью Visual Studio создание DAL на основе типизированных наборов данных — это задача, которую можно выполнить за 10–15 минут без написания строки кода. Руководства в дальнейшем будут опираться на этот DAL. В следующем руководстве мы определим ряд бизнес-правил и посмотрим, как реализовать их в отдельном уровне бизнес-логики.

Счастливое программирование!

Дополнительные материалы

Дополнительные сведения о разделах, описанных в этом руководстве, см. в следующих ресурсах:

Учебный видеоролик по темам, содержащимся в этом руководстве

Об авторе

Скотт Митчелл, автор семи книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с технологиями Microsoft Web с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его последняя книга Sams обучает ASP.NET 2.0 за 24 часа. С ним можно связаться по адресу mitchell@4GuysFromRolla.com.

Особое спасибо кому

Эта серия учебников была проверена многими полезными рецензентами. Ведущие рецензенты для этого руководства были Рон Грин, Хилтон Гизенау, Деннис Паттерсон, Лиз Шулок, Абель Гомес и Карлос Сантос. Хотите просмотреть мои предстоящие статьи MSDN? Если да, напишите мне на mitchell@4GuysFromRolla.com.