Поделиться через


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

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

Скачать в формате 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 для создания базы данных, если вы хотите использовать другую версию базы данных. Если вы используете другую версию базы данных 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, установленной на сервере базы данных. Если сервер базы данных еще не установлен, сначала необходимо добавить ее на сервер базы данных, выполнив скрипт установки, включенный в скачивание этого руководства.

После установки базы данных перейдите в обозреватель серверов в Visual Studio, щелкните правой кнопкой мыши узел "Подключения к данным" и нажмите кнопку "Добавить подключение". Если обозреватель сервера не отображается, перейдите в представление или обозреватель сервера или нажмите клавиши CTRL+ALT+S. Откроется диалоговое окно "Добавление подключения", где можно указать сервер для подключения, сведения о проверке подлинности и имя базы данных. После успешной настройки сведений о подключении к базе данных и нажатия кнопки "ОК" база данных будет добавлена в качестве узла под узлом подключения к данным. Узел базы данных можно развернуть для изучения таблиц, представлений, хранимых процедур и т. д.

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

Рис. 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. Строки[index]["columnName"]. Свободный ввод DataTable в этом примере демонстрируется тем фактом, что нам нужно получить доступ к имени столбца с помощью строкового или порядкового индекса. С другой стороны, строго типизированный DataTable будет иметь каждый из его столбцов, реализованных как свойства, что приводит к тому, что код выглядит следующим образом: DataTable. Строки[индекс].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, который функционирует как наш уровень доступа к данным. Для dataTable для продуктов TableAdapter будет содержать методы GetProducts(), GetProductByCategoryID(categoryID) и т. д., которые будут вызываться на уровне презентации. Роль DataTable заключается в том, чтобы служить строго типизированными объектами, используемыми для передачи данных между слоями.

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

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

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

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

Сохранение строки подключения в 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. Изменение имени метода с GetData на GetProducts (щелкните, чтобы просмотреть изображение полного размера)

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

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

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

На этом этапе у нас есть типизированный набор данных с одним классом 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();
    }
}

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

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

Хотя в этом примере необходимо написать три строки кода в обработчике событий Page_Load страницы ASP.NET , в будущих руководствах мы рассмотрим, как использовать 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, отображающую только те продукты в указанной категории. В следующем примере показаны все продукты, которые находятся в категории "Напитки", которые имеют идентификатор категории 1.

Beverages.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(), который можно передать типизированный набор данных, строго типизированный DataTable или один или несколько DataRows. Если вы оставили флажок "GenerateDBDirectMethods" при первом создании таблицыAdapter прямой шаблон базы данных также будет реализован с помощью методов Insert(), Update()и Delete().

Оба шаблона изменения данных используют свойства InsertCommand, UpdateCommand и DeleteCommand TableAdapter для выдачи команд 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 единиц акций или меньше:

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 .

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

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

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

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

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

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

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

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

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

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

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

Рис. 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

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

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

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

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

Рис. 30. DataTable products имеет два новых столбца

Чтобы обновить предложение SELECT в методе GetProductsByCategoryID(categoryID), сделайте некоторое время.

Если вы обновляете GetProducts() SELECT с помощью синтаксиса JOIN , конструктор наборов данных не сможет автоматически создавать методы для вставки, обновления и удаления данных базы данных с помощью прямого шаблона базы данных. Вместо этого вам придется вручную создавать их так же, как и с методом InsertProduct ранее в этом руководстве. Кроме того, необходимо вручную указать значения свойств InsertCommand, UpdateCommand и DeleteCommand, если вы хотите использовать шаблон пакетного обновления.

Добавление оставшихся таблицAdapters

До сих пор мы рассмотрели только работу с одной таблицей базы данных TableAdapter. Однако база данных Northwind содержит несколько связанных таблиц, с которыми мы должны работать в нашем веб-приложении. Типизированный набор данных может содержать несколько связанных наборов данных. Поэтому для завершения DAL необходимо добавить таблицы DataTable для других таблиц, которые мы будем использовать в этих руководствах. Чтобы добавить новый TableAdapter в типизированный набор данных, откройте конструктор наборов данных, щелкните правой кнопкой мыши конструктор и нажмите кнопку "Добавить / TableAdapter". В результате вы создадите таблицу DataTable и TableAdapter и ознакомьтесь с мастером, рассмотренным ранее в этом руководстве.

Чтобы создать следующие таблицыAdapters и методы, выполните несколько минут, используя следующие запросы. Обратите внимание, что запросы в ProductsTableAdapter включают вложенные запросы, чтобы получить имена категорий и поставщиков каждого продукта. Кроме того, если вы следуют вместе, вы уже добавили методы GetProducts() класса ProductsTableAdapter и 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
      
  • EmployeesTableAdapter

    • 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
      

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

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

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

ТаблицыAdapters и DataTables, добавленные в типизированный набор данных, выражаются в виде файла определения схемы 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 . Класс 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.

Метод 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. В следующем руководстве мы определим ряд бизнес-правил и посмотрим, как реализовать их в отдельном уровне бизнес-логики.

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

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

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

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

Об авторе

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

Особое спасибо

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