Обработка исключений на уровнях BLL и DAL (VB)

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

Скачивание PDF

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

Введение

В руководстве по редактированию и удалению данных в учебнике DataList мы создали список данных, который предложил простые возможности редактирования и удаления. Хотя он полностью функциональный, он вряд ли был удобным для пользователя, так как любая ошибка, которая произошла во время процесса редактирования или удаления, привела к необработанному исключению. Например, если пропустить имя продукта или при редактировании продукта ввести значение цены "Очень доступно!", это вызовет исключение. Так как это исключение не обрабатывается в коде, оно доходит до исполняющей среды ASP.NET, которая затем отображает информацию об ошибке на веб-странице.

Как мы видели в руководстве по обработке исключений BLL и DAL-Level в ASP.NET Page, если исключение обрабатывается из бизнес-логики или уровней доступа данных, сведения об исключении возвращаются в ObjectDataSource, а затем в GridView. Мы узнали, как корректно обрабатывать эти исключения, создавая Updated или RowUpdated обработчики событий для ObjectDataSource или GridView, проверяя исключение, а затем указывая, что исключение было обработано.

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

Примечание.

В учебнике Обзор редактирования и удаления данных в DataList мы обсудили различные методы редактирования и удаления данных из DataList. Некоторые из этих методов включали использование ObjectDataSource для обновления и удаления. Если вы используете эти методы, можно обрабатывать исключения из BLL или DAL с помощью обработчиков событий ObjectDataSource Updated или Deleted.

Шаг 1. Создание редактируемого списка данных

Прежде чем беспокоиться об обработке исключений, возникающих во время обновления рабочего процесса, сначала создадим редактируемый список данных. Откройте страницу ErrorHandling.aspx в папке EditDeleteDataList, добавьте элемент DataList в дизайнер, задайте его свойству ID значение Products и добавьте новый элемент ObjectDataSource с именем ProductsDataSource. Настройте ObjectDataSource для использования метода GetProducts() класса ProductsBLL для выбора записей; установите раскрывающиеся списки на вкладках INSERT, UPDATE и DELETE в "(Нет значения)".

Возврат сведений о продукте с помощью метода GetProducts()

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

После завершения работы мастера ObjectDataSource Visual Studio автоматически создаст ItemTemplate объект для DataList. Замените это на ItemTemplate, который отображает имя и цену каждого продукта и включает кнопку "Изменить". Затем создайте EditItemTemplate веб-элемент управления TextBox для ввода имени и цены, а также кнопки "Обновить" и "Отмена". Наконец, задайте для свойства DataList RepeatColumns значение 2.

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

<asp:DataList ID="Products" runat="server" DataKeyField="ProductID"
    DataSourceID="ProductsDataSource" RepeatColumns="2">
    <ItemTemplate>
        <h5>
            <asp:Label runat="server" ID="ProductNameLabel"
                Text='<%# Eval("ProductName") %>' />
        </h5>
        Price:
            <asp:Label runat="server" ID="Label1"
                Text='<%# Eval("UnitPrice", "{0:C}") %>' />
        <br />
            <asp:Button runat="server" id="EditProduct" CommandName="Edit"
                Text="Edit" />
        <br />
        <br />
    </ItemTemplate>
    <EditItemTemplate>
        Product name:
            <asp:TextBox ID="ProductName" runat="server"
                Text='<%# Eval("ProductName") %>' />
        <br />
        Price:
            <asp:TextBox ID="UnitPrice" runat="server"
                Text='<%# Eval("UnitPrice", "{0:C}") %>' />
        <br />
        <br />
            <asp:Button ID="UpdateProduct" runat="server" CommandName="Update"
                Text="Update" /> 
            <asp:Button ID="CancelUpdate" runat="server" CommandName="Cancel"
                Text="Cancel" />
    </EditItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
    SelectMethod="GetProducts" TypeName="ProductsBLL"
    OldValuesParameterFormatString="original_{0}">
</asp:ObjectDataSource>

Примечание.

Для этого руководства следует включить «состояние представления» DataList.

Ознакомьтесь с нашим прогрессом в браузере (см. рис. 2).

Каждый продукт включает кнопку

Рис. 2. Каждый продукт включает кнопку "Изменить" (нажмите, чтобы просмотреть изображение полного размера)

В настоящее время кнопка "Изменить" вызывает только перезагрузку страницы и пока не делает продукт редактируемым. Чтобы включить редактирование, необходимо создать обработчики событий для dataList s EditCommandCancelCommandи UpdateCommand событий. События EditCommand и CancelCommand просто обновляют свойство EditItemIndex в DataList и перепривязывают данные к DataList.

Protected Sub Products_EditCommand(source As Object, e As DataListCommandEventArgs) _
    Handles Products.EditCommand
    ' Set the DataList's EditItemIndex property to the
    ' index of the DataListItem that was clicked
    Products.EditItemIndex = e.Item.ItemIndex
    ' Rebind the data to the DataList
    Products.DataBind()
End Sub
Protected Sub Products_CancelCommand(source As Object, e As DataListCommandEventArgs) _
    Handles Products.CancelCommand
    ' Set the DataList's EditItemIndex property to -1
    Products.EditItemIndex = -1
    ' Rebind the data to the DataList
    Products.DataBind()
End Sub

Обработчик UpdateCommand событий немного более сложен. Он должен прочитать в отредактированном продукте ProductID из коллекции DataKeys вместе с именем и ценой продукта из TextBoxes EditItemTemplate, а затем вызвать метод UpdateProduct класса ProductsBLL до возвращения DataList к состоянию до редактирования.

Давайте просто используем этот же самый код из UpdateCommand обработчика событий в учебнике Обзор редактирования и удаления данных в DataList. Мы добавим код для корректной обработки исключений на шаге 2.

Protected Sub Products_UpdateCommand(source As Object, e As DataListCommandEventArgs) _
    Handles Products.UpdateCommand
    ' Read in the ProductID from the DataKeys collection
    Dim productID As Integer = Convert.ToInt32(Products.DataKeys(e.Item.ItemIndex))
    ' Read in the product name and price values
    Dim productName As TextBox = CType(e.Item.FindControl("ProductName"), TextBox)
    Dim unitPrice As TextBox = CType(e.Item.FindControl("UnitPrice"), TextBox)
    Dim productNameValue As String = Nothing
    If productName.Text.Trim().Length > 0 Then
        productNameValue = productName.Text.Trim()
    End If
    Dim unitPriceValue As Nullable(Of Decimal) = Nothing
    If unitPrice.Text.Trim().Length > 0 Then
        unitPriceValue = Decimal.Parse(unitPrice.Text.Trim(), _
                         System.Globalization.NumberStyles.Currency)
    End If
    ' Call the ProductsBLL's UpdateProduct method...
    Dim productsAPI As New ProductsBLL()
    productsAPI.UpdateProduct(productNameValue, unitPriceValue, productID)
    ' Revert the DataList back to its pre-editing state
    Products.EditItemIndex = -1
    Products.DataBind()
End Sub

В случае недопустимых входных данных, которые могут проявляться в виде неправильно отформатированной цены единицы, недопустимого значения цены единицы, например -5,00 рубля, или пропуска имени продукта, будет вызвано исключение. UpdateCommand Так как обработчик событий не включает в себя код обработки исключений на данный момент, исключение будет пузыриться до среды выполнения ASP.NET, где она будет отображаться для конечного пользователя (см. рис. 3).

При возникновении необработанного исключения конечный пользователь видит страницу ошибки

Рис. 3. При возникновении необработанного исключения конечный пользователь видит страницу ошибки

Шаг 2. Аккуратная обработка исключений в обработчике событий UpdateCommand

Во время обновления рабочих процессов исключения могут возникать в UpdateCommand обработчике событий, BLL или DAL. Например, если пользователь вводит слишком высокую цену, Decimal.Parse инструкция в обработчике UpdateCommand событий вызовет FormatException исключение. Если пользователь пропускает имя продукта или если цена имеет отрицательное значение, DAL вызовет исключение.

При возникновении исключения мы хотим отобразить информативное сообщение на самой странице. Добавьте элемент управления Label Web на страницу, для которой ID установлено в ExceptionDetails. Настройте текст Label для отображения в красном, большом, полужирном и курсивном шрифте, назначив его CssClass свойству Warning класс CSS, который определен в Styles.css файле.

При возникновении ошибки мы хотим, чтобы метка отображалась только один раз. То есть при последующих постбэках предупреждение надписи Label должно исчезнуть. Это можно сделать путем очистки свойства Text или настройки его свойства Visible на False в обработчике событий Page_Load (как мы сделали в руководстве по обработке исключений BLL и DAL-Level в ASP.NET) или отключением поддержки состояния представления Метки. Давайте будем использовать последний вариант.

<asp:Label ID="ExceptionDetails" EnableViewState="False" CssClass="Warning"
    runat="server" />

При возникновении исключения мы назначим сведения об исключении свойству ExceptionDetails элемента управления Text Label. Так как состояние представления отключено, при последующих обратных вызовах программные изменения в свойстве Text будут потеряны, возвращая текст по умолчанию (пустую строку), что тем самым скрывает предупреждающее сообщение.

Чтобы определить момент возникновения ошибки и отобразить полезное сообщение на странице, необходимо добавить блок Try ... Catch в обработчик событий UpdateCommand. Часть Try содержит код, который может привести к исключению, а Catch блок содержит код, выполняемый перед лицом исключения. Дополнительные сведения о блоке см. в разделе "Основы обработки исключенийTry ... Catch" в документации по .NET Framework.

Protected Sub Products_UpdateCommand(source As Object, e As DataListCommandEventArgs) _
    Handles Products.UpdateCommand
    ' Handle any exceptions raised during the editing process
    Try
        ' Read in the ProductID from the DataKeys collection
        Dim productID As Integer = _
            Convert.ToInt32(Products.DataKeys(e.Item.ItemIndex))
        ... Some code omitted for brevity ...
    Catch ex As Exception
        ' TODO: Display information about the exception in ExceptionDetails
    End Try
End Sub

Если исключение любого типа выбрасывается кодом внутри блока Try, код блока Catch начнет выполняться. Тип исключения, вызываемого DbException, NoNullAllowedExceptionArgumentExceptionи т. д. зависит от того, что именно, спровоцировало ошибку в первую очередь. Если на уровне базы данных возникнет проблема, будет выброшено DbException. Если введено недопустимое значение для полей UnitPrice, UnitsInStock, UnitsOnOrder или ReorderLevel, будет выброшено ArgumentException исключение, так как мы добавили код для проверки значений этих полей в классе ProductsDataTable (см. руководство по созданию уровня бизнес-логики).

Мы можем предоставить более полезное объяснение для конечного пользователя, основывая текст сообщения на типе пойманного исключения. Следующий код, который использовался в почти идентичной форме в руководстве по обработке исключений BLL и DAL-Level на странице ASP.NET, предоставляет этот уровень детализации:

Private Sub DisplayExceptionDetails(ByVal ex As Exception)
    ' Display a user-friendly message
    ExceptionDetails.Text = "There was a problem updating the product. "
    If TypeOf ex Is System.Data.Common.DbException Then
        ExceptionDetails.Text += "Our database is currently experiencing problems." + _
                                 "Please try again later."
    ElseIf TypeOf ex Is System.Data.NoNullAllowedException Then
        ExceptionDetails.Text+="There are one or more required fields that are missing."
    ElseIf TypeOf ex Is ArgumentException Then
        Dim paramName As String = CType(ex, ArgumentException).ParamName
        ExceptionDetails.Text+=String.Concat("The ", paramName, " value is illegal.")
    ElseIf TypeOf ex Is ApplicationException Then
        ExceptionDetails.Text += ex.Message
    End If
End Sub

Чтобы завершить работу с этим руководством, просто вызовите DisplayExceptionDetails метод из Catch блока, передаваемого в пойманный Exception экземпляр (ex).

Try ... Catch При наличии блока пользователи получают более информативное сообщение об ошибке, как показано на рисунках 4 и 5. Обратите внимание, что в случае исключения DataList остается в режиме редактирования. Это связано с тем, что после возникновения исключения поток управления немедленно перенаправляется сразу к блоку Catch, минуя код, возвращающий DataList в состояние предварительного редактирования.

Сообщение об ошибке отображается, если пользователю не удается указать обязательное поле

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

При вводе отрицательной цены отображается сообщение об ошибке

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

Итоги

GridView и ObjectDataSource предоставляют обработчики событий после уровня, содержащие сведения о любых исключениях, возникающих во время обновления и удаления рабочего процесса, а также свойства, которые можно задать, чтобы указать, обрабатывается ли исключение. Однако эти функции недоступны при работе с DataList и использовании BLL напрямую. Вместо этого мы отвечаем за реализацию обработки исключений.

В этом руководстве мы рассмотрели, как добавить обработку исключений в процесс обновления DataList, добавив блок Try ... Catch в обработчик событий UpdateCommand. Если во время обновления возникает исключение, Catch код блока выполняется, отображая полезные сведения в ExceptionDetails метке.

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

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

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

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

Об авторе

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

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

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