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


Использование существующих хранимых процедур для адаптеров таблиц TableAdapter типизированного DataSet (VB)

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

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

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

Введение

В предыдущем руководстве мы узнали, как табличные наборы данных в TableAdapters можно настроить для использования хранимых процедур для доступа к данным, а не с помощью нерегламентированных инструкций SQL. В частности, мы изучили, как мастер TableAdapter автоматически создавать эти хранимые процедуры. При переносе устаревшего приложения на ASP.NET 2.0 или при создании веб-сайта ASP.NET 2.0 вокруг существующей модели данных вероятность того, что база данных уже содержит необходимые хранимые процедуры. Кроме того, вы можете создать хранимые процедуры вручную или с помощью какого-либо средства, отличного от мастера TableAdapter, который автоматически создает хранимые процедуры.

В этом руководстве мы рассмотрим, как настроить TableAdapter для использования существующих хранимых процедур. Так как база данных Northwind имеет только небольшой набор встроенных хранимых процедур, мы также рассмотрим шаги, необходимые для ручного добавления новых хранимых процедур в базу данных через среду Visual Studio. Давайте приступим!

Примечание.

В руководстве по изменениям базы данных-оболочке в руководстве по транзакциям мы добавили методы в TableAdapter для поддержки транзакций (BeginTransactionи CommitTransactionт. д.). Кроме того, транзакции можно управлять полностью в хранимой процедуре, которая не требует изменений в коде уровня доступа к данным. В этом руководстве мы рассмотрим команды T-SQL, используемые для выполнения инструкций хранимой процедуры в области транзакции.

Шаг 1. Добавление хранимых процедур в базу данных Northwind

Visual Studio упрощает добавление новых хранимых процедур в базу данных. Давайте добавим новую хранимую процедуру в базу данных Northwind, которая возвращает все столбцы из Products таблицы для тех, у которых есть определенное CategoryID значение. В окне обозревателя серверов разверните базу данных Northwind, чтобы ее папки — диаграммы баз данных, таблицы, представления и т. д. отображались. Как мы видели в предыдущем руководстве, папка хранимых процедур содержит существующие хранимые процедуры базы данных. Чтобы добавить новую хранимую процедуру, просто щелкните правой кнопкой мыши папку хранимых процедур и выберите параметр "Добавить новую хранимую процедуру" в контекстном меню.

Щелкните правой кнопкой мыши папку хранимых процедур и добавьте новую хранимую процедуру

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

Как показано на рисунке 1, при выборе параметра "Добавить новую хранимую процедуру" откроется окно скрипта в Visual Studio с структурой скрипта SQL, необходимого для создания хранимой процедуры. Это наша задача, чтобы провести этот скрипт и выполнить его, в какой момент хранимая процедура будет добавлена в базу данных.

Введите следующий скрипт:

CREATE PROCEDURE dbo.Products_SelectByCategoryID 
(
    @CategoryID int
)
AS
SELECT ProductID, ProductName, SupplierID, CategoryID, 
       QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, 
       ReorderLevel, Discontinued
FROM Products
WHERE CategoryID = @CategoryID

Этот скрипт при выполнении добавит новую хранимую процедуру в базу данных Northwind с именем Products_SelectByCategoryID. Эта хранимая процедура принимает один входной параметр (@CategoryIDтип int) и возвращает все поля для этих продуктов с соответствующим значением CategoryID .

Чтобы выполнить этот CREATE PROCEDURE скрипт и добавить хранимую процедуру в базу данных, щелкните значок "Сохранить" на панели инструментов или нажмите клавиши CTRL+S. После этого папка хранимых процедур обновляется, показывая только что созданную хранимую процедуру. Кроме того, скрипт в окне изменит тонкость на CREATE PROCEDURE dbo.Products_SelectProductByCategoryID ALTER PROCEDURE dbo.Products_SelectProductByCategoryID. CREATE PROCEDURE добавляет новую хранимую процедуру в базу данных, в то время как ALTER PROCEDURE обновляет существующую. После начала скрипта ALTER PROCEDUREизменится, изменив входные параметры хранимых процедур или инструкции SQL и щелкнув значок "Сохранить", изменит хранимую процедуру с этими изменениями.

На рисунке 2 показана Visual Studio после сохранения хранимой Products_SelectByCategoryID процедуры.

Хранимая процедура Products_SelectByCategoryID добавлена в базу данных

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

Шаг 2. Настройка TableAdapter для использования существующей хранимой процедуры

Теперь, когда Products_SelectByCategoryID хранимая процедура была добавлена в базу данных, мы можем настроить уровень доступа к данным для использования этой хранимой процедуры при вызове одного из его методов. В частности, мы добавим GetProductsByCategoryID(<_i22_>categoryID)<!--_i22_--> метод ProductsTableAdapter в NorthwindWithSprocs набор данных Typed DataSet, который вызывает Products_SelectByCategoryID только что созданную хранимую процедуру.

Начните с открытия NorthwindWithSprocs набора данных. Щелкните правой ProductsTableAdapter кнопкой мыши и выберите команду "Добавить запрос", чтобы запустить мастер настройки запросов TableAdapter. В предыдущем руководстве мы решили создать новую хранимую процедуру для нас. Однако в этом руководстве мы хотим подключить новый метод TableAdapter к существующей Products_SelectByCategoryID хранимой процедуре. Поэтому выберите вариант "Использовать существующую хранимую процедуру" на первом шаге мастера и нажмите кнопку "Далее".

Выберите параметр

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

На следующем экране представлен раскрывающийся список, заполненный хранимыми процедурами базы данных. Выбор хранимой процедуры содержит входные параметры слева и поля данных, возвращаемые (если таковые) справа. Выберите хранимую Products_SelectByCategoryID процедуру из списка и нажмите кнопку "Далее".

Выберите хранимую процедуру Products_SelectByCategoryID

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

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

Так как хранимая Products_SelectByCategoryID процедура возвращает все продукты, принадлежащие определенной категории, выберите первый ответ — табличные данные — и нажмите кнопку "Далее".

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

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

Все, что остается, заключается в том, чтобы указать, какие шаблоны методов следует использовать с именами этих методов. Оставьте флажок Fill a DataTable и Return a DataTable, но переименуйте методы FillByCategoryID в и GetProductsByCategoryID. Затем нажмите кнопку "Далее", чтобы просмотреть сводку задач, которые будет выполнять мастер. Если все выглядит правильно, нажмите кнопку "Готово".

Назовите методы FillByCategoryID и GetProductsByCategoryID

Рис. 6. Назовите методы FillByCategoryID и GetProductsByCategoryID (щелкните, чтобы просмотреть изображение полного размера)

Примечание.

Только что созданные FillByCategoryID методы TableAdapter и GetProductsByCategoryIDожидают входного параметра типа Integer. Это входное значение параметра передается в хранимую процедуру через его @CategoryID параметр. При изменении Products_SelectByCategory параметров хранимой процедуры необходимо также обновить параметры для этих методов TableAdapter. Как описано в предыдущем руководстве, это можно сделать одним из двух способов: путем ручного добавления или удаления параметров из коллекции параметров или повторного запуска мастера TableAdapter.

Шаг 3. ДобавлениеGetProductsByCategoryID(categoryID)метода в BLL

GetProductsByCategoryID После завершения метода DAL следующим шагом является предоставление доступа к этому методу на уровне бизнес-логики. ProductsBLLWithSprocs Откройте файл класса и добавьте следующий метод:

<System.ComponentModel.DataObjectMethodAttribute _
    (System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductsByCategoryID(ByVal categoryID As Integer) _
    As NorthwindWithSprocs.ProductsDataTable
    Return Adapter.GetProductsByCategoryID(categoryID)
End Function

Этот метод BLL просто возвращает ProductsDataTable возвращаемый из ProductsTableAdapter метода S GetProductsByCategoryID . Атрибут DataObjectMethodAttribute предоставляет метаданные, используемые мастером настройки источника данных ObjectDataSource. В частности, этот метод появится в раскрывающемся списке вкладок SELECT.

Шаг 4. Отображение продуктов по категориям

Чтобы протестировать только что добавленную Products_SelectByCategoryID хранимую процедуру и соответствующие методы DAL и BLL, давайте создадим страницу ASP.NET, содержащую DropDownList и GridView. В раскрывающемся списке будут перечислены все категории в базе данных, а GridView будет отображать продукты, принадлежащие выбранной категории.

Примечание.

Мы создали основные и подробные интерфейсы с помощью DropDownLists в предыдущих руководствах. Дополнительные сведения о реализации такого основного или подробного отчета см. в руководстве по фильтрации master/Detail С помощью dropDownList .

ExistingSprocs.aspx Откройте страницу в AdvancedDAL папке и перетащите dropDownList из панели элементов в конструктор. Задайте для свойства Categories DropDownList ID значение и его AutoPostBack свойствоTrue. Затем из смарт-тега привязать DropDownList к новому объекту ObjectDataSource с именем CategoriesDataSource. Настройте ObjectDataSource, чтобы он извлекал данные из CategoriesBLL метода класса GetCategories . Установите раскрывающийся список на вкладках UPDATE, INSERT и DELETE (Нет).

Получение данных из метода GetCategoriesBLL класса GetCategories

Рис. 7. Получение данных из CategoriesBLL метода класса GetCategories (щелкните, чтобы просмотреть изображение полного размера)

Задайте раскрывающимся спискам в вкладках UPDATE, INSERT и DELETE (Нет)

Рис. 8. Задайте раскрывающийся список в вкладках UPDATE, INSERT и DELETE (Нет) (Щелкните, чтобы просмотреть изображение полного размера)

После завершения мастера ObjectDataSource настройте DropDownList для отображения CategoryName поля данных и использования CategoryID поля в качестве Value поля для каждого ListItem.

На этом этапе декларативная разметка DropDownList и ObjectDataSource должны выглядеть следующим образом:

<asp:DropDownList ID="Categories" runat="server" AutoPostBack="True" 
    DataSourceID="CategoriesDataSource" DataTextField="CategoryName" 
    DataValueField="CategoryID">
</asp:DropDownList>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
    OldValuesParameterFormatString="original_{0}" 
    SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

Затем перетащите GridView в конструктор, поместив его под dropDownList. Задайте для GridView значение ID ProductsByCategory и, из смарт-тега, привязать его к новому объекту ObjectDataSource с именем ProductsByCategoryDataSource. ProductsByCategoryDataSource Настройте ObjectDataSource для использования ProductsBLLWithSprocs класса, получив его данные с помощью GetProductsByCategoryID(categoryID) метода. Так как этот GridView будет использоваться только для отображения данных, задайте раскрывающийся список на вкладках UPDATE, INSERT и DELETE (Нет) и нажмите кнопку "Далее".

Настройка ObjectDataSource для использования класса ProductsBLLWithSprocs

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

Получение данных из метода GetProductsByCategoryID(categoryID)

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

Метод, выбранный на вкладке SELECT, ожидает параметр, поэтому последний шаг мастера предложит нам источник параметра. Задайте раскрывающийся список источника параметров элементу управления и выберите Categories элемент управления в раскрывающемся списке ControlID. Чтобы завершить работу мастера, нажмите кнопку Готово .

Используйте раскрывающийся список категорий в качестве источника параметра categoryID

Рис. 11. Используйте Categories DropDownList в качестве источника categoryID параметра (щелкните, чтобы просмотреть изображение полного размера)

После завершения мастера ObjectDataSource Visual Studio добавит BoundFields и CheckBoxField для каждого поля данных продукта. Вы можете настроить эти поля, как вы видите нужные.

Посетите страницу через браузер. При посещении страницы выбрана категория "Напитки" и соответствующие продукты, перечисленные в сетке. Изменение раскрывающегося списка на альтернативную категорию, как показано на рис. 12, вызывает обратную передачу и перезагрузит сетку с продуктами только что выбранной категории.

Продукты в категории

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

Шаг 5. Упаковка инструкций хранимой процедуры в пределах области транзакции

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

  • Использование классов в System.Transactions пространстве имен
  • Использование уровня доступа к данным используйте классы ADO.NET, например SqlTransaction, и
  • Добавление команд транзакций T-SQL непосредственно в хранимую процедуру

Изменения базы данных-оболочки в руководстве по транзакциям использовали классы ADO.NET в DAL. В оставшейся части этого руководства рассматривается управление транзакцией с помощью команд T-SQL из хранимой процедуры.

Три ключевых команды SQL для запуска, фиксации и отката транзакции выполняются BEGIN TRANSACTIONCOMMIT TRANSACTIONвручную и ROLLBACK TRANSACTIONсоответственно. Как и в случае с подходом ADO.NET, при использовании транзакций из хранимой процедуры необходимо применить следующий шаблон:

  1. Укажите начало транзакции.
  2. Выполните инструкции SQL, составляющие транзакцию.
  3. Если в любом из операторов из шага 2 возникает ошибка, откат транзакции.
  4. Если все инструкции из шага 2 завершены без ошибок, зафиксируйте транзакцию.

Этот шаблон можно реализовать в синтаксисе T-SQL с помощью следующего шаблона:

BEGIN TRY
  BEGIN TRANSACTION -- Start the transaction
  ... Perform the SQL statements that makeup the transaction ...
  -- If we reach here, success!
  COMMIT TRANSACTION
END TRY
BEGIN CATCH 
  -- Whoops, there was an error
  ROLLBACK TRANSACTION
  -- Raise an error with the 
  -- details of the exception   
  DECLARE @ErrMsg nvarchar(4000),
          @ErrSeverity int 
  SELECT @ErrMsg = ERROR_MESSAGE(), 
         @ErrSeverity = ERROR_SEVERITY() 
 
  RAISERROR(@ErrMsg, @ErrSeverity, 1) 
END CATCH

Шаблон начинается с определения TRY...CATCH блока, создания нового для SQL Server 2005. Как и в Try...Catch блоках в Visual Basic, блок SQL TRY...CATCH выполняет инструкции в блоке TRY . Если любая инструкция вызывает ошибку, элемент управления немедленно передается в CATCH блок.

Если нет ошибок при выполнении инструкций SQL, которые создают транзакцию, COMMIT TRANSACTION инструкция фиксирует изменения и завершает транзакцию. Однако если одна из инструкций приводит к ошибке, ROLLBACK TRANSACTION блок CATCH возвращает базу данных в состояние до начала транзакции. Хранимая процедура также вызывает ошибку с помощью команды RAISERROR, которая приводит SqlException к возникновению в приложении.

Примечание.

Так как блок не является новым для SQL Server 2005, приведенный TRY...CATCH выше шаблон не будет работать, если вы используете более старые версии Microsoft SQL Server.

Рассмотрим конкретный пример. Ограничение внешнего ключа существует между Categories таблицами и Products таблицами, что означает, что каждое CategoryID поле в Products таблице должно сопоставляться со CategoryID значением Categories в таблице. Любое действие, которое нарушает это ограничение, например попытку удалить категорию, связанную с продуктами, приводит к нарушению ограничений внешнего ключа. Чтобы проверить это, вернитесь к примеру "Обновление и удаление существующих двоичных данных" в разделе "Работа с двоичными данными" (~/BinaryData/UpdatingAndDeleting.aspx). На этой странице перечислены все категории в системе вместе с кнопками "Изменить" и "Удалить" (см. рис. 13), но при попытке удалить категорию, связанную с продуктами , такими как напитки, удаление завершается ошибкой из-за нарушения ограничений внешнего ключа (см. рис. 14).

Каждая категория отображается в GridView с кнопками редактирования и удаления

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

Невозможно удалить категорию с существующими продуктами

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

Представьте себе, что мы хотим разрешить удалять категории независимо от того, имеют ли они связанные продукты. Если категория с продуктами будет удалена, представьте, что мы хотим также удалить существующие продукты (хотя другим вариантом будет просто задать значения NULLпродуктовCategoryID). Эту функцию можно реализовать с помощью каскадных правил ограничения внешнего ключа. Кроме того, можно создать хранимую процедуру, которая принимает @CategoryID входной параметр, и при вызове явным образом удаляет все связанные продукты, а затем указанную категорию.

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

CREATE PROCEDURE dbo.Categories_Delete
(
    @CategoryID int
)
AS
-- First, delete the associated products...
DELETE FROM Products
WHERE CategoryID = @CategoryID
-- Now delete the category
DELETE FROM Categories
WHERE CategoryID = @CategoryID

Хотя это, безусловно, приведет к удалению связанных продуктов и категорий, это не делается под зонтиком транзакции. Представьте, что существует другое ограничение внешнего ключа, Categories которое запрещает удаление определенного @CategoryID значения. Проблема заключается в том, что в таком случае все продукты будут удалены перед попыткой удалить категорию. Чистый результат заключается в том, что для такой категории эта хранимая процедура будет удалять все его продукты в то время как категория остается, так как она по-прежнему имеет связанные записи в какой-то другой таблице.

Если хранимая процедура была заключена в область транзакции, однако удаление Products таблицы будет откатировано в лицо сбоем удаления Categories. Следующий скрипт хранимой процедуры использует транзакцию для обеспечения атомарности между двумя DELETE операторами:

CREATE PROCEDURE dbo.Categories_Delete
(
    @CategoryID int
)
AS
BEGIN TRY
  BEGIN TRANSACTION -- Start the transaction
  -- First, delete the associated products...
  DELETE FROM Products
  WHERE CategoryID = @CategoryID
  -- Now delete the category
  DELETE FROM Categories
  WHERE CategoryID = @CategoryID
  -- If we reach here, success!
  COMMIT TRANSACTION
END TRY
BEGIN CATCH 
  -- Whoops, there was an error
  ROLLBACK TRANSACTION
  -- Raise an error with the 
  -- details of the exception   
  DECLARE @ErrMsg nvarchar(4000),
          @ErrSeverity int 
  SELECT @ErrMsg = ERROR_MESSAGE(), 
         @ErrSeverity = ERROR_SEVERITY() 
 
  RAISERROR(@ErrMsg, @ErrSeverity, 1) 
END CATCH

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

Шаг 6. ОбновлениеCategoriesTableAdapter

Хотя мы добавили Categories_Delete хранимую процедуру в базу данных, DAL в настоящее время настраивается для использования нерегламентированных инструкций SQL для выполнения удаления. Нам нужно обновить CategoriesTableAdapter и указать ему использовать Categories_Delete хранимую процедуру.

Примечание.

Ранее в этом руководстве мы работали с набором NorthwindWithSprocs данных. Но этот набор данных имеет только одну сущность, ProductsDataTableи нам нужно работать с категориями. Таким образом, для остальной части этого руководства, когда я говорю о уровне доступа к данным, который я ссылаюсь на Northwind Набор данных, который мы впервые создали в руководстве по созданию уровня доступа к данным.

Откройте набор данных Northwind, выберите CategoriesTableAdapterи перейдите к окно свойств. Окно свойств перечисляет InsertCommand,UpdateCommandDeleteCommand, и используется TableAdapter, а также его имя и SelectCommand сведения о подключении. DeleteCommand Разверните свойство, чтобы просмотреть его сведения. Как показано на рисунке 15, DeleteCommand свойство s CommandType имеет значение Text, которое указывает ему отправить текст в свойстве в CommandText виде нерегламентированного SQL-запроса.

Выберите CategoriesTableAdapter в конструкторе, чтобы просмотреть его свойства в окне свойств

Рис. 15. Выберите CategoriesTableAdapter конструктор, чтобы просмотреть его свойства в окне свойств

Чтобы изменить эти параметры, выберите текст (DeleteCommand) в окно свойств и выберите (Создать) из раскрывающегося списка. Это приведет к очистке параметров для CommandTextсвойств CommandType, а также Parameters свойств. Затем задайте CommandType для свойства StoredProcedure значение и введите имя хранимой процедуры для CommandText (dbo.Categories_Delete). Если вы обязательно введите свойства в этом порядке — сначала CommandType , а затем CommandText — Visual Studio автоматически заполняет коллекцию параметров. Если вы не вводите эти свойства в этом порядке, необходимо вручную добавить параметры через редактор коллекции параметров. В любом случае необходимо щелкнуть многоточие в свойстве "Параметры", чтобы открыть редактор коллекции параметров, чтобы убедиться, что были внесены правильные параметры параметров (см. рис. 16). Если в диалоговом окне нет параметров, добавьте @CategoryID этот параметр вручную (не нужно добавлять @RETURN_VALUE этот параметр).

Убедитесь в правильности параметров

Рис. 16. Убедитесь в правильности параметров

После обновления DAL удаление категории автоматически удаляет все связанные с ним продукты и делает это под зонтиком транзакции. Чтобы проверить это, вернитесь на страницу "Обновление и удаление существующих двоичных данных" и нажмите кнопку "Удалить" для одной из категорий. При одном щелчке мыши категория и все связанные с ней продукты будут удалены.

Примечание.

Перед тестированием Categories_Delete хранимой процедуры, которая будет удалять ряд продуктов вместе с выбранной категорией, может быть разумно сделать резервную копию базы данных. Если вы используете NORTHWND.MDF базу данных, App_Dataпросто закройте Visual Studio и скопируйте файлы MDF и LDF в App_Data другую папку. После тестирования функциональных возможностей можно восстановить базу данных, закрыв Visual Studio и заменив текущие файлы MDF и LDF на App_Data копии резервных копий.

Итоги

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

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

Об авторе

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

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

Эта серия учебников была проверена многими полезными рецензентами. Ведущие рецензенты для этого руководства были Хилтон Geisenow, S ren Джейкоб Лоритсен и Тереса Мерфи. Хотите просмотреть мои предстоящие статьи MSDN? Если да, упадите меня линию в mitchell@4GuysFromRolla.com.