Доступ к информации базы данных в ASP.NET 2.0
ОГЛАВЛЕНИЕ
К примеру, для того, чтобы отобразить содержимое таблицы базы данных на веб-странице с ASP.NET 1.x, вам нужно было бы написать код для того, чтобы:
- осуществить соединение к базе данных,
- выполнить команду,
- получить результат,
- работать с результатами; связать их с элементом управления данными (таким как DataGrid)
Осуществляем доступ к информации базы данных
ASP.NET 2.0 предоставляет два элемента управления источниками данных разработанных специально для осуществления доступа к информации из базы данных:
- SqlDataSource - пригоден для доступа к информации любой реляционной базы данных. "Sql" в названии не имеет отношения к Microsoft SQL Server, скорее к синтаксису SQL относительно запросов к реляционным базам данных, так как элемент SqlDataSource может быть использован для осуществления доступа не только к базам данных Microsoft SQL Server, но и к базам Microsoft Access, Oracle ... в общем к любому информационному массиву, соответствующему OLE-DB либо ODBC.
- AccessDataSource - схож SqlDataSource. Ключевой разницей является то, что вместо того чтобы требовать строку соединения к базе данных, элемент AccessDataSource позволяет вам просто указать путь к Access файлу .MDB посредством свойства DataFile.
Оба элемента управления имеют практически одинаковый набор свойств, и единственной разницей будет то, как вы укажете информацию о соединении. В принципе, элемент AccessDataSource по большому счету является излишком, так как доступ к базам данных Microsoft Access может быть сделан посредством SqlDataSource. (Вам, конечно же, придется предоставить строку соединения вместо простого указания пути к файлу, но Visual Studio 2005 может автоматически создавать соответствующие строки соединения для баз данных, расположенных в вашем App_Data каталоге.)
В данной статье мы подробно рассмотрим SqlDataSource и немного поговорим об этой разнице с AccessDataSource. Более того, элементы SqlDataSource и AccessDataSource могут быть использованы для осуществления доступа и выполнения изменений информации; тем не менее статья освещает только доступ к данным. Мы рассмотрим способ использования элементов управления источниками данных для выполнения изменений информации в следующей статье.
Для начала, запустите Visual Studio 2005 и создайте новый веб-сайт или скачайте пример кода из приложения к данной статье. Я буду рассматривать осуществление доступа, получение результата и отображение информации из базы данных Microsoft Access Northwind, которая также приведена в приложении к данной статье. (Заметьте, я использую более укороченную версию базы Northwind, где я для уменьшения объема файла удалил Forms, Modules, Reports и Macros.)
ASP.NET 2.0 предоставляет каталог App_Data - его вы можете добавить в ваш веб-сайт для того, чтобы хранить в нем такие файлы, как XML-файлы, файлы базы данных Access (.mdb), файлы базы данных SQL Server 2000 и SQL Server 2005 Express (.mdf). В приложении вы найдете файл Northwind.mdb в каталоге App_Data. Конечно, ASP.NET может работать с базами данных которые не находятся в каталоге App_Data. При использовании профессиональных систем баз данных, таких как Microsoft SQL Server, они обычно зарегистрированы на удаленном сервере баз данных.
Неважно, где находится ваша база данных - как только у вас будет база данных, с которой вам нужно работать, для осуществления доступа к ее содержимому вы захотите добавить SqlDataSource (либо AccessDataSource) на страницу. Начните с создания новой ASP.NET-страницы и перейдите в режим просмотра Design. Далее, перетащите элемент SqlDataSource из Toolbox в Designer. Элементы управления источниками отображены в качестве маленького прямоугольника в Designer; они не порождают HTML-разметку при посещении страницы обозревателем. Для того чтобы правильно осуществить доступ к данным, мы должны настроить некоторые свойства SqlDataSource, которые указывают используемую базу данных и запрос, который нужно выполнить. Хотя это можно осуществить посредством панели Properties, это выполняется быстрее, при помощи мастера (wizard), которую можно вызвать нажатием на ссылку "Configure Data Source" (Настроить источник данных) в смарт-теге элемента SqlDataSource.
Настраиваем элемент управления SqlDataSource при помощи мастера (Wizard)
При нажатии на ссылку "Configure Data Source" (Настроить источник данных) появляется мастер, который просит вас выбрать необходимую информацию. На первом экране, показанном ниже, он запрашивает вас выбрать базу данных, из которой будет получена данная информация. Выпадающий список перечисляет существующие строки соединения, указанные в секции<connectionStrings> файла Web.config и те, что находятся в каталоге App_Data. Для соединения к базе данных, зарегистрированной где-то в другом месте, нажмите кнопку New Connection (новое соединение) и укажите сервер баз данных и информацию аутентификации.

После того, как вы выбрали базу данных, нажмите кнопку Next (далее). Если вы не указали <connectionStrings> для данной базы данных в файле Web.config, мастер сделает это за вас, если вам так будет угодно. Есть множество преимуществ в указании строки соединения в файле Web.config, ключевым из них будет то, что оно делает ваше приложение более удобным в сопровождении. Представьте, что вам позже придется переместить серверы баз данных и необходимо обновить строку соединения. Если она у вас объявлена в файле Web.config, то вам нужно ее обновить только там, а если нет, то вам придется найти все страницы, которые ссылаются посредством данной строки, и изменить ее там. Более того, помещением вашей строки соединения в файл Web.config вы можете зашифровать ваши строки соединения в качестве защиты сайта. (Тем не менее, мастер не сможет автоматически записывать в секцию <connectionStrings> в случае, если она у вас зашифрована.) Следующее изображение показывает данный второй шаг; как вы видите, я попросил сохранить данную строку соединения в Web.config, назвав ее NorthwindConnectionString.
Сравнение с AccessDataSource. |
---|
AccessDataSource отличается от SqlDataSource одним небольшим аспектом - вместо того, чтобы указывать строку соединения, как в случае с SqlDataSource, AccessDataSource просит ввести путь к файлу базы данных. Тем самым изображение, приведенное выше ,отличается в случае с AccessDataSource. В частности, первое изображение просить ввести путь к файлу; поскольку строка соединения не используется, второе изображение не относится к AccessDataSource. Тем не менее все последующие изображения идентичны для обоих элементов. |
На следующем изображении вас попросят выбрать ваше SELECT-выражение. При помощи кнопок с зависимой фиксацией расположенных на верхней части экрана вы можете выбрать таблицы либо представление, и выбрать те столбцы, которые необходимо получить (так, как это показано в следующем изображении), либо написать выражение SQL или выбрать хранимую процедуру. Если вы решите указать колонки из таблицы или представления, вам всего лишь надо выбрать таблицу или представление из выпадающего списка и затем выбрать колонки. В данном примере я запрашиваю колонки ProductID, ProductName, UnitPrice и UnitsInStock из таблицы Products. Данный запрос возвратит все записи из таблицы Products.

По умолчанию, информация возвращена в таком порядке, в каком она хранится в базе данных. Если у вас есть групповой индекс (как, к примеру, ограничение по первичному ключу) по таблице, то данные будут отсортированы соответственно по колонкам, составляющим индекс; в противном случае данные будут отсортированы по мере поступления в базу данных. Для указания какого-либо другого способа сортировки, нажмите на кнопку ORDER BY. Здесь вы можете выбрать колонки, по которым будет производиться сортировка и порядок сортировки (убывания либо возрастания). Настройки в данном окне управляют выражением ORDER BY в SELECT.

Если вам нужно получить колонки из таблицы или представления, то вы сможете настроить только простые запросы, которые включают в себя только одну таблицу или представление. В случае с реляционными базами данных, данные также находятся в связанных таблицах, и для получения ее нам необходимо использовать JOIN (соединение). Либо данные могут быть доступны при помощи использования хранимой процедуры. В данных случаях нам необходимо выбрать "Specify a custom SQL statement of stored procedure" (Указать специализированное выражение SQL для хранимой процедуры) и затем нажать Next (далее). Тем самым вы попадете на следующий этап, указанный на изображении. Здесь вы можете вписать свой собственный SQL-запрос в выражение SELECT, либо выбрать хранимую процедуру из тех, которые указаны в списке.

Если вы создаете достаточно громоздкий запрос, то вам, наверное, захочется использовать Query Builder (создатель запросов). Нажав на кнопку Query Builder, вы запустите графический инструмент, который позволит вам выбрать таблицы для осуществления запросов и автоматически добавит синтаксис соединений JOIN. Следующее изображение показывает Query Builder в действии. Как вы видите, я добавил две таблицы в окне запроса - Categories и Products - и выбрал две колонки из данных таблиц. Я также указал то, что результаты должны быть сначала отсортированы по колонке CategoryName, а затем - по ProductName. Парочка щелчков - и Query Builder создаст необходимый запрос.

После создания вашего SELECT-выражения - посредством таблицы или представления, с помощью хранимой процедуры или написания специализированного запроса , нажмите на кнопку Next (далее) для того, чтобы перейти к последнему экрану. Здесь вы выполните тот запрос, который вы создали, и увидите его результаты. Нажмите кнопку Finish для завершения.

Настраиваем элемент управления SqlDataSource при помощи панели свойств
Если вы не любите использовать услуги мастера, а предпочитаете настраивать все вручную, то вы легко можете это сделать, настроив следующие свойства SqlDataSource, либо используя панель свойств (Properties pane), либо при помощи декларативной разметки:
- ConnectionString - установите это в строку соединения вашей базы данных. Это может быть либо полноценная строка соединения либо ссылка на строку в вашем <connectionStrings> секции файла Web.config. Если это ссылка на строку запроса Web.config , то используйте данный синтаксис: ConnectionString="<%$ ConnectionStrings:ConnectionStringName %>".
Для элемента управления AccessDataSource установите свойство DataFile в значение пути к файлу базы данных Access, к примеру ~/App_Data/Northwind.mdb.
- ProviderName - название используемого провайдера базы данных. По умолчанию SqlDataSource использует SqlClient, что является провайдером, разработанным специально для for Microsoft SQL Server. Если вы используете другую базу данных, тогда вам надо будет соответственно установить данное свойство. .NET Framework обладает четырьмя провайдерами баз данных:
- System.Data.SqlClient - используется для быстрого доступа к Microsoft SQL Server (установлен по умолчанию)
- System.Data.OleDb - используется для доступа к информационным массивам, поддерживающим OLE-DB, таким как Access
- System.Data.Odbc - используется для доступа к более старым информационным массивам, поддерживающим ODBC
- System.Data.OracleClient - используется для доступа к базам данных Oracle
- SelectCommand - запрос, который нужно осуществить к базе данных, к примеру SELECT [ProductID], [ProductName] FROM [Products] ORDER BY [ProductName].
Получаем данные из элемента управления SqlDataSource
Обычно при осуществлении доступа к информации базы данных мы хотим каким-то образом отобразить или обработать результат. Чтобы просто отобразить информацию, используйте любой элемент управления данными (к примеру, GridView) и установите его свойство DataSourceID в ID элемента SqlDataSource. (Это легче всего сделать посредством смарт-тега элемента управления данными в режиме просмотра Design. Там вы найдете выпадающий список, где вы можете выбрать элемент SqlDataSource, с которым нужно связать элемент управления данными.) Когда страница будет посещена, элемент управления данными автоматически получит данные из связанного с ним элемента SqlDataSource и отобразит результат. Пример вы можете загрузить в конце данной статьи. Следующее изображение демонстрирует GridView при отображении колонок ProductID, ProductName, UnitPrice и UnitsInStock из таблицы Products , которая была отсортирована в возрастающем порядке по колонке UnitPrice , и второстепенной сортировкой по ProductNamе.
Вы также можете программно осуществить доступ к содержимому элемента SqlDataSource и работать с данными в коде. До того как мы перейдем к этому, давайте рассмотрим, как информация возвращается из базы данных элементом SqlDataSource. Если вы знакомы с доступом к данным в ASP.NET версии 1.x , то вы знаете, что существуют две техники/объекта для работы с данными:
- DataReaders - предлагают доступный только для чтения доступ к базе данных, требует наличие активного соединения. Более продуктивен чем DataSets/DataTables/DataViews.
- DataSet/DataTable/DataView - DataTable предлагает свободный доступ, настраиваемое представление запроса. DataSet является набором DataTables. DataViews может быть использовано для получения фильтрованного или сортированного набора записей в DataTable.
Элемент SqlDataSource может вернуть DataSet либо DataReader. Вы можете это настроить при помощи свойства DataSourceMode SqlDataSource, где DataSet установлен по умолчанию. Для того чтобы использовать сортировку и листание в GridView, или возможность кэширования SqlDataSource вам нужно получить DataSet от SqlDataSource. (Элемент AccessDataSource также имеет свойство DataSourceMode.)
Чтобы программно получить доступ к содержимому элемента SqlDataSource, вызовите метод Select() . Данный метод предполагает наличие единого входного параметра типа DataSourceSelectArguments. Данный параметр может быть использован для передачи запросамии к SqlDataSource для посылки информации до ее возврата. К примеру, работая с сортирующим GridView, сортировка колонки вызывает метод Select(), передавая экземпляр DataSourceSelectArguments с его свойством SortExpression, установленным в названии той колонки, которую пользователь выбрал для сортировки. Если вы не хотите, чтобы SqlDataSource производил сортировку, фильтрацию или возвращал только "страницу" данных, просто передайте DataSourceSelectArguments.Empty, как это показано в следующем коде.
Метод Select() возвращает объект, который реализует IEnumerable. В частности, он возвращает DataView если свойство DataSourceMode было установлено в DataSet, и объект DataReader если он установлен в DataReader. Следующие два примера показывают способ программного доступа к данным из SqlDataSource. Первый написан в VB и возвращает DataSet и затем проходит по записям, выводя результат. Второй пример написан в C# и использует SqlDataSource, который возвращает скалярную величину (средняя цена по всем продуктам) и выводит ее в элементе управления Label.
HTML Portion
<asp:Label ID="RandomProductNames" runat="server"></asp:Label>
<asp:SqlDataSource ID="rndProductsDataSource" runat="server"
ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
ProviderName="<%$ ConnectionStrings:NorthwindConnectionString.ProviderName %>"
SelectCommand="SELECT [ProductID], [ProductName] FROM [Products]">
</asp:SqlDataSource>
Page_Load Event Handler
'Осуществляем программный доступ к SqlDataSource - возвращаем DataView
Dim dv As DataView = _
CType(rndProductsDataSource.Select(DataSourceSelectArguments.Empty), DataView)
Dim rndCoinFlip As New Random
RandomProductNames.Text = String.Empty
For Each dr As DataRow In dv.Table.Rows
'Проверяем если нам надо это отобразить
If rndCoinFlip.Next(2) = 0 Then
'Отображаем
RandomProductNames.Text &= _
dr("ProductName").ToString() & "<br />"
Else
'Don't show
End If
Next
HTML Portion
The average price of all products in the Products database table is:
<asp:Label ID="AvgPrice" runat="server" ForeColor="Red"></asp:Label>
<asp:SqlDataSource ID="avgPriceDataSource" runat="server"
ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
ProviderName="<%$ ConnectionStrings:NorthwindConnectionString.ProviderName %>"
SelectCommand="SELECT AVG(UnitPrice) FROM Products">
</asp:SqlDataSource>
Page_Load Event Handler
// Возвращаем результаты из SqlDataSource в качестве DataReader
OleDbDataReader reader = (OleDbDataReader)
avgPriceDataSource.Select(DataSourceSelectArguments.Empty);
// Читаем значение
if (reader.Read())
// Форматируем цену в денежные единицы
AvgPrice.Text = string.Format("{0:c}", reader[0]);
else
// Ой, не было получено ни одного результата!
AvgPrice.Text = "I don't know!";
// Close the reader
reader.Close();
Заметьте, что в обоих примерах я преобразую возвращаемое методом Select() значение либо в DataView, либо в объект OleDbDataReader, в зависимости от свойства DataSourceMode. (Поскольку DataReaders являются объектами провайдеров, то если бы я использовал SqlClient провайдер, я бы также использовал SqlDataReader; в случае с провайдером OracleProvider, я бы использовал OracleDataReader, и т.д.)
При осуществлении запроса данных при помощи элемента SqlDataSource вы можете указать ту базу данных, к которой надо подключиться, и информацию, которую надо получить, посредством мастера "Configure Data Source" (Установка источника данных), который можно вызывать при помощи смарт-тега элемента SqlDataSource. Как только вы настроите источник данных (Data Source), информация может быть отображена путем связывания ее с элементом управления данными, либо обрабатывая ее программно вызовом метода Select() элемента SqlDataSource control. Полный код, используемый при программной работе с информацией элемента SqlDataSource, зависит от того, как она возвращается (что, в свою очередь, зависит от значения свойства DataSourceMode).
Использование оптимистичного параллелизма
Поскольку множество пользователей может посетить веб-страницу одновременно, то есть также вероятность того, что они посетят страницу редактирования, где они могут непреднамеренно переписать изменения, сделанные другим пользователем. Представьте себе страницу, где у вас есть возможность редактировать GridView. Если два пользователя открют страницу одновременно с разных компьютеров, и оба внесут изменения в одну и ту же строку, то изменения, внесенные тем, кто сохранил их раньше, будут переписаны изменениями, сделанными вторым пользователем позже. Данное поведение, когда последний "побеждает", является стандартным для веб-приложений.
Данное поведение обоснованно для приложений, где в редких случаях пользователи могут работать с одной и той же информацией. Если пользователи чаще всего работают с одной и той же информацией, то вам необходимо продумать реализацию какого-либо элемента управления параллелизмом (concurrency control). Существует два типа таких элементов: оптимистичные и пессимистичные. Оптимистичные элементы предполагают то, что нарушения параллелизма происходят не так часто, и в случае возникновения стоит попросить одну из сторон повторить ввод информации. Пессимистичная конкуренция реализует политику обеспечения предотвращения нарушений параллелизма. Данные политики могут в результате дать противоречия в введенной пользователем информации.
Microsoft предлагает некоторую форму элемента управления оптимистичным параллелизмом из элемента SqlDataSource, который может быть активирован, если отметить кнопку с независимой фиксацией (checkbox). Данная статья рассматривает различные типы элементов управления параллелизмом, а затем демонстрирует реализацию встроенного элемента управления оптимистичным параллелизмом, который предоставляет SqlDataSource. Читайте далее, чтобы узнать больше об этом!
Необходимость в элементе управления параллелизмом
До того, как мы начнем рассматривать возможности элемента управления параллелизмом, и применим функциональность встроенного в SqlDataSource элемента управления оптимистичным параллелизмом, давайте сначала уделим немного внимания обсуждению необходимости в данном элементе.
Представьте себе веб-приложение интернет-магазина, обладающее интерфейсом администратора, который представляет собой редактируемый элемент GridView, где менеджеры могут изменить название продукта, цену и статус скидки на инвентарь. Допустим, что компания следует правилу, согласно которому товар, дешевле $5.00, не должен быть в продаже. Теперь представьте двух менджеров, которые, посетив страницу с двух разных компьютеров, замечают, что товар "Scott's Tea" стоит дешевле $5.00. Первый менеджер, увидев это, может понять, что была допущена ошибка, и что на самом деле цена должна быть $15.00. Он нажимает на кнопку редактирования (Edit) и изменяет цену. В это же время другой менеджер посещает страницу и, увидев цену ниже $5.00, согласно правилу компании, решает убрать товар с прилавка. Нажав кнопку редактирования (Edit), он начинает совершать необходимые изменения.
Так что же мы получим в конце концов? Будет ли товар "Scott's Tea":
- отмечен как недоступный из-за цены ниже $5.00,
- иметь цену в $15.00 и не быть недоступным, или
- быть недоступным, при этом стоить $15.00?
Элементы управления SqlDataSource и GridView по умолчанию работают таким образом, что обновляются все редактируемые поля, вне зависимости от того, были ли они отредактированы пользователем, или нет. Это означает, что мы можем быть уверены в том, что последний вариант нереален. Тем самым тот менеджер, который сохранит свои изменения последним, перепишет изменения, сделанные до данного момента. Следующий рисунок демонстрирует данные действия, когда менеджер A - тот, кто меняет цену на $15.00 - нажимает кнопку обновления (Update) после того, как менеджер B уже сохранил изменения.

Как уже отмечено в начале статьи, данное поведение описывается как "в выигрыше последний", и это стандартное поведение элемента SqlDataSource (а также большинства веб-приложений). Данное поведение отлично подходит многим веб-приложениям, тем не менее, конфликты могут происходить в случаях, когда несколько пользователей обновляют или удаляют одну и ту же информацию. Во многих веб-приложениях пользователи могут изменять информацию, относящуюся только к их учетной записи - в таких случаях нет никакой необходимости в элементе управления параллелизмом. Но в приложениях, где могут произойти конфликты, все же есть смысл в добавлении какого-либа элемента управления параллелизмом.
Изучение элемента управления пессимистичным параллелизмом
Единственной гарантией того, что конфликты не будут происходить, может послужить ограничение на редактирование до одного пользователя за раз. Такие элементы управления параллелизма называются элементами управления пессимистичным параллелизмом. При использовании такого элемента управления необходимо иметь какой-нибудь механизм, где пользователь может заблокировать конкретную строку или целую таблицу базы данных, тем самым говоря: "Никто не может изменять данные записи, пока я не закончил!" Препятствием тут будет то, что мы не совсем в курсе, когда пользователь по-настоящему закончил редактировать записи. Если прошло 30 минут с того времени, как пользователи в последний раз загрузили страницу, значит ли это, что они всё еще редактируют информацию, и ее необходимо блокировать, или они уже закрыли свой обозреватель и ушли домой спать? Как вам уже стало ясно, используя элемент управления пессимистичным параллелизмом, необходимо выполнить некоторые действия для того, чтобы гарантировать блокировку данных, чтобы тем самым не предоставить другим пользователям возможность редактирования данных.
Из-за препятствий и неудобств в использовании данный элемент управления используется крайне редко. Он используется только тогда, когда конфликты просто- напросто не могут произойти. Обычным примером использования данного элемента является резервация. Если вы заказываете билет на концерт или на самолет через интернет, то очень важно удостовериться в том, что ваше место, бронируемое в течение некоторого времени, не будет забронировано другим клиентом. Для реализации данной гарантии многие системы позволяют вам выбрать место и затем блокируют его на некоторое время. Если вы завершите процесс покупки по окончании периода блокировки, то место остается за вами. Если же вы не успеваете осуществить покупку - по причине обеда, либо вы слишком медленно вводите информацию - то место становится "свободным".
ASP.NET не обладает встроенной поддержкой элемента управления пессимистичного параллелизма. Вам придется самому разбираться во всем в случае, если вам необходимо использовать данный элемент.
Обзор элемента управления оптимистичным параллелизмом
Пессимистичный параллелизм полезен в случаях, когда нарушения параллелизма не могут произойти ни под каким предлогом. Но в большинстве веб-приложений нарушения параллелизма не являются концом света. Оптимистичный параллелизм хоть и приобретает черты поведения типа "в выигрыше последний" (нет никакой блокировки или другого поведения для пользователей, изменяющих информацию), но он запрещает одному пользователю непреднамеренно переписывать изменения, внесенные другим пользователем. В случае, когда один пользователь переписывает изменения, сделанные другим (как в примере с менеджером A и менеджером B, приведеном ранее), изменения того, кто пытается переписать правку другого пользователя, отменяются. Вкратце, элемент управления оптимистичным параллелизмом останавливает все записи, непреднамеренно осуществляемые поверх других.
Существует множество способов реализации данного элемента управления . Стандартным способом является добавление какой-либо колонки с временем к таблице базы данных, которая записывает время последнего изменения для каждой строки. Когда пользователь начинает редактировать строку (нажав кнопку редактирования (Edit)), то данное время будет записано. Когда пользователь решит сохранить свои изменения (нажав кнопку обновления (Update)), то изменения будут приняты только тогда, когда временная метка будет совпадать с текущей меткой для данной строки. Если другой пользователь совершил какие-либо изменения в промежутке, когда данный пользователь нажал кнопку редактирования (Edit) и кнопку обновления (Update), временной штамп будет обновлен.
Microsoft предоставляет множество элементов управления оптимистичным параллелизмом посредством SqlDataSource. Вместо того, чтобы использовать временные метки, SqlDataSource обновляет оператор WHERE для выражений UPDATE и DELETE таким образом, что UPDATE и DELETE будут выполнены в случае, когда другие колонки в записи имеют те же значения, как и во время нажатия кнопки Edit.
Реализация оптимистичного параллелизма при помощи элемента управления SqlDataSource
Благодаря мастеру Data Source элемента управления SqlDataSource реализация элемента управления оптимистичным параллелизмом очень проста. Вы можете автоматически сгенерировать выражения INSERT, UPDATE и DELETE нажав кнопку Advanced в мастере Data Source и выбрав пункт "Generate INSERT, UPDATE, and DELETE statements". В дополнение к опции "Generate INSERT, UPDATE, and DELETE statements", диалоговое окно опции Advanced SQL Generation (Расширенная генерация SQL) также включает в себя опцию с названием "Use optimistic concurrency" (Использовать оптимистичный параллелизм).Отметьте ее для получения возможности использования данной опции.

Активировация оптимистичного параллелизма обновляет оператор WHERE выражений UPDATE и DELETE элемента SqlDataSource таким образом, что он включает в себя параметр, названный @original_ColumnName. К примеру, при создании элемента управления SqlDataSource, возвращающего поля ProductID, ProductName, UnitPrice и Discontinued из таблицы Products базы данных Northwind, будет сгенерировано следующее выражение UPDATE:
UPDATE [Products] SET
[ProductName] = @ProductName,
[UnitPrice] = @UnitPrice,
[Discontinued] = @Discontinued
WHERE [ProductID] = @original_ProductID AND
[ProductName] = @original_ProductName AND
[UnitPrice] = @original_UnitPrice AND
[Discontinued] = @original_Discontinued
Без оптимистичного параллелизма оператор WHERE содержал бы : WHERE [ProductID] = @ProductID. Активировав оптимистичный параллелизм вы добавляет дополнительные проверки к стандартным значениям других колонок, возвращаемых запросом SELECT.
При использовании оптимистичного параллелизма с редактируемым элементом управления данными элемент управления запоминает начальные значения при загрузке интерфейса редактирования. К примеру, возьмем GridView. Когда пользователь нажимает кнопку редактирования (Edit), GridView запоминает значения, загруженные в интерфейс редактирования. Данные значения затем будут загружены в параметры @original_ColumnName в момент нажатия кнопки обновления (Update) табличной сетки.
Для того, чтобы увидеть это в действии, давайте вернемся к нашему примеру с менеджерами A и B, но на этот раз представьте то, что был активирован оптимистичный параллелизм. Когда менеджер A нажмет кнопку редактирования для товара Scott's Tea, GridView запомни, что ProductName равен "Scott's Tea", UnitPrice равно $5.00 и то что Discontinued равно False. Аналогично в случае, когда B нажмет кнопку редактирования, GridView запомнит те же самые детали.
После того, как менеджер B отметит опцию Discontinued (убрать товар из списка предлагаемых) и нажмет кнопку обновления Update, вышеуказанное выражение UPDATE будет послано в базу данных . Выражение UPDATE (обладающее сохраненными данными), будет выглядеть следующим образом:
UPDATE [Products] SET
[ProductName] = "Scott's Tea",
[UnitPrice] = 5.00,
[Discontinued] = True
WHERE [ProductID] = 3 AND
[ProductName] = "Scott's Tea" AND
[UnitPrice] = 5.00 AND
[Discontinued] = False
Поскольку информация в базе данных совпадает с информацией, хранимой в GridView, то оператор WHERE возвратит одну запись и эта запись будет обновлена.
Когда менеджер A изменит цену на $15.00 и нажмет кнопку обновления, следующее выражение UPDATE (обладающее значениями параметров) будет послано базе данных:
UPDATE [Products] SET
[ProductName] = "Scott's Tea",
[UnitPrice] = 15.00,
[Discontinued] = False
WHERE [ProductID] = 3 AND
[ProductName] = "Scott's Tea" AND
[UnitPrice] = 5.00 AND
[Discontinued] = False
Оператор WHERE не возвратит никаких записей, потому что запись, в которой ProductID равно 3, будет иметь значение Discontinued, установленное в значение True (обновленное ранее менеджером B). Поэтому изменения, сделанные менеджером A, не будут осуществлены ни для единой записи. Ничего не будет изменено в базе данных, поскольку оригинальные значения и текущие значения не совпали, там самым означая то, что в то время как менеджер A осуществлял изменения, другой пользователь также изменял информацию. Вкратце, изменения, сделанные менеджером A, были утеряны, но хорошей новостью является то, что изменения менеджера B не были слепо переписаны.
Следующее изображение отображает процесс использования оптимистичного параллелизма.

Определение нарушения параллелизма
По умолчанию, при наличии элемента управления оптимистичным параллелизмом нарушения проходят незаметно. В указанном выше примере, когда менеджер A нажмет на кнопку обновления Update, табличная сетка будет возвращена в состояние до момента редактирования (как и ожидалось), но изменения не появятся в ней. Это происходит потому, что изменения не были применены из-за нестыковки между сохраненными значениями и значениями базы данных, но пользователь не был оповещен.
Если произошло нарушение, и пользовательские изменения не были сохранены, мы должны оповестить о данном факте, указав на то, что их изменения не были записаны, потому что другой пользователь сохранил изменения одновременно с ними. Мы можем определить количество строк, измененных во время обновления, создав обработчик для события RowUpdated, принадлежащего GridView. Данному обработчику события передается объект, который включает в себя свойство AffectedRows. Также существует свойство KeepInEditMode, которое мы можем установить в True для того, чтобы редактируемая строка была в режиме редактирования (тем самым пользователю будет легче повторно применить изменения).
Приложение, доступное в конце данной статьи, включает в себя веб-сайт с двумя рабочими примерами: один из них демонстрирует использование оптимистичного параллелизма с редактированием, а другой демонстрирует его использование при удалении. (Для того, чтобы использовать данные примеры, вам необходимо открыть два окна обозревателя, чтобы воспроизвести сценарий, когда два пользователя осуществляют изменения (либо удаляют) информацию таким образом, что нарушается параллелизм.) В данных примерах я создал обработчик для события RowUpdated элемента GridView со следующим кодом:
Protected Sub Products_RowUpdated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewUpdatedEventArgs) Handles Products.RowUpdated
If e.AffectedRows = 0 Then
'Не было совершено никаких изменений!
CurrencyConflictMsg.Visible = True
'Держите конфликтующую строку в режиме редактирования
e.KeepInEditMode = True
'Повторная привязка данных, тем самым загружается текущая информация
Products.DataBind()
End If
End Sub
CurrencyConflictMsg является элементом управления Label, который отображает сообщение, оповещающее пользователя о произошедшем нарушении параллелизма. После отображения и указания GridView о том, что он должен оставить строку в режиме редактирования, я повторно привязал информацию к Products, тем самым загрузив изменения, ранее внесенные пользователем. Если вы откроете два окошка обозревателя и воспроизведете случай с менеджерами A и B, то после нажатия кнопки обновления (Update) со стороны менеджера A, в окне вы должны увидеть следующее:

На данном этапе стоит отметить две вещи: информативное сообщение и информацию в редактируемой строке. Во-первых, обратите внимание на большие красные буквы, указывающие на то, что был нарушен параллелизм. Далее, обратите внимание на то, что информация, отображенная в редактируемой строке, не является той, которая была введена менеджером A (который изменил цену на $15.00), а вместо этого была отображена текущая информация базы данных, информация, введенная менеджером B, - а именно оригинальная цена товара ($5.00), отмеченного как"недоступный" (discontinued). Благодаря элементу управления оптимистичным параллелизмом, встроенным в SqlDataSource, а также нескольким строкам кода, мы можем уловить факт попытки переписать изменения, внесеные менеджером В, изменениями, внесенными менеджером А, и в качестве ответа, остановить обновление, обновить табличную сетку текущими значениями из базы данных, а также оставить изменяемую строку в режиме редактирования. Менеджер A может теперь пересмотреть текущие значения,выполнитьнеобходимыеизмененияизатемсохранитьизменения.
Вывод
По умолчанию, элемент управления источником данных ASP.NET не унаследует ничего от элемента управления оптимистичным параллелизмом - вместо этого он использует режим "в выигрыше последний". Данное поведение применимо тогда, когда нарушения параллелизма практически не происходят в случаях редактирования информации. Тем не менее, часто случается такое, что два или более пользователя работают с одной и той же информацией в одно и то же время, тем самым реализуя какую-то форму параллелизма. Простейшей формой элемента управления параллелизмом, а также формой, которую предоставляет элемент SqlDataSource, является элемент управления оптимистичным параллелизмом. Как мы увидели в данной статье, применение элемента управления оптимистичным параллелизмом является очень простым занятием. Примеры кодов, предложенные в конце данной статьи, позволят вам изучить данный элемент в случаях осуществления изменений и удаления информации.
Веселого программирования!
Scott Mitchell