Обновление и удаление существующих двоичных данных (VB)
В предыдущих руководствах мы видели, как элемент управления GridView упрощает редактирование и удаление текстовых данных. В этом руководстве мы посмотрим, как элемент управления GridView также позволяет изменять и удалять двоичные данные, независимо от того, сохраняются ли эти двоичные данные в базе данных или в файловой системе.
Введение
В последних трех руководствах мы добавили довольно много возможностей для работы с двоичными данными. Мы начали с добавления столбца BrochurePath
в таблицу Categories
и соответствующим образом обновили архитектуру. Мы также добавили методы уровня доступа к данным и уровня бизнес-логики для работы с существующим Picture
столбцом таблицы Категорий, который содержит двоичное содержимое файла изображения. Мы создали веб-страницы для представления двоичных данных в GridПросмотреть ссылку для скачивания брошюры с изображением категории, показанным <img>
в элементе, и добавили DetailsView, чтобы пользователи могли добавить новую категорию и загрузить ее данные брошюры и рисунка.
Все, что еще предстоит реализовать, — это возможность изменять и удалять существующие категории, что мы выполним в этом руководстве с помощью встроенных функций редактирования и удаления GridView. При редактировании категории пользователь может при необходимости отправить новое изображение или продолжить использовать имеющуюся. Для брошюры они могут выбрать использование существующего буклета, загрузить новый буклет или указать, что с категорией больше нет связанного буклета. Приступим!
Шаг 1. Обновление уровня доступа к данным
DAL имеет автоматически созданные Insert
методы , Update
и Delete
, но эти методы были созданы на CategoriesTableAdapter
основе запроса main , который не включает Picture
столбец. Insert
Поэтому методы и Update
не включают параметры для указания двоичных данных для изображения категории. Как и в предыдущем руководстве, необходимо создать новый метод TableAdapter для обновления Categories
таблицы при указании двоичных данных.
Откройте типизированный набор данных и в Designer щелкните правой кнопкой мыши CategoriesTableAdapter
заголовок s и выберите в контекстном меню пункт Добавить запрос, чтобы запустить мастер настройки запросов TableAdapter. Этот мастер начинается с запроса о том, как запрос TableAdapter должен получить доступ к базе данных. Выберите Использовать инструкции SQL и нажмите кнопку Далее. На следующем шаге запрашивается тип создаваемого запроса. Так как мы повторно создадим запрос для добавления новой записи в таблицу Categories
, нажмите кнопку UPDATE и нажмите кнопку Далее.
Рис. 1. Выбор параметра UPDATE (щелкните для просмотра полноразмерного изображения)
Теперь необходимо указать инструкцию UPDATE
SQL. Мастер автоматически предлагает инструкцию, соответствующую UPDATE
запросу main TableAdapter (который обновляет CategoryName
значения , Description
и BrochurePath
). Измените оператор таким образом, чтобы Picture
столбец был включен вместе с параметром @Picture
, например:
UPDATE [Categories] SET
[CategoryName] = @CategoryName,
[Description] = @Description,
[BrochurePath] = @BrochurePath ,
[Picture] = @Picture
WHERE (([CategoryID] = @Original_CategoryID))
На последнем экране мастера появится запрос на присвоение имени новому методу TableAdapter. Введите UpdateWithPicture
и нажмите кнопку Готово.
Рис. 2. Присвойтите имя методу UpdateWithPicture
New TableAdapter (щелкните для просмотра полноразмерного изображения)
Шаг 2. Добавление методов уровня бизнес-логики
Помимо обновления DAL, необходимо обновить BLL, чтобы включить методы обновления и удаления категории. Это методы, которые будут вызываться из уровня представления.
Для удаления категории можно использовать CategoriesTableAdapter
автоматически созданный Delete
метод s. Добавьте в класс CategoriesBLL
следующий метод:
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Delete, True)> _
Public Function DeleteCategory(ByVal categoryID As Integer) As Boolean
Dim rowsAffected As Integer = Adapter.Delete(categoryID)
' Return true if precisely one row was deleted, otherwise false
Return rowsAffected = 1
End Function
В этом руководстве мы создадим два метода обновления категории: один из них ожидает данные двоичного рисунка и вызывает UpdateWithPicture
метод, который мы только что добавили в CategoriesTableAdapter
, а другой принимает только CategoryName
значения , Description
и BrochurePath
и использует CategoriesTableAdapter
автоматически сгенерированную Update
инструкцию класса. Обоснование использования двух методов заключается в том, что в некоторых случаях пользователю может потребоваться обновить изображение категории вместе с другими полями. В этом случае пользователю придется отправить новое изображение. Затем двоичные данные загруженного изображения можно использовать в инструкции UPDATE
. В других случаях пользователь может быть заинтересован только в обновлении, например, имени и описания. Но если UPDATE
инструкция также ожидает двоичные данные для столбца Picture
, необходимо также предоставить эти сведения. Для этого потребуется дополнительная поездка в базу данных для возврата данных изображения для редактируемой записи. Поэтому нам нужны два UPDATE
метода. Уровень бизнес-логики определяет, какой из них следует использовать, в зависимости от того, предоставляются ли данные изображения при обновлении категории.
Чтобы упростить эту задачу, добавьте в класс два метода CategoriesBLL
с именем UpdateCategory
. Первый должен принимать три String
s, Byte
массив и в Integer
качестве входных параметров; второй, только три String
s и Integer
. Входные String
параметры относятся к имени категории, описанию и пути к файлу буклета, Byte
массив — для двоичного содержимого рисунка категории и Integer
определяет CategoryID
обновляемую запись. Обратите внимание, что первая перегрузка вызывает вторую, если переданный Byte
массив имеет значение Nothing
:
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Update, False)> _
Public Function UpdateCategory(categoryName As String, description As String, _
brochurePath As String, picture() As Byte, categoryID As Integer) As Boolean
' If no picture is specified, use other overload
If picture Is Nothing Then
Return UpdateCategory(categoryName, description, brochurePath, categoryID)
End If
' Update picture, as well
Dim rowsAffected As Integer = Adapter.UpdateWithPicture _
(categoryName, description, brochurePath, picture, categoryID)
' Return true if precisely one row was updated, otherwise false
Return rowsAffected = 1
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateCategory(categoryName As String, description As String, _
brochurePath As String, categoryID As Integer) As Boolean
Dim rowsAffected As Integer = Adapter.Update _
(categoryName, description, brochurePath, categoryID)
' Return true if precisely one row was updated, otherwise false
Return rowsAffected = 1
End Function
Шаг 3. Копирование функций вставки и просмотра
В предыдущем руководстве мы создали страницу UploadInDetailsView.aspx
со списком всех категорий в GridView и предоставили DetailsView для добавления новых категорий в систему. В этом руководстве мы расширим GridView, включив в него поддержку редактирования и удаления. Вместо того, чтобы продолжать работать с UploadInDetailsView.aspx
, позвольте вместо этого поместить изменения этого руководства на страницу UpdatingAndDeleting.aspx
из той же папки , ~/BinaryData
. Скопируйте и вставьте декларативную разметку и код из UploadInDetailsView.aspx
в UpdatingAndDeleting.aspx
.
Начните с открытия страницы UploadInDetailsView.aspx
. Скопируйте весь декларативный синтаксис в элементе <asp:Content>
, как показано на рис. 3. Затем откройте UpdatingAndDeleting.aspx
и вставьте эту разметку в свой <asp:Content>
элемент . Аналогичным образом скопируйте код из UploadInDetailsView.aspx
класса кода программной части страницы в UpdatingAndDeleting.aspx
.
Рис. 3. Копирование декларативной разметки из UploadInDetailsView.aspx
(щелкните для просмотра полноразмерного изображения)
После копирования декларативной разметки и кода посетите страницу UpdatingAndDeleting.aspx
. Вы должны увидеть те же выходные данные и пользовательский интерфейс, что UploadInDetailsView.aspx
и на странице из предыдущего руководства.
Шаг 4. Добавление поддержки удаления в ObjectDataSource и GridView
Как мы уже говорили в руководстве Общие сведения о вставке, обновлении и удалении данных , GridView предоставляет встроенные возможности удаления, и эти возможности можно включить с галочкой флажка, если базовый источник данных сетки поддерживает удаление. В настоящее время ObjectDataSource, к которому привязан GridView (CategoriesDataSource
), не поддерживает удаление.
Чтобы устранить эту проблему, щелкните параметр Настройка источника данных в смарт-теге ObjectDataSource, чтобы запустить мастер. На первом экране показано, что ObjectDataSource настроен для работы с классом CategoriesBLL
. Нажмите Далее. В настоящее время указываются только свойства ObjectDataSource InsertMethod
и SelectMethod
. Однако мастер автоматически заполнил раскрывающийся список на вкладках UPDATE и DELETE методами UpdateCategory
и DeleteCategory
соответственно. Это связано с тем, что в классе мы помечаем CategoriesBLL
DataObjectMethodAttribute
эти методы, используя в качестве методов по умолчанию для обновления и удаления.
Пока задайте для раскрывающегося списка update tab s значение (Нет), но оставьте для DeleteCategory
раскрывающегося списка delete значение . Мы вернемся к этому мастеру на шаге 6, чтобы добавить поддержку обновления.
Рис. 4. Настройка ObjectDataSource для использования DeleteCategory
метода (щелкните для просмотра полноразмерного изображения)
Примечание
После завершения работы мастера Visual Studio может спросить, хотите ли вы обновить поля и ключи, чтобы повторно создать поля веб-элементов управления данными. Выберите Нет, так как при выборе параметра Да все внесенные вами настройки полей будут перезаписаны.
Объект ObjectDataSource теперь будет содержать значение для своего DeleteMethod
свойства, а также DeleteParameter
значение . Помните, что при использовании мастера для указания методов Visual Studio задает свойству original_{0}
ObjectDataSource OldValuesParameterFormatString
значение , что вызывает проблемы с вызовами метода обновления и удаления. Поэтому либо полностью очистите это свойство, либо сбросьте его до значения по умолчанию , {0}
. Если вам нужно обновить память для этого свойства ObjectDataSource, см. статью Общие сведения о вставке, обновлении и удалении данных .
После завершения работы мастера и исправления OldValuesParameterFormatString
декларативная разметка ObjectDataSource должна выглядеть примерно так:
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="{0}" SelectMethod="GetCategories"
TypeName="CategoriesBLL" InsertMethod="InsertWithPicture"
DeleteMethod="DeleteCategory">
<InsertParameters>
<asp:Parameter Name="categoryName" Type="String" />
<asp:Parameter Name="description" Type="String" />
<asp:Parameter Name="brochurePath" Type="String" />
<asp:Parameter Name="picture" Type="Object" />
</InsertParameters>
<DeleteParameters>
<asp:Parameter Name="categoryID" Type="Int32" />
</DeleteParameters>
</asp:ObjectDataSource>
После настройки ObjectDataSource добавьте возможности удаления в GridView, установив флажок Включить удаление из смарт-тега GridView. Это добавит CommandField в GridView, свойство которого ShowDeleteButton
имеет значение True
.
Рис. 5. Включение поддержки удаления в GridView (щелкните, чтобы просмотреть полноразмерное изображение)
Проверьте функциональность удаления. Между таблицами и Categories
таблицами CategoryID
CategoryID
существует внешний ключProducts
, поэтому при попытке удалить одну из первых восьми категорий возникнет исключение нарушения ограничения внешнего ключа. Чтобы проверить эту функциональность, добавьте новую категорию, предоставив брошюру и рисунок. Моя тестовая категория, показанная на рис. 6, включает файл тестового буклета с именем Test.pdf
и изображение теста. На рисунке 7 показан элемент GridView после добавления категории теста.
Рис. 6. Добавление категории теста с брошюрой и изображением (щелкните для просмотра полноразмерного изображения)
Рис. 7. После вставки категории теста она отображается в GridView (Щелкните для просмотра полноразмерного изображения)
В Visual Studio обновите Обозреватель решений. Теперь в папке ~/Brochures
Test.pdf
должен появиться новый файл (см. рис. 8).
Затем щелкните ссылку Удалить в строке Категория теста, что приведет к обратной отправке страницы и CategoriesBLL
вызову метода класса DeleteCategory
. Это приведет к вызову метода DAL s Delete
, что приведет к отправке соответствующей DELETE
инструкции в базу данных. Затем данные отскокируются в GridView, а разметка отправляется обратно клиенту, при этом категория "Тест" больше не отображается.
Хотя рабочий процесс удаления успешно удалил запись категории теста из Categories
таблицы, файл брошюры не был удален из файловой системы веб-сервера. Обновите Обозреватель решений, и вы увидите, что Test.pdf
он по-прежнему находится в папке~/Brochures
.
Рис. 8. Файл Test.pdf
не был удален из файловой системы веб-сервера
Шаг 5. Удаление файла брошюры удаленных категорий
Одним из недостатков хранения двоичных данных за пределами базы данных является то, что при удалении связанной записи базы данных необходимо предпринять дополнительные действия для очистки этих файлов. GridView и ObjectDataSource предоставляют события, которые запускают как до, так и после выполнения команды удаления. На самом деле необходимо создать обработчики событий как для событий до, так и для событий после действия. Перед удалением Categories
записи необходимо определить путь к PDF-файлу, но мы не хотим удалять PDF-файл перед удалением категории в случае, если есть исключение, а категория не удаляется.
Событие GridView RowDeleting
срабатывает до вызова команды удаления ObjectDataSource, а его RowDeleted
событие срабатывает после. Создайте обработчики событий для этих двух событий, используя следующий код:
' A page variable to "remember" the deleted category's BrochurePath value
Private deletedCategorysPdfPath As String = Nothing
Protected Sub Categories_RowDeleting(sender As Object, e As GridViewDeleteEventArgs) _
Handles Categories.RowDeleting
' Determine the PDF path for the category being deleted...
Dim categoryID As Integer = Convert.ToInt32(e.Keys("CategoryID"))
Dim categoryAPI As New CategoriesBLL()
Dim categoriesData As Northwind.CategoriesDataTable = _
categoryAPI.GetCategoryByCategoryID(categoryID)
Dim category As Northwind.CategoriesRow = categoriesData(0)
If category.IsBrochurePathNull() Then
deletedCategorysPdfPath = Nothing
Else
deletedCategorysPdfPath = category.BrochurePath
End If
End Sub
Protected Sub Categories_RowDeleted(sender As Object, e As GridViewDeletedEventArgs) _
Handles Categories.RowDeleted
' Delete the brochure file if there were no problems deleting the record
If e.Exception Is Nothing Then
DeleteRememberedBrochurePath()
End If
End Sub
В обработчике RowDeleting
CategoryID
событий удаляемая строка извлекается из коллекции GridView DataKeys
, доступ к которой в этом обработчике событий можно получить через коллекцию e.Keys
. Затем вызывается класс s GetCategoryByCategoryID(categoryID)
для CategoriesBLL
возврата сведений об удаляемой записи. Если возвращаемый CategoriesDataRow
объект имеет значение, отличноеNULL``BrochurePath
от , он сохраняется в переменной deletedCategorysPdfPath
страницы, чтобы файл можно было удалить в обработчике RowDeleted
событий.
Примечание
Вместо того чтобы получать BrochurePath
сведения об удаляемой Categories
записи в RowDeleting
обработчике событий, можно было бы добавить BrochurePath
в свойство GridView DataKeyNames
и получить доступ к значению записи через коллекцию e.Keys
. Это немного увеличит размер состояния представления GridView, но сократит объем необходимого кода и сэкономит поездку в базу данных.
После вызова базовой команды удаления ObjectDataSource срабатывает обработчик событий GridView RowDeleted
. Если при удалении данных не было исключений и имеется значение deletedCategorysPdfPath
, pdf-файл удаляется из файловой системы. Обратите внимание, что этот дополнительный код не требуется для очистки двоичных данных категории, связанных с ее изображением. Это связано с тем, что данные рисунка хранятся непосредственно в базе данных, поэтому при удалении Categories
строки также удаляются данные изображения этой категории.
После добавления двух обработчиков событий снова запустите этот тестовый случай. При удалении категории также удаляется связанный с ней PDF-файл.
Обновление существующих двоичных данных, связанных с записями, создает некоторые интересные проблемы. В оставшейся части этого руководства рассматривается добавление возможностей обновления в брошюру и рисунок. На шаге 6 рассматриваются методы обновления сведений о брошюре, а на шаге 7 рассматривается обновление рисунка.
Шаг 6. Обновление брошюры категории
Как описано в руководстве Общие сведения о вставке, обновлении и удалении данных, GridView предлагает встроенную поддержку редактирования на уровне строк, которую можно реализовать с помощью флажка, если его базовый источник данных настроен соответствующим образом. В настоящее CategoriesDataSource
время ObjectDataSource еще не настроен для включения поддержки обновления, поэтому добавим это в .
Щелкните ссылку Настройка источника данных в мастере ObjectDataSource и перейдите ко второму шагу. Из-за используемого DataObjectMethodAttribute
в CategoriesBLL
раскрывающийся список UPDATE должен автоматически заполняться UpdateCategory
перегрузкой, которая принимает четыре входных параметра (для всех столбцов, кроме Picture
). Измените это значение таким образом, чтобы он использовал перегрузку с пятью параметрами.
Рис. 9. Настройка ObjectDataSource для использования UpdateCategory
метода , который включает параметр для Picture
(щелкните, чтобы просмотреть полноразмерное изображение)
ObjectDataSource теперь будет включать значение для своего UpdateMethod
свойства, а также соответствующие UpdateParameter
значения . Как указано в шаге 4, Visual Studio задает свойству ObjectDataSource OldValuesParameterFormatString
значение original_{0}
при использовании мастера настройки источника данных. Это приведет к проблемам с вызовами метода обновления и удаления. Поэтому либо полностью очистите это свойство, либо сбросьте его до значения по умолчанию , {0}
.
После завершения работы мастера и исправления OldValuesParameterFormatString
декларативная разметка ObjectDataSource должна выглядеть следующим образом:
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="{0}" SelectMethod="GetCategories"
TypeName="CategoriesBLL" InsertMethod="InsertWithPicture"
DeleteMethod="DeleteCategory" UpdateMethod="UpdateCategory">
<InsertParameters>
<asp:Parameter Name="categoryName" Type="String" />
<asp:Parameter Name="description" Type="String" />
<asp:Parameter Name="brochurePath" Type="String" />
<asp:Parameter Name="picture" Type="Object" />
</InsertParameters>
<DeleteParameters>
<asp:Parameter Name="categoryID" Type="Int32" />
</DeleteParameters>
<UpdateParameters>
<asp:Parameter Name="categoryName" Type="String" />
<asp:Parameter Name="description" Type="String" />
<asp:Parameter Name="brochurePath" Type="String" />
<asp:Parameter Name="picture" Type="Object" />
<asp:Parameter Name="categoryID" Type="Int32" />
</UpdateParameters>
</asp:ObjectDataSource>
Чтобы включить встроенные функции редактирования GridView, проверка параметр Включить редактирование из смарт-тега GridView. При этом свойству CommandField будет присвоено ShowEditButton
значение True
, что приведет к добавлению кнопки Изменить (и кнопок Обновить и Отмена для редактируемой строки).
Рис. 10. Настройка GridView для поддержки редактирования (щелкните для просмотра полноразмерного изображения)
Перейдите на страницу в браузере и нажмите одну из кнопок "Изменить". Поля CategoryName
и Description
BoundFields отображаются в виде текстовых полей. В BrochurePath
TemplateField отсутствует EditItemTemplate
, поэтому он продолжает отображать ссылку ItemTemplate
на брошюру. ImageField Picture
отрисовывается в виде элемента TextBox, свойству которого Text
присваивается значение imageField DataImageUrlField
, в данном случае CategoryID
.
Рис. 11. GridView не имеет интерфейса редактирования для BrochurePath
(щелкните, чтобы просмотреть полноразмерное изображение)
НастройкаBrochurePath
интерфейса редактирования
Нам нужно создать интерфейс редактирования для BrochurePath
TemplateField, который позволяет пользователю выполнять следующие действия:
- Оставьте брошюру категории "как есть",
- Обновите брошюру категории, загрузив новый буклет, или
- Полностью удалите брошюру категории (в случае, если в категории больше нет связанного буклета).
Нам также нужно обновить Picture
интерфейс редактирования ImageField, но мы перейдем к этому на шаге 7.
В смарт-теге GridView щелкните ссылку Изменить шаблоны и выберите BrochurePath
TemplateField в раскрывающемся списке EditItemTemplate
. Добавьте веб-элемент управления RadioButtonList в этот шаблон, задав для его ID
свойства значение BrochureOptions
, а для свойства AutoPostBack
— значение True
. В окно свойств щелкните многоточие в свойстве Items
, чтобы открыть ListItem
Редактор Коллекция. Добавьте следующие три параметра с Value
s 1, 2 и 3 соответственно:
- Использование текущего буклета
- Удаление текущего буклета
- Загрузка нового буклета
Присвойте первому ListItem
свойству s Selected
значение True
.
Рис. 12. Добавление трех ListItem
в RadioButtonList
Под Элементом RadioButtonList добавьте элемент управления FileUpload с именем BrochureUpload
. Для его свойства Visible
задайте значение False
.
Рис. 13. Добавление элемента управления RadioButtonList и FileUpload в EditItemTemplate
элемент управления (щелкните для просмотра полноразмерного изображения)
Этот элемент RadioButtonList предоставляет три варианта для пользователя. Идея заключается в том, что элемент управления FileUpload будет отображаться только в том случае, если выбран последний параметр Отправить новый буклет. Для этого создайте обработчик событий для события RadioButtonList SelectedIndexChanged
и добавьте следующий код:
Protected Sub BrochureOptions_SelectedIndexChanged _
(sender As Object, e As EventArgs)
' Get a reference to the RadioButtonList and its Parent
Dim BrochureOptions As RadioButtonList = _
CType(sender, RadioButtonList)
Dim parent As Control = BrochureOptions.Parent
' Now use FindControl("controlID") to get a reference of the
' FileUpload control
Dim BrochureUpload As FileUpload = _
CType(parent.FindControl("BrochureUpload"), FileUpload)
' Only show BrochureUpload if SelectedValue = "3"
BrochureUpload.Visible = (BrochureOptions.SelectedValue = "3")
End Sub
Так как элементы управления RadioButtonList и FileUpload находятся в шаблоне, нам нужно написать немного кода для программного доступа к этим элементам управления. Обработчику SelectedIndexChanged
событий передается ссылка на RadioButtonList во входном параметре sender
. Чтобы получить элемент управления FileUpload, необходимо получить родительский элемент управления RadioButtonList и использовать FindControl("controlID")
метод . После получения ссылки на элементы управления RadioButtonList и FileUpload свойству элемента управления Visible
FileUpload присваивается значение True
только в том случае, если значение RadioButtonList SelectedValue
равно 3, которое является свойством Value
для отправки нового буклета ListItem
.
Создав этот код, протестируйте интерфейс редактирования. Нажмите кнопку Изменить для строки. Изначально следует выбрать параметр Использовать текущий буклет. Изменение выбранного индекса приводит к обратной отправке. Если выбран третий параметр, отображается элемент управления FileUpload, в противном случае он скрыт. На рисунке 14 показан интерфейс редактирования при первом нажатии кнопки Изменить. На рисунке 15 показан интерфейс после выбора параметра Отправить новый буклет.
Рис. 14. Изначально выбран параметр Использовать текущий буклет (щелкните, чтобы просмотреть полноразмерное изображение)
Рис. 15. Выбор параметра Отправить новый буклет отображает элемент управления FileUpload (Щелкните для просмотра полноразмерного изображения)
Сохранение файла буклета и обновление столбцаBrochurePath
При нажатии кнопки Update gridView срабатывает событие RowUpdating
. Вызывается команда objectDataSource update, после чего срабатывает событие GridView RowUpdated
. Как и в случае с рабочим процессом удаления, необходимо создать обработчики событий для обоих этих событий. В обработчике RowUpdating
событий необходимо определить, какое действие следует предпринять на SelectedValue
BrochureOptions
основе элемента RadioButtonList:
SelectedValue
Если значение равно 1, мы хотим использовать тот жеBrochurePath
параметр. Поэтому необходимо задать для параметра ObjectDataSourcebrochurePath
существующееBrochurePath
значение обновляемой записи. Параметр ObjectDataSourcebrochurePath
можно задать с помощьюe.NewValues["brochurePath"] = value
.SelectedValue
Если имеет значение 2, необходимо задать для записиBrochurePath
значениеNULL
. Это можно сделать, задав параметру ObjectDataSourcebrochurePath
значениеNothing
, что приводит к использованию базы данныхNULL
в инструкцииUPDATE
. Если существует файл брошюры, который удаляется, необходимо удалить существующий файл. Однако мы хотим сделать это только в том случае, если обновление завершается без возникновения исключения.SelectedValue
Если имеет значение 3, мы хотим убедиться, что пользователь загрузил PDF-файл, а затем сохранить его в файловой системе и обновить значение столбцаBrochurePath
записи. Кроме того, если существует файл брошюры, который заменяется, необходимо удалить предыдущий файл. Однако мы хотим сделать это только в том случае, если обновление завершается без возникновения исключения.
Действия, необходимые для выполнения, когда значение RadioButtonList SelectedValue
равно 3, практически идентичны действиям, используемым обработчиком событий DetailsView ItemInserting
. Этот обработчик событий выполняется при добавлении новой записи категории из элемента управления DetailsView, добавленного в предыдущем руководстве. Поэтому мы должны выполнить рефакторинг этой функции в отдельные методы. В частности, я переместил общую функциональность на два метода:
ProcessBrochureUpload(FileUpload, out bool)
принимает в качестве входных данных экземпляр элемента управления FileUpload и выходное логическое значение, указывающее, должна ли операция удаления или изменения продолжаться или ее следует отменить из-за ошибки проверки. Этот метод возвращает путь к сохраненного файла илиnull
значение , если файл не был сохранен.DeleteRememberedBrochurePath
Удаляет файл, указанный путем в переменнойdeletedCategorysPdfPath
страницы, еслиdeletedCategorysPdfPath
неnull
имеет значение .
Ниже приведен код для этих двух методов. Обратите внимание на сходство обработчика ProcessBrochureUpload
ItemInserting
событий DetailsView и из предыдущего руководства. В этом руководстве я обновил обработчики событий DetailsView, чтобы использовать эти новые методы. Скачайте код, связанный с этим руководством, чтобы просмотреть изменения в обработчиках событий DetailsView.
Private Function ProcessBrochureUpload _
(BrochureUpload As FileUpload, CancelOperation As Boolean) As String
CancelOperation = False ' by default, do not cancel operation
If BrochureUpload.HasFile Then
' Make sure that a PDF has been uploaded
If String.Compare(System.IO.Path.GetExtension(BrochureUpload.FileName), _
".pdf", True) <> 0 Then
UploadWarning.Text = _
"Only PDF documents may be used for a category's brochure."
UploadWarning.Visible = True
CancelOperation = True
Return Nothing
End If
Const BrochureDirectory As String = "~/Brochures/"
Dim brochurePath As String = BrochureDirectory + BrochureUpload.FileName
Dim fileNameWithoutExtension As String = _
System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName)
Dim iteration As Integer = 1
While System.IO.File.Exists(Server.MapPath(brochurePath))
brochurePath = String.Concat(BrochureDirectory, _
fileNameWithoutExtension, "-", iteration, ".pdf")
iteration += 1
End While
' Save the file to disk and set the value of the brochurePath parameter
BrochureUpload.SaveAs(Server.MapPath(brochurePath))
Return brochurePath
Else
' No file uploaded
Return Nothing
End If
End Function
Private Sub DeleteRememberedBrochurePath()
' Is there a file to delete?
If deletedCategorysPdfPath IsNot Nothing Then
System.IO.File.Delete(Server.MapPath(deletedCategorysPdfPath))
End If
End Sub
Обработчики RowUpdating
событий и RowUpdated
GridView используют ProcessBrochureUpload
методы и DeleteRememberedBrochurePath
, как показано в следующем коде:
Protected Sub Categories_RowUpdating _
(sender As Object, e As GridViewUpdateEventArgs) _
Handles Categories.RowUpdating
' Reference the RadioButtonList
Dim BrochureOptions As RadioButtonList = _
CType(Categories.Rows(e.RowIndex).FindControl("BrochureOptions"), _
RadioButtonList)
' Get BrochurePath information about the record being updated
Dim categoryID As Integer = Convert.ToInt32(e.Keys("CategoryID"))
Dim categoryAPI As New CategoriesBLL()
Dim categoriesData As Northwind.CategoriesDataTable = _
categoryAPI.GetCategoryByCategoryID(categoryID)
Dim category As Northwind.CategoriesRow = categoriesData(0)
If BrochureOptions.SelectedValue = "1" Then
' Use current value for BrochurePath
If category.IsBrochurePathNull() Then
e.NewValues("brochurePath") = Nothing
Else
e.NewValues("brochurePath") = category.BrochurePath
End If
ElseIf BrochureOptions.SelectedValue = "2" Then
' Remove the current brochure (set it to NULL in the database)
e.NewValues("brochurePath") = Nothing
ElseIf BrochureOptions.SelectedValue = "3" Then
' Reference the BrochurePath FileUpload control
Dim BrochureUpload As FileUpload = _
CType(categories.Rows(e.RowIndex).FindControl("BrochureUpload"), _
FileUpload)
' Process the BrochureUpload
Dim cancelOperation As Boolean = False
e.NewValues("brochurePath") = _
ProcessBrochureUpload(BrochureUpload, cancelOperation)
e.Cancel = cancelOperation
Else
' Unknown value!
Throw New ApplicationException( _
String.Format("Invalid BrochureOptions value, {0}", _
BrochureOptions.SelectedValue))
End If
If BrochureOptions.SelectedValue = "2" OrElse _
BrochureOptions.SelectedValue = "3" Then
' "Remember" that we need to delete the old PDF file
If (category.IsBrochurePathNull()) Then
deletedCategorysPdfPath = Nothing
Else
deletedCategorysPdfPath = category.BrochurePath
End If
End If
End Sub
Protected Sub Categories_RowUpdated _
(sender As Object, e As GridViewUpdatedEventArgs) _
Handles Categories.RowUpdated
' If there were no problems and we updated the PDF file,
' then delete the existing one
If e.Exception Is Nothing Then
DeleteRememberedBrochurePath()
End If
End Sub
Обратите внимание, RowUpdating
что обработчик событий использует ряд условных инструкций для выполнения соответствующего действия на BrochureOptions
основе значения свойства RadioButtonList SelectedValue
.
Используя этот код, вы можете изменить категорию и заставить ее использовать текущую брошюру, не использовать брошюру или отправить новую. Идите дальше и попробуйте. Задайте точки останова RowUpdating
в обработчиках событий и RowUpdated
, чтобы получить представление о рабочем процессе.
Шаг 7. Отправка нового рисунка
Интерфейс Picture
редактирования ImageField отображается в виде текстового поля, заполненного значением из его DataImageUrlField
свойства . Во время рабочего процесса редактирования GridView передает параметр ObjectDataSource с именем параметра, значением свойства ImageField s DataImageUrlField
и значением параметра, введенным в текстовое поле в интерфейсе редактирования. Такое поведение подходит, если образ сохраняется в файловой системе, а DataImageUrlField
содержит полный URL-адрес изображения. В таких случаях интерфейс редактирования отображает URL-адрес изображения в текстовом поле, который пользователь может изменить и сохранить обратно в базу данных. Конечно, этот интерфейс по умолчанию не позволяет пользователю отправлять новое изображение, но позволяет ему изменять URL-адрес изображения с текущего значения на другое. Однако в этом руководстве недостаточно интерфейса редактирования ImageField по умолчанию, так как двоичные Picture
данные хранятся непосредственно в базе данных, а DataImageUrlField
свойство содержит только CategoryID
.
Чтобы лучше понять, что происходит в нашем руководстве, когда пользователь редактирует строку с помощью ImageField, рассмотрим следующий пример: пользователь редактирует строку с CategoryID
10, в результате чего Picture
ImageField будет отображаться в виде текстового поля со значением 10. Представьте, что пользователь изменяет значение в этом текстовом поле на 50 и нажимает кнопку Обновить. Происходит обратная передача, и GridView изначально создает параметр CategoryID
со значением 50. Однако перед отправкой этого параметра (и CategoryName
параметров и Description
) GridView добавляет значения из DataKeys
коллекции. Поэтому параметр перезаписывается CategoryID
базовым CategoryID
значением текущей строки 10. Короче говоря, интерфейс редактирования ImageField не влияет на рабочий процесс редактирования в этом руководстве, так как имена свойства ImageField и DataImageUrlField
значения сетки DataKey
совпадают.
Хотя ImageField упрощает отображение изображения на основе данных базы данных, мы не хотим предоставлять текстовое поле в интерфейсе редактирования. Вместо этого мы хотим предложить элемент управления FileUpload, который конечный пользователь может использовать для изменения изображения категории. BrochurePath
В отличие от значения, для этих учебников мы решили требовать, чтобы каждая категория должна иметь рисунок. Поэтому нам не нужно разрешать пользователю указывать, что нет связанного рисунка, который пользователь может отправить новое изображение или оставить текущий рисунок как есть.
Чтобы настроить интерфейс редактирования ImageField, необходимо преобразовать его в TemplateField. В смарт-теге GridView щелкните ссылку Изменить столбцы, выберите ImageField и щелкните ссылку Преобразовать это поле в TemplateField.
Рис. 16. Преобразование ImageField в templateField
Преобразование ImageField в TemplateField таким образом создает TemplateField с двумя шаблонами. Как показано в следующем декларативном синтаксисе ItemTemplate
, содержит элемент управления Image Web, свойство которого ImageUrl
назначается с помощью синтаксиса привязки данных на основе свойств ImageField DataImageUrlField
и DataImageUrlFormatString
. Содержит EditItemTemplate
объект TextBox, свойство которого Text
привязано к значению, заданному свойством DataImageUrlField
.
<asp:TemplateField>
<EditItemTemplate>
<asp:TextBox ID="TextBox1" runat="server"
Text='<%# Eval("CategoryID") %>'></asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Image ID="Image1" runat="server"
ImageUrl='<%# Eval("CategoryID",
"DisplayCategoryPicture.aspx?CategoryID={0}") %>' />
</ItemTemplate>
</asp:TemplateField>
Необходимо обновить , EditItemTemplate
чтобы использовать элемент управления FileUpload. В смарт-теге GridView щелкните ссылку Изменить шаблоны и выберите Picture
TemplateField в раскрывающемся списке EditItemTemplate
. В шаблоне должно появиться свойство TextBox удалить это. Затем перетащите элемент управления FileUpload из панели элементов в шаблон, задав для этого ID
элемента значение PictureUpload
. Кроме того, добавьте текст Чтобы изменить рисунок категории, укажите новый рисунок. Чтобы оставить изображение категории прежним, оставьте поле пустым для шаблона.
Рис. 17. Добавление элемента управления FileUpload в EditItemTemplate
(Щелкните для просмотра полноразмерного изображения)
После настройки интерфейса редактирования просмотрите ход выполнения в браузере. При просмотре строки в режиме только для чтения изображение категории отображается как раньше, но при нажатии кнопки Изменить столбец рисунка отображается в виде текста с элементом управления FileUpload.
Рис. 18. Интерфейс редактирования включает элемент управления FileUpload (Щелкните для просмотра полноразмерного изображения)
Помните, что ObjectDataSource настроен для вызова CategoriesBLL
метода класса s UpdateCategory
, который принимает в качестве входных данных двоичные данные для рисунка в виде массива Byte
. Однако если этот массив имеет значение Nothing
, вызывается альтернативная UpdateCategory
перегрузка, которая выдает инструкцию UPDATE
SQL, которая не изменяет Picture
столбец, тем самым оставляя текущую картину категории без изменений. Поэтому в обработчике событий GridView RowUpdating
необходимо программно ссылаться на PictureUpload
элемент управления FileUpload и определить, был ли отправлен файл. Если один из них не был отправлен, мы не хотим указывать значение параметра picture
. С другой стороны, если файл был отправлен в PictureUpload
элементе управления FileUpload, мы хотим убедиться, что он является JPG-файлом. Если это так, то мы можем отправить его двоичное содержимое в ObjectDataSource с помощью picture
параметра .
Как и в случае с кодом, используемым на шаге 6, большая часть необходимого здесь кода уже существует в обработчике ItemInserting
событий DetailsView. Поэтому я выполнил рефакторинг общих функциональных возможностей в новый метод , ValidPictureUpload
и обновил ItemInserting
обработчик событий для использования этого метода.
Добавьте следующий код в начало обработчика RowUpdating
событий GridView. Важно, чтобы этот код предшествовал коду, который сохраняет файл брошюры, так как мы не хотим сохранять брошюру в файловой системе веб-сервера при отправке недопустимого файла рисунка.
' Reference the PictureUpload FileUpload
Dim PictureUpload As FileUpload = _
CType(categories.Rows(e.RowIndex).FindControl("PictureUpload"), _
FileUpload)
If PictureUpload.HasFile Then
' Make sure the picture upload is valid
If ValidPictureUpload(PictureUpload) Then
e.NewValues("picture") = PictureUpload.FileBytes
Else
' Invalid file upload, cancel update and exit event handler
e.Cancel = True
Exit Sub
End If
End If
Метод ValidPictureUpload(FileUpload)
принимает элемент управления FileUpload в качестве единственного входного параметра и проверяет расширение отправленного файла, чтобы убедиться, что отправленный файл является JPG; он вызывается только при отправке файла рисунка. Если файл не отправлен, параметр picture не задан и поэтому использует значение Nothing
по умолчанию . Если изображение было отправлено и ValidPictureUpload
возвращается True
, picture
параметр назначается двоичным данным отправленного изображения; если метод возвращает False
, рабочий процесс обновления отменяется и обработчик событий завершается.
Код ValidPictureUpload(FileUpload)
метода, который был рефакторингован из обработчика ItemInserting
событий DetailsView, выглядит следующим образом:
Private Function ValidPictureUpload(ByVal PictureUpload As FileUpload) As Boolean
' Make sure that a JPG has been uploaded
If String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
".jpg", True) <> 0 AndAlso _
String.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), _
".jpeg", True) <> 0 Then
UploadWarning.Text = _
"Only JPG documents may be used for a category's picture."
UploadWarning.Visible = True
Return False
Else
Return True
End If
End Function
Шаг 8. Замена исходных изображений категорий на JPG
Напомним, что исходные изображения восьми категорий представляют собой файлы растровых рисунков, заключенные в заголовок OLE. Теперь, когда мы добавили возможность редактировать существующие изображения записей, уделите некоторое время, чтобы заменить эти растровые изображения на JPG. Если вы хотите продолжать использовать текущие изображения категорий, вы можете преобразовать их в JPG, выполнив следующие действия:
- Сохраните растровые изображения на жестком диске. Перейдите на страницу
UpdatingAndDeleting.aspx
браузера и для каждой из первых восьми категорий щелкните изображение правой кнопкой мыши и выберите сохранить изображение. - Откройте изображение в выбранном редакторе изображений. Например, можно использовать Microsoft Paint.
- Сохраните растровое изображение в формате JPG.
- Обновите изображение категории с помощью интерфейса редактирования с помощью JPG-файла.
После редактирования категории и отправки изображения JPG изображение не будет отображаться в браузере, так как DisplayCategoryPicture.aspx
страница удаляет первые 78 байт из изображений первых восьми категорий. Исправьте это, удалив код, который выполняет зачистку заголовка OLE. После этого DisplayCategoryPicture.aspx``Page_Load
обработчик событий должен иметь только следующий код:
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim categoryID As Integer = _
Convert.ToInt32(Request.QueryString("CategoryID"))
' Get information about the specified category
Dim categoryAPI As New CategoriesBLL()
Dim categories As Northwind.CategoriesDataTable = _
categoryAPI.GetCategoryWithBinaryDataByCategoryID(categoryID)
Dim category As Northwind.CategoriesRow = categories(0)
' For new categories, images are JPGs...
' Output HTTP headers providing information about the binary data
Response.ContentType = "image/jpeg"
' Output the binary data
Response.BinaryWrite(category.Picture)
End Sub
Примечание
Интерфейсы UpdatingAndDeleting.aspx
вставки и редактирования страниц могут использовать немного больше работы. Поля CategoryName
BoundField и Description
в DetailsView и GridView должны быть преобразованы в TemplateFields. Так как CategoryName
не допускает NULL
значения, необходимо добавить RequiredFieldValidator. Кроме того, Description
элемент TextBox, вероятно, следует преобразовать в многострочный элемент TextBox. Я оставляю эти последние штрихи как упражнение для вас.
Сводка
В этом руководстве мы завершаем работу с двоичными данными. В этом руководстве и предыдущих трех мы узнали, как двоичные данные могут храниться в файловой системе или непосредственно в базе данных. Пользователь предоставляет двоичные данные в систему, выбрав файл с жесткого диска и отправив его на веб-сервер, где его можно сохранить в файловой системе или вставить в базу данных. ASP.NET 2.0 включает элемент управления FileUpload, который упрощает предоставление такого интерфейса, как перетаскивание. Однако, как указано в учебнике Отправка файлов , элемент управления FileUpload хорошо подходит только для относительно небольших отгрузок файлов, в идеале не превышающих мегабайт. Мы также изучили, как связать отправленные данные с базовой моделью данных, а также как изменить и удалить двоичные данные из существующих записей.
В следующем наборе учебников рассматриваются различные методы кэширования. Кэширование позволяет повысить общую производительность приложения, принимая результаты ресурсоемких операций и сохраняя их в месте, к которому можно получить более быстрый доступ.
Счастливого программирования!
Об авторе
Скотт Митчелл (Scott Mitchell), автор семи книг ASP/ASP.NET и основатель 4GuysFromRolla.com, работает с Веб-технологиями Майкрософт с 1998 года. Скотт работает независимым консультантом, тренером и писателем. Его последняя книга Sams Teach Yourself ASP.NET 2.0 в 24 часа. Его можно связать по адресу mitchell@4GuysFromRolla.com. или через его блог, который можно найти по адресу http://ScottOnWriting.NET.
Отдельная благодарность
Эта серия учебников была проверена многими полезными рецензентами. Ведущим рецензентом этого руководства была Тереса Мерфи. Хотите ознакомиться с моими предстоящими статьями MSDN? Если да, опустите мне строку в mitchell@4GuysFromRolla.com.