Обновление и удаление существующих двоичных данных (C#)Updating and Deleting Existing Binary Data (C#)

по Скотт Митчеллby Scott Mitchell

Скачивание примера приложения или Загрузка PDF-файлаDownload Sample App or Download PDF

В предыдущих учебных курсах мы увидели, как элемент управления GridView упрощает редактирование и удаление текстовых данных.In earlier tutorials we saw how the GridView control makes it simple to edit and delete text data. В этом учебнике показано, как элемент управления GridView также позволяет изменять и удалять двоичные данные независимо от того, сохраняются ли эти двоичные данные в базе данных или хранятся в файловой системе.In this tutorial we see how the GridView control also makes it possible to edit and delete binary data, whether that binary data is saved in the database or stored in the file system.

ВведениеIntroduction

За последние три руководства мы добавили довольно много функций для работы с двоичными данными.Over the past three tutorials we ve added quite a bit of functionality for working with binary data. Мы начали с добавления столбца BrochurePath в таблицу Categories и соответствующим образом обновили архитектуру.We started by adding a BrochurePath column to the Categories table and updated the architecture accordingly. Мы также добавили уровень доступа к данным и методы уровня бизнес-логики для работы с Picture столбцом Categories, который содержит двоичное содержимое файла изображения.We also added Data Access Layer and Business Logic Layer methods to work with the Categories table s existing Picture column, which holds the binary content s of an image file. Мы создали веб-страницы для представления двоичных данных в элементе управления GridView ссылкой для скачивания буклета, где изображение категорий отображается в элементе <img> и добавило элемент DetailsView, чтобы пользователи могли добавлять новые категории и отправлять данные из брошюры и изображений.We have built web pages to present the binary data in a GridView a download link for the brochure, with the category s picture shown in an <img> element and have added a DetailsView to allow users to add a new category and upload its brochure and picture data.

Все, что остается реализованным, — это возможность изменять и удалять существующие категории, которые мы будем выполнять в этом руководстве, используя встроенные функции редактирования и удаления в GridView.All that remains to be implemented is the ability to edit and delete existing categories, which we'll accomplish in this tutorial using the GridView s built-in editing and deleting features. При редактировании категории пользователь может при необходимости передать новую картинку или продолжить использовать существующую.When editing a category, the user will be able to optionally upload a new picture or have the category continue to use the existing one. Для буклета они могут либо использовать существующую буклет, либо отправить новую буклет, либо указать, что в категорию больше не связан буклет.For the brochure, they can either choose to use the existing brochure, to upload a new brochure, or to indicate that the category no longer has a brochure associated with it. Давайте приступим к работе!Let s get started!

Шаг 1. обновление уровня доступа к даннымStep 1: Updating the Data Access Layer

DAL содержит автоматически созданные методы Insert, Updateи Delete, но эти методы были созданы на основе основного запроса CategoriesTableAdapter s, который не включает столбец Picture.The DAL has auto-generated Insert, Update, and Delete methods, but these methods were generated based on the CategoriesTableAdapter s main query, which does not include the Picture column. Таким образом, методы Insert и Update не включают параметры для указания двоичных данных для изображения категории s.Therefore, the Insert and Update methods do not include parameters for specifying the binary data for the category s picture. Как и в предыдущем учебном курсе, нам нужно создать новый метод TableAdapter для обновления таблицы Categories при указании двоичных данных.Like we did in the preceding tutorial, we need to create a new TableAdapter method for updating the Categories table when specifying binary data.

Откройте типизированный набор данных и в конструкторе щелкните правой кнопкой мыши заголовок CategoriesTableAdapter s и выберите в контекстном меню команду Добавить запрос, чтобы запустить мастер настройки запроса адаптера таблицы.Open the Typed DataSet and, from the Designer, right-click on the CategoriesTableAdapter s header and choose Add Query from the context menu to launch the TableAdapter Query Configuration Wizard. Этот мастер запустится с просьбой получить доступ к базе данных с помощью запроса адаптера таблицы.This wizard starts by asking us how the TableAdapter query should access the database. Выберите использовать инструкции SQL и нажмите кнопку Далее.Choose Use SQL statements and click Next. На следующем шаге запрашивается тип создаваемого запроса.The next step prompts for the type of query to be generated. Так как мы повторно создаем запрос для добавления новой записи в Categories таблицу, выберите Обновить и нажмите кнопку Далее.Since we re creating a query to add a new record to the Categories table, choose UPDATE and click Next.

выбрать параметр обновленияSelect the UPDATE Option

Рис. 1. Выбор параметра обновления (щелкните, чтобы просмотреть изображение с полным размером)Figure 1: Select the UPDATE Option (Click to view full-size image)

Теперь необходимо указать инструкцию SQL UPDATE.We now need to specify the UPDATE SQL statement. Мастер автоматически предложит инструкцию UPDATE, соответствующую основному запросу TableAdapter (один из которых обновляет значения CategoryName, Descriptionи BrochurePath).The wizard automatically suggests an UPDATE statement corresponding to the TableAdapter s main query (one that updates the CategoryName, Description, and BrochurePath values). Измените инструкцию таким образом, чтобы столбец Picture был включен вместе с параметром @Picture, например следующим образом:Change the statement so that the Picture column is included along with a @Picture parameter, like so:

UPDATE [Categories] SET 
    [CategoryName] = @CategoryName, 
    [Description] = @Description, 
    [BrochurePath] = @BrochurePath ,
    [Picture] = @Picture
WHERE (([CategoryID] = @Original_CategoryID))

На последнем экране мастера запрашивается имя нового метода TableAdapter.The final screen of the wizard asks us to name the new TableAdapter method. Введите UpdateWithPicture и нажмите кнопку Готово.Enter UpdateWithPicture and click Finish.

назовите новый метод TableAdapter УпдатевиспиктуреName the New TableAdapter Method UpdateWithPicture

Рис. 2. Именование нового метода адаптера таблицы UpdateWithPicture (щелкните, чтобы просмотреть изображение с полным размером)Figure 2: Name the New TableAdapter Method UpdateWithPicture (Click to view full-size image)

Шаг 2. Добавление методов уровня бизнес-логикиStep 2: Adding the Business Logic Layer Methods

В дополнение к обновлению DAL необходимо обновить слой BLL, чтобы включить методы для обновления и удаления категорий.In addition to updating the DAL, we need to update the BLL to include methods for updating and deleting a category. Это методы, которые будут вызываться из уровня представления данных.These are the methods that will be invoked from the Presentation Layer.

Для удаления категории можно использовать автоматически созданный метод CategoriesTableAdapter s Delete.For deleting a category, we can use the CategoriesTableAdapter s auto-generated Delete method. Добавьте в класс CategoriesBLL следующий метод:Add the following method to the CategoriesBLL class:

[System.ComponentModel.DataObjectMethodAttribute
    (System.ComponentModel.DataObjectMethodType.Delete, true)]
public bool DeleteCategory(int categoryID)
{
    int rowsAffected = Adapter.Delete(categoryID);
    // Return true if precisely one row was deleted, otherwise false
    return rowsAffected == 1;
}

В этом руководстве мы создадим два метода для обновления категории — один, который ожидает двоичные данные изображения и вызывает метод UpdateWithPicture, который был только что добавлен в CategoriesTableAdapter и другой, который принимает только значения CategoryName, Descriptionи BrochurePath и использует CategoriesTableAdapter Class s автоматически созданная инструкция Update.For this tutorial, let s create two methods for updating a category - one that expects the binary picture data and invokes the UpdateWithPicture method we just added to the CategoriesTableAdapter and another that accepts just the CategoryName, Description, and BrochurePath values and uses CategoriesTableAdapter class s auto-generated Update statement. Смысл использования двух методов заключается в том, что в некоторых обстоятельствах пользователю может потребоваться обновить изображение категорий вместе с другими полями, в этом случае пользователю нужно будет загрузить новую картинку.The rationale behind using two methods is that in some circumstances, a user might want to update the category s picture along with its other fields, in which case the user will have to upload the new picture. Затем в инструкции UPDATE можно использовать отправленные двоичные данные изображения s.The uploaded picture s binary data can then be used in the UPDATE statement. В других случаях пользователь может заинтересовать только обновление, скажем, имя и описание.In other cases, the user might only be interested in updating, say, the name and description. Но если инструкция UPDATE ожидает для столбца Picture двоичные данные, то нам также нужно предоставить эту информацию.But if the UPDATE statement expects the binary data for the Picture column as well, then we d need to provide that information as well. Это потребует дополнительного обращения к базе данных, чтобы вернуть данные изображения для редактируемой записи.This would require an extra trip to the database to bring back the picture data for the record being edited. Поэтому нам нужны два метода UPDATE.Therefore, we want two UPDATE methods. Уровень бизнес-логики определяет, какой из них будет использоваться в зависимости от того, предоставляются ли данные изображения при обновлении категории.The Business Logic Layer will determine which one to use based on whether picture data is provided when updating the category.

Чтобы упростить это, добавьте два метода в класс CategoriesBLL, с именем UpdateCategory.To facilitate this, add two methods to the CategoriesBLL class, both named UpdateCategory. Первый из них должен принимать три string s, массив byte и int как входные параметры; второй, только три string s и int.The first one should accept three string s, a byte array, and an int as its input parameters; the second, just three string s and an int. Входные параметры string предназначены для имени категории, описания и пути к файлу брошюры, byte массив предназначен для двоичного содержимого изображения категории s, а int определяет CategoryID обновляемой записи.The string input parameters are for the category s name, description, and brochure file path, the byte array is for the binary contents of the category s picture, and the int identifies the CategoryID of the record to update. Обратите внимание, что первая перегрузка вызывает вторую, если переданный byte массив null:Notice that the first overload invokes the second if the passed-in byte array is null:

[System.ComponentModel.DataObjectMethodAttribute
    (System.ComponentModel.DataObjectMethodType.Update, false)]
public bool UpdateCategory(string categoryName, string description, 
    string brochurePath, byte[] picture, int categoryID)
{
    // If no picture is specified, use other overload
    if (picture == null)
        return UpdateCategory(categoryName, description, brochurePath, categoryID);
    // Update picture, as well
    int rowsAffected = Adapter.UpdateWithPicture
        (categoryName, description, brochurePath, picture, categoryID);
    // Return true if precisely one row was updated, otherwise false
    return rowsAffected == 1;
}
[System.ComponentModel.DataObjectMethodAttribute
    (System.ComponentModel.DataObjectMethodType.Update, true)]
public bool UpdateCategory(string categoryName, string description, 
    string brochurePath, int categoryID)
{
    int rowsAffected = Adapter.Update
        (categoryName, description, brochurePath, categoryID);
    // Return true if precisely one row was updated, otherwise false
    return rowsAffected == 1;
}

Шаг 3. копирование функций вставки и просмотраStep 3: Copying Over the Insert and View Functionality

В предыдущем учебном курсе мы создали страницу с именем UploadInDetailsView.aspx, в которой перечислены все категории в GridView и предоставлена DetailsView для добавления новых категорий в систему.In the preceding tutorial we created a page named UploadInDetailsView.aspx that listed all categories in a GridView and provided a DetailsView to add new categories to the system. В этом учебнике мы будем расширять GridView, чтобы включить поддержку редактирования и удаления.In this tutorial we will extend the GridView to include editing and deleting support. Вместо того, чтобы продолжать работу с UploadInDetailsView.aspx, давайте поместим в этом руководстве изменения на UpdatingAndDeleting.aspx странице из той же папки, ~/BinaryData.Rather than continuing to work from UploadInDetailsView.aspx, let s instead place this tutorial s changes in the UpdatingAndDeleting.aspx page from the same folder, ~/BinaryData. Скопируйте и вставьте декларативную разметку и код из UploadInDetailsView.aspx в UpdatingAndDeleting.aspx.Copy and paste the declarative markup and code from UploadInDetailsView.aspx to UpdatingAndDeleting.aspx.

Для начала откройте страницу UploadInDetailsView.aspx.Start by opening the UploadInDetailsView.aspx page. Скопируйте весь декларативный синтаксис в элемент <asp:Content>, как показано на рис. 3.Copy all of the declarative syntax within the <asp:Content> element, as shown in Figure 3. Затем откройте UpdatingAndDeleting.aspx и вставьте эту разметку в элемент <asp:Content>.Next, open UpdatingAndDeleting.aspx and paste this markup within its <asp:Content> element. Аналогичным образом скопируйте код из класса кода программной части UploadInDetailsView.aspx Page s в UpdatingAndDeleting.aspx.Similarly, copy the code from the UploadInDetailsView.aspx page s code-behind class to UpdatingAndDeleting.aspx.

скопировать декларативную разметку из Уплоадиндетаилсвиев. aspxCopy the Declarative Markup from UploadInDetailsView.aspx

Рис. 3. копирование декларативной разметки из UploadInDetailsView.aspx (щелкните, чтобы просмотреть изображение с полным размером)Figure 3: Copy the Declarative Markup from UploadInDetailsView.aspx (Click to view full-size image)

После копирования декларативной разметки и кода перейдите на UpdatingAndDeleting.aspx.After copying over the declarative markup and code, visit UpdatingAndDeleting.aspx. Вы увидите те же выходные данные и получите те же результаты, что и на UploadInDetailsView.aspx странице из предыдущего руководства.You should see the same output and have the same user experience as with UploadInDetailsView.aspx page from the previous tutorial.

Шаг 4. Добавление поддержки удаления в элемент управления ObjectDataSource и GridViewStep 4: Adding Deleting Support to the ObjectDataSource and GridView

Как мы обсуждали в обзоре руководства по вставке, обновлению и удалению данных , GridView предоставляет встроенные возможности удаления, и эти возможности можно включить в такте флажка, если базовый источник данных Grid поддерживает удаление.As we discussed back in the An Overview of Inserting, Updating, and Deleting Data tutorial, the GridView provides built-in deleting capabilities and these capabilities can be enabled at the tick of a checkbox if the grid s underlying data source supports deleting. В настоящее время элемент управления ObjectDataSource, к которому привязан элемент управления GridView (CategoriesDataSource), не поддерживает удаление.Currently the ObjectDataSource the GridView is bound to (CategoriesDataSource) does not support deleting.

Чтобы устранить эту проблему, щелкните параметр настроить источник данных из смарт-тега ObjectDataSource s, чтобы запустить мастер.To remedy this, click on the Configure Data Source option from the ObjectDataSource s smart tag to launch the wizard. На первом экране показано, что ObjectDataSource настроен для работы с классом CategoriesBLL.The first screen shows that the ObjectDataSource is configured to work with the CategoriesBLL class. Нажмите кнопку "Далее".Hit Next. В настоящее время указаны только свойства ObjectDataSource InsertMethod и SelectMethod.Currently, only the ObjectDataSource s InsertMethod and SelectMethod properties are specified. Тем не менее мастер автоматически заполняет раскрывающиеся списки на вкладках UPDATE и DELETE с помощью методов UpdateCategory и DeleteCategory соответственно.However, the wizard auto-populated the drop-down lists in the UPDATE and DELETE tabs with the UpdateCategory and DeleteCategory methods, respectively. Это связано с тем, что в классе CategoriesBLL мы отметили эти методы, используя DataObjectMethodAttribute как методы по умолчанию для обновления и удаления.This is because in the CategoriesBLL class we marked these methods using the DataObjectMethodAttribute as the default methods for updating and deleting.

Пока установите в раскрывающемся списке вкладки обновления значение (нет), а в раскрывающемся списке DELETE Tab s — DeleteCategory.For now, set the UPDATE tab s drop-down list to (None), but leave the DELETE tab s drop-down list set to DeleteCategory. Мы вернемся к этому мастеру на шаге 6, чтобы добавить поддержку обновления.We'll return to this wizard in Step 6 to add updating support.

настроить ObjectDataSource для использования метода ДелетекатегориConfigure the ObjectDataSource to Use the DeleteCategory Method

Рис. 4. Настройка ObjectDataSource для использования метода DeleteCategory (щелкните, чтобы просмотреть изображение с полным размером)Figure 4: Configure the ObjectDataSource to Use the DeleteCategory Method (Click to view full-size image)

Note

После завершения работы мастера Visual Studio может запросить обновление полей и ключей, что приведет к повторному формированию полей веб-элементов управления данными.Upon completing the wizard, Visual Studio may ask if you want to Refresh Fields and Keys, which will regenerate the data Web controls fields. Выберите Нет, так как при нажатии кнопки Да будут перезаписаны все сделанные настройки полей.Choose No, because choosing Yes will overwrite any field customizations you may have made.

Теперь ObjectDataSource будет включать значение свойства DeleteMethod, а также DeleteParameter.The ObjectDataSource will now include a value for its DeleteMethod property as well as a DeleteParameter. Помните, что при использовании мастера для указания методов, Visual Studio устанавливает свойство ObjectDataSource OldValuesParameterFormatString в значение original_{0}, что вызывает проблемы с вызовами методов Update и DELETE.Recall that when using the wizard to specify the methods, Visual Studio sets the ObjectDataSource s OldValuesParameterFormatString property to original_{0}, which causes problems with the update and delete method invocations. Поэтому либо очистите это свойство полностью, либо сбросьте его до значения по умолчанию {0}.Therefore, either clear out this property altogether or reset it to the default, {0}. Если вам нужно обновить память в этом свойстве ObjectDataSource, ознакомьтесь с обзором учебника по вставке, обновлению и удалению данных .If you need to refresh your memory on this ObjectDataSource property, see the An Overview of Inserting, Updating, and Deleting Data tutorial.

После завершения работы мастера и исправления OldValuesParameterFormatStringдекларативная разметка ObjectDataSource s должна выглядеть примерно так, как показано ниже:After completing the wizard and fixing the OldValuesParameterFormatString, the ObjectDataSource s declarative markup should look similar like the following:

<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 s.After configuring the ObjectDataSource, add deleting capabilities to the GridView by checking the Enable Deleting checkbox from the GridView s smart tag. Это приведет к добавлению CommandField в GridView, свойство ShowDeleteButton которого имеет значение true.This will add a CommandField to the GridView whose ShowDeleteButton property is set to true.

включить поддержку удаления в GridViewEnable Support for Deleting in the GridView

Рис. 5. Включение поддержки удаления в GridView (щелкните, чтобы просмотреть изображение с полным размером)Figure 5: Enable Support for Deleting in the GridView (Click to view full-size image)

Уделите несколько минут тестированию функции удаления.Take a moment to test out the delete functionality. Существует внешний ключ между Products таблицей CategoryID и Categories таблицей CategoryID, поэтому при попытке удалить одну из первых восьми категорий возникнет исключение нарушения ограничения внешнего ключа.There is a foreign key between the Products table s CategoryID and the Categories table s CategoryID, so you will get a foreign key constraint violation exception if you attempt to delete any of the first eight categories. Чтобы протестировать эту функциональность, добавьте новую категорию, предоставляя как буклет, так и рисунок.To test this functionality out, add a new category, providing both a brochure and picture. Моя Категория тестирования, показанная на рис. 6, содержит тестовый файл буклета с именем Test.pdf и тестовый рисунок.My test category, shown in Figure 6, includes a test brochure file named Test.pdf and a test picture. На рис. 7 показан элемент управления GridView после добавления категории теста.Figure 7 shows the GridView after the test category has been added.

Добавление категории тестов с помощью буклета и изображенияAdd a Test Category with a Brochure and Image

Рис. 6. Добавление категории тестов с помощью буклета и изображения (щелкните, чтобы просмотреть изображение с полным размером)Figure 6: Add a Test Category with a Brochure and Image (Click to view full-size image)

после вставки категории тестов он отображается в GridViewAfter Inserting the Test Category, it is Displayed in the GridView

Рис. 7. После вставки категории тестов она отображается в GridView (щелкните, чтобы просмотреть изображение с полным размером)Figure 7: After Inserting the Test Category, it is Displayed in the GridView (Click to view full-size image)

В Visual Studio Обновите обозреватель решений.In Visual Studio, refresh the Solution Explorer. Теперь в папке ~/Brochures должен отобразиться новый файл Test.pdf (см. рис. 8).You should now see a new file in the ~/Brochures folder, Test.pdf (see Figure 8).

Затем щелкните ссылку удалить в строке Категория теста, чтобы вызвать обратную передачу страницы и метод CategoriesBLL класса DeleteCategory.Next, click the Delete link in the Test Category row, causing the page to postback and the CategoriesBLL class s DeleteCategory method to fire. Это вызовет метод DAL Delete, который вызывает отправку соответствующей инструкции DELETE в базу данных.This will invoke the DAL s Delete method, causing the appropriate DELETE statement to be sent to the database. Затем данные повторно привязываются к GridView, а разметка отправляется обратно клиенту с категорией тестов, которая больше не существует.The data is then rebound to the GridView and the markup is sent back to the client with the Test Category no longer present.

Хотя рабочий процесс удаления успешно удалил запись категории теста из таблицы Categories, файл брошюры не был удален из файловой системы веб-сервера.While the delete workflow successfully removed the Test Category record from the Categories table, it did not remove its brochure file from the web server s file system. Обновите обозреватель решений, и вы увидите, что Test.pdf по-прежнему находится в папке ~/Brochures.Refresh the Solution Explorer and you will see that Test.pdf is still sitting in the ~/Brochures folder.

Файл Test. PDF не был удален из файловой системы веб-сервера

Рис. 8. файл Test.pdf не был удален из файловой системы веб-сервераFigure 8: The Test.pdf File Was Not Deleted from the Web Server s File System

Шаг 5. Удаление файла брошюры удаленной категорииStep 5: Removing the Deleted Category s Brochure File

Одним из недостатков хранения двоичных данных, внешних в базе данных, является необходимость выполнения дополнительных действий для очистки этих файлов при удалении связанной записи базы данных.One of the downsides of storing binary data external to the database is that extra steps must be taken to clean up these files when the associated database record is deleted. GridView и ObjectDataSource предоставляют события, которые срабатывают как до, так и после выполнения команды DELETE.The GridView and ObjectDataSource provide events that fire both before and after the delete command has been performed. Нам действительно нужно создать обработчики событий для событий, выполняемых до и после действия.We actually need to create event handlers for both the pre- and post-action events. Перед удалением записи Categories необходимо определить путь к PDF-файлу, но мы не хотим удалять PDF-файл перед удалением категории в случае, если есть исключение и категория не удалена.Before the Categories record is deleted we need to determine its PDF file s path, but we don t want to delete the PDF before the category is deleted in case there is some exception and the category is not deleted.

СобытиеRowDeleting GridView s срабатывает до вызова команды ObjectDataSource s DELETE, в то время как событиеRowDeleted срабатывает после.The GridView s RowDeleting event fires before the ObjectDataSource s delete command has been invoked, while its RowDeleted event fires after. Создайте обработчики событий для этих двух событий, используя следующий код:Create event handlers for these two events using the following code:

// A page variable to "remember" the deleted category's BrochurePath value 
string deletedCategorysPdfPath = null;
protected void Categories_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
    // Determine the PDF path for the category being deleted...
    int categoryID = Convert.ToInt32(e.Keys["CategoryID"]);
    CategoriesBLL categoryAPI = new CategoriesBLL();
    Northwind.CategoriesDataTable categories = 
        categoryAPI.GetCategoryByCategoryID(categoryID);
    Northwind.CategoriesRow category = categories[0];
    if (category.IsBrochurePathNull())
        deletedCategorysPdfPath = null;
    else
        deletedCategorysPdfPath = category.BrochurePath;
}
protected void Categories_RowDeleted(object sender, GridViewDeletedEventArgs e)
{
    // Delete the brochure file if there were no problems deleting the record
    if (e.Exception == null)
    {
        // Is there a file to delete?
        if (deletedCategorysPdfPath != null)
        {
            System.IO.File.Delete(Server.MapPath(deletedCategorysPdfPath));
        }
    }
}

В обработчике событий RowDeleting CategoryID удаляемой строки извлечена из коллекции GridView s DataKeys, которую можно получить в этом обработчике событий через коллекцию e.Keys.In the RowDeleting event handler, the CategoryID of the row being deleted is grabbed from the GridView s DataKeys collection, which can be accessed in this event handler through the e.Keys collection. Затем вызывается GetCategoryByCategoryID(categoryID) класса CategoriesBLL, чтобы получить сведения об удаляемой записи.Next, the CategoriesBLL class s GetCategoryByCategoryID(categoryID) is invoked to return information about the record being deleted. Если возвращаемый объект CategoriesDataRow имеет значение, отличное отNULL``BrochurePath, то оно сохраняется в переменной страницы deletedCategorysPdfPath, чтобы файл можно было удалить в обработчике событий RowDeleted.If the returned CategoriesDataRow object has a non-NULL``BrochurePath value then it is stored in the page variable deletedCategorysPdfPath so that the file can be deleted in the RowDeleted event handler.

Note

Вместо того чтобы получать сведения о BrochurePath для записи Categories, удаляемой в обработчике событий RowDeleting, можно было бы добавить BrochurePath в свойство GridView s DataKeyNames и получить доступ к значению записи s через коллекцию e.Keys.Rather than retrieving the BrochurePath details for the Categories record being deleted in the RowDeleting event handler, we could have alternatively added the BrochurePath to the GridView s DataKeyNames property and accessed the record s value through the e.Keys collection. Это приведет к незначительному увеличению размера представления GridView s, но уменьшит объем необходимого кода и сохранит поездку в базе данных.Doing so would slightly increase the GridView s view state size, but would reduce the amount of code needed and save a trip to the database.

После вызова базовой команды удаления ObjectDataSource s вызывается обработчик событий RowDeleted GridView s.After the ObjectDataSource s underlying delete command has been invoked, the GridView s RowDeleted event handler fires. Если при удалении данных нет исключений и имеется значение для deletedCategorysPdfPath, то PDF-файл будет удален из файловой системы.If there were no exceptions in deleting the data and there is a value for deletedCategorysPdfPath, then the PDF is deleted from the file system. Обратите внимание, что этот дополнительный код не требуется для очистки двоичных данных категории s, связанных со своим изображением.Note that this extra code is not needed to clean up the category s binary data associated with its picture. Так как данные изображения хранятся непосредственно в базе данных, удаление Categories строки также приводит к удалению данных из этой категории.That s because the picture data is stored directly in the database, so deleting the Categories row also deletes that category s picture data.

После добавления двух обработчиков событий запустите этот тестовый случай еще раз.After adding the two event handlers, run this test case again. При удалении категории также удаляется связанный с ней PDF-файл.When deleting the category, its associated PDF is also deleted.

Обновление существующих двоичных данных, связанных с существующими записями, предоставляет некоторые интересные трудности.Updating an existing record s associated binary data provides some interesting challenges. Оставшаяся часть этого руководства посвящена добавлению возможностей обновления в буклет и фотографию.The remainder of this tutorial delves into adding update capabilities to the brochure and picture. На шаге 6 рассматриваются методы обновления сведений буклета, а на шаге 7 рассматривается обновление изображения.Step 6 explores techniques for updating the brochure information while Step 7 looks at updating the picture.

Шаг 6. обновление буклета категорииStep 6: Updating a Category s Brochure

Как обсуждалось в обзоре руководства по вставке, обновлению и удалению данных , GridView предлагает встроенную поддержку редактирования на уровне строк, которая может быть реализована в такте флажка, если его базовый источник данных настроен соответствующим образом.As discussed in the An Overview of Inserting, Updating, and Deleting Data tutorial, the GridView offers built-in row-level editing support that can be implemented by the tick of a checkbox if its underlying data source is appropriately configured. В настоящее время CategoriesDataSource ObjectDataSource еще не настроен на включение поддержки обновления, поэтому добавим его в.Currently, the CategoriesDataSource ObjectDataSource is not yet configured to include updating support, so let s add that in.

Щелкните ссылку Настроить источник данных в мастере ObjectDataSource s и перейдите к второму шагу.Click the Configure Data Source link from the ObjectDataSource s wizard and proceed to the second step. Из-за DataObjectMethodAttribute, используемых в CategoriesBLL, раскрывающийся список обновлений должен автоматически заполняться перегрузкой UpdateCategory, которая принимает четыре входных параметра (для всех столбцов, но Picture).Because of the DataObjectMethodAttribute used in CategoriesBLL, the UPDATE drop-down list should automatically be populated with the UpdateCategory overload that accepts four input parameters (for all columns but Picture). Измените этот параметр, чтобы использовать перегрузку с пятью параметрами.Change this so that it uses the overload with five parameters.

настроить ObjectDataSource для использования метода Упдатекатегори, который включает параметр для изображенияConfigure the ObjectDataSource to Use the UpdateCategory Method that Includes a Parameter for Picture

Рис. 9. Настройка ObjectDataSource для использования метода UpdateCategory, который включает параметр для Picture (щелкните, чтобы просмотреть изображение с полным размером)Figure 9: Configure the ObjectDataSource to Use the UpdateCategory Method that Includes a Parameter for Picture (Click to view full-size image)

Теперь ObjectDataSource будет включать значение свойства UpdateMethod, а также соответствующие UpdateParameter s.The ObjectDataSource will now include a value for its UpdateMethod property as well as corresponding UpdateParameter s. Как указано на шаге 4, Visual Studio устанавливает свойство OldValuesParameterFormatString ObjectDataSource s в значение original_{0} при использовании мастера настройки источника данных.As noted in Step 4, Visual Studio sets the ObjectDataSource s OldValuesParameterFormatString property to original_{0} when using the Configure Data Source wizard. Это вызовет проблемы с вызовами методов Update и DELETE.This will cause problems with the update and delete method invocations. Поэтому либо очистите это свойство полностью, либо сбросьте его до значения по умолчанию {0}.Therefore, either clear out this property altogether or reset it to the default, {0}.

После завершения работы мастера и исправления OldValuesParameterFormatStringдекларативная разметка ObjectDataSource s должна выглядеть следующим образом:After completing the wizard and fixing the OldValuesParameterFormatString, the ObjectDataSource s declarative markup should look like the following:

<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 s, установите флажок Включить правку в смарт-теге GridView s.To turn on the GridView s built-in editing features, check the Enable Editing option from the GridView s smart tag. При этом для свойства CommandField ShowEditButton s устанавливается значение true, что приводит к добавлению кнопки изменить (и кнопкам обновления и отмены для редактируемой строки).This will set the CommandField s ShowEditButton property to true, resulting in the addition of an Edit button (and Update and Cancel buttons for the row being edited).

настроить GridView для поддержки редактированияConfigure the GridView to Support Editing

Рис. 10. Настройка элемента управления GridView для поддержки редактирования (щелкните, чтобы просмотреть изображение с полным размером)Figure 10: Configure the GridView to Support Editing (Click to view full-size image)

Откройте страницу в браузере и щелкните одну из кнопок редактирования строки.Visit the page through a browser and click one of the row s Edit buttons. CategoryName и Description BoundFields подготавливаются как текстовые поля.The CategoryName and Description BoundFields are rendered as textboxes. BrochurePath TemplateField не имеет EditItemTemplate, поэтому он по-своему ItemTemplate ссылку на буклет.The BrochurePath TemplateField lacks an EditItemTemplate, so it continues to show its ItemTemplate a link to the brochure. Picture Имажефиелд готовится к просмотру как текстовое поле, Text свойству присваивается значение DataImageUrlField Имажефиелд s, в данном случае CategoryID.The Picture ImageField renders as a TextBox whose Text property is assigned the value of the ImageField s DataImageUrlField value, in this case CategoryID.

GridView не имеет интерфейса редактирования для БрочурепасThe GridView Lacks an Editing Interface for BrochurePath

Рис. 11. в GridView отсутствует интерфейс редактирования для BrochurePath (щелкните, чтобы просмотреть изображение с полным размером)Figure 11: The GridView Lacks an Editing Interface for BrochurePath (Click to view full-size image)

Настройка интерфейса редактированияBrochurePathsCustomizing theBrochurePaths Editing Interface

Необходимо создать интерфейс редактирования для BrochurePath TemplateField, который позволяет пользователю:We need to create an editing interface for the BrochurePath TemplateField, one that allows the user to either:

  • Оставьте буклет категории "как есть",Leave the category s brochure as-is,
  • Обновление буклета Category с помощью отправки новой брошюры илиUpdate the category s brochure by uploading a new brochure, or
  • Полностью удалите буклет Category (категория) (в случае, если категория больше не имеет связанной брошюры).Remove the category s brochure altogether (in the case that the category no longer has an associated brochure).

Также необходимо обновить интерфейс редактирования Picture Имажефиелд s, но мы покажем это на шаге 7.We also need to update the Picture ImageField s editing interface, but we'll get to this in Step 7.

Из смарт-тега GridView s щелкните ссылку Edit Templates (изменить шаблоны) и выберите BrochurePath TemplateField s EditItemTemplate из раскрывающегося списка.From the GridView s smart tag, click on the Edit Templates link and select the BrochurePath TemplateField s EditItemTemplate from the drop-down list. Добавьте в этот шаблон веб-элемент управления RadioButtonList, задав для его свойства ID значение BrochureOptions а свойству AutoPostBack — значение true.Add a RadioButtonList Web control to this template, setting its ID property to BrochureOptions and its AutoPostBack property to true. На окно свойств щелкните многоточие в свойстве Items, чтобы открыть редактор коллекции ListItem.From the Properties window, click on the ellipses in the Items property, which will bring up the ListItem Collection Editor. Добавьте следующие три параметра с Value s 1, 2 и 3 соответственно:Add the following three options with Value s 1, 2 and 3, respectively:

  • Использовать текущий буклетUse current brochure
  • Удалить текущую буклетRemove current brochure
  • Отправить новую брошюруUpload new brochure

Задайте для свойства First ListItem s Selected значение true.Set the first ListItem s Selected property to true.

Добавление трех ListItem в RadioButtonList

Рис. 12. добавление трех ListItem s в RadioButtonListFigure 12: Add Three ListItem s to the RadioButtonList

Под названием RadioButtonList добавьте элемент управления FileUpload с именем BrochureUpload.Beneath the RadioButtonList, add a FileUpload control named BrochureUpload. Задайте для свойства Visible значение false.Set its Visible property to false.

добавить элемент управления RadioButtonList и FileUpload в EditItemTemplateAdd a RadioButtonList and FileUpload Control to the EditItemTemplate

Рис. 13. Добавление элемента управления RadioButtonList и FileUpload в EditItemTemplate (щелкните, чтобы просмотреть изображение с полным размером)Figure 13: Add a RadioButtonList and FileUpload Control to the EditItemTemplate (Click to view full-size image)

Этот RadioButtonList предоставляет три варианта для пользователя.This RadioButtonList provides the three options for the user. Идея состоит в том, что элемент управления FileUpload будет отображаться только в том случае, если выбран последний параметр Отправить новую брошюру.The idea is that the FileUpload control will be displayed only if the last option, Upload new brochure, is selected. Для этого создайте обработчик событий для события SelectedIndexChanged RadioButtonList s и добавьте следующий код:To accomplish this, create an event handler for the RadioButtonList s SelectedIndexChanged event and add the following code:

protected void BrochureOptions_SelectedIndexChanged(object sender, EventArgs e)
{
    // Get a reference to the RadioButtonList and its Parent
    RadioButtonList BrochureOptions = (RadioButtonList)sender;
    Control parent = BrochureOptions.Parent;
    // Now use FindControl("controlID") to get a reference of the 
    // FileUpload control
    FileUpload BrochureUpload = 
        (FileUpload)parent.FindControl("BrochureUpload");
    // Only show BrochureUpload if SelectedValue = "3"
    BrochureUpload.Visible = (BrochureOptions.SelectedValue == "3");
}

Поскольку элементы управления RadioButtonList и FileUpload находятся в шаблоне, нам нужно написать немного кода для программного доступа к этим элементам управления.Since the RadioButtonList and FileUpload controls are within a template, we have to write a bit of code to programmatically access these controls. Обработчику SelectedIndexChanged событий передается ссылка на RadioButtonList в входном параметре sender.The SelectedIndexChanged event handler is passed a reference of the RadioButtonList in the sender input parameter. Чтобы получить элемент управления FileUpload, необходимо получить родительский элемент управления RadioButtonList s и использовать в нем метод FindControl("controlID").To get the FileUpload control, we need to get the RadioButtonList s parent control and use the FindControl("controlID") method from there. После того как у нас есть ссылка на элементы управления RadioButtonList и FileUpload, свойство Visible элемента управления FileUpload имеет значение true только в том случае, если RadioButtonList s SelectedValue равно 3, то есть Value для нового буклета upload ListItem.Once we have a reference to both the RadioButtonList and FileUpload controls, the FileUpload control s Visible property is set to true only if the RadioButtonList s SelectedValue equals 3, which is the Value for the Upload new brochure ListItem.

Используя этот код, уделите немного времени тестированию интерфейса правки.With this code in place, take a moment to test out the editing interface. Нажмите кнопку изменить для строки.Click on the Edit button for a row. Изначально должен быть выбран параметр использовать текущий буклет.Initially, the Use current brochure option should be selected. Изменение выбранного индекса вызывает обратную передачу.Changing the selected index causes a postback. Если выбран третий параметр, элемент управления FileUpload отображается, в противном случае он является скрытым.If the third option is selected, the FileUpload control is displayed, otherwise it is hidden. На рис. 14 показан интерфейс правки при первом нажатии кнопки «изменить»; На рис. 15 показан интерфейс после выбора параметра отправить новый буклет.Figure 14 shows the editing interface when the Edit button is first clicked; Figure 15 shows the interface after the Upload new brochure option is selected.

изначально выбран параметр использовать текущую буклет.Initially, the Use current brochure Option is Selected

Рис. 14. изначально выбран параметр использовать текущую буклет (щелкните, чтобы просмотреть изображение с полным размером).Figure 14: Initially, the Use current brochure Option is Selected (Click to view full-size image)

выборе параметра Отправить новую брошюру отображается элемент управления FileUpload.Choosing the Upload new brochure Option Displays the FileUpload Control

Рис. 15. при выборе параметра Отправить новую брошюру отображается элемент управления FileUpload (щелкните, чтобы просмотреть изображение с полным размером)Figure 15: Choosing the Upload new brochure Option Displays the FileUpload Control (Click to view full-size image)

Сохранение файла буклета и обновление столбцаBrochurePathSaving the Brochure File and Updating theBrochurePathColumn

При нажатии кнопки "Обновить" GridView s срабатывает RowUpdating событие.When the GridView s Update button is clicked, its RowUpdating event fires. Вызовите команду ObjectDataSource s Update, после чего срабатывает событие RowUpdated GridView s.The ObjectDataSource s update command is invoked and then the GridView s RowUpdated event fires. Как и при удалении рабочего процесса, необходимо создать обработчики событий для обоих этих событий.Like with the deleting workflow, we need to create event handlers for both of these events. В обработчике событий RowUpdating необходимо определить, какое действие следует предпринять в зависимости от SelectedValue BrochureOptions RadioButtonList:In the RowUpdating event handler, we need to determine what action to take based on the SelectedValue of the BrochureOptions RadioButtonList:

  • Если SelectedValue равен 1, мы хотим использовать тот же параметр BrochurePath.If the SelectedValue is 1, we want to keep using the same BrochurePath setting. Поэтому необходимо установить параметр brochurePath ObjectDataSource s в существующее BrochurePath значение обновляемой записи.Therefore, we need to set the ObjectDataSource s brochurePath parameter to the existing BrochurePath value of the record being updated. Параметр brochurePath ObjectDataSource s можно задать с помощью e.NewValues["brochurePath"] = value.The ObjectDataSource s brochurePath parameter can be set using e.NewValues["brochurePath"] = value.
  • Если SelectedValue имеет значение 2, необходимо задать BrochurePath записи для значения NULL.If the SelectedValue is 2, then we want to set the record s BrochurePath value to NULL. Это можно сделать, присвоив параметру ObjectDataSource brochurePath значение Nothing, в результате чего NULL базы данных будет использоваться в инструкции UPDATE.This can be accomplished by setting the ObjectDataSource s brochurePath parameter to Nothing, which results in a database NULL being used in the UPDATE statement. Если удаляется существующий файл буклета, необходимо удалить существующий файл.If there is an existing brochure file that is being removed, we need to delete the existing file. Однако мы хотим сделать это только в том случае, если обновление завершается без возникновения исключения.However, we only want to do this if the update completes without raising an exception.
  • Если SelectedValue равен 3, мы хотим убедиться, что пользователь передал PDF-файл, а затем сохранил его в файловой системе и изменил значение столбца записей BrochurePath.If the SelectedValue is 3, then we want to ensure that the user has uploaded a PDF file and then save it to the file system and update the record s BrochurePath column value. Более того, если имеется существующий файл буклета, который заменяется, необходимо удалить предыдущий файл.Moreover, if there is an existing brochure file that is being replaced, we need to delete the previous file. Однако мы хотим сделать это только в том случае, если обновление завершается без возникновения исключения.However, we only want to do this if the update completes without raising an exception.

Действия, которые необходимо выполнить, когда RadioButtonList s SelectedValue имеет значение 3, практически идентичны тем, которые используются обработчиком событий DetailsView s ItemInserting.The steps needed to be completed when the RadioButtonList s SelectedValue is 3 are virtually identical to those used by the DetailsView s ItemInserting event handler. Этот обработчик событий выполняется при добавлении новой записи категории из элемента управления DetailsView, добавленного в предыдущем руководстве.This event handler is executed when a new category record is added from the DetailsView control we added in the previous tutorial. Таким образом, мы наполнения, что эти функции можно оптимизировать в отдельные методы.Therefore, it behooves us to refactor this functionality out into separate methods. В частности, я переместил общую функциональность на два метода:Specifically, I moved out the common functionality into two methods:

  • ProcessBrochureUpload(FileUpload, out bool) принимает в качестве входных данных экземпляр элемента управления FileUpload и выходное логическое значение, которое указывает, следует ли продолжать операцию удаления или изменения или отменить ее из-за ошибки проверки.ProcessBrochureUpload(FileUpload, out bool) accepts as input a FileUpload control instance and an output Boolean value that specifies whether the delete or edit operation should proceed or if it should be cancelled due to some validation error. Этот метод возвращает путь к сохраненному файлу или null, если файл не был сохранен.This method returns the path to the saved file or null if no file was saved.
  • DeleteRememberedBrochurePath удаляет файл, указанный в пути в переменной страницы, deletedCategorysPdfPath если deletedCategorysPdfPath не null.DeleteRememberedBrochurePath deletes the file specified by the path in the page variable deletedCategorysPdfPath if deletedCategorysPdfPath is not null.

Ниже приведен код для этих двух методов.The code for these two methods follows. Обратите внимание на сходство между ProcessBrochureUpload и обработчиком событий DetailsView s ItemInserting из предыдущего руководства.Note the similarity between ProcessBrochureUpload and the DetailsView s ItemInserting event handler from the previous tutorial. В этом руководстве я обновил обработчики событий DetailsView s для использования этих новых методов.In this tutorial I have updated the DetailsView s event handlers to use these new methods. Скачайте код, связанный с этим руководством, чтобы увидеть изменения в обработчиках событий DetailsView s.Download the code associated with this tutorial to see the modifications to the DetailsView s event handlers.

private string ProcessBrochureUpload
    (FileUpload BrochureUpload, out bool CancelOperation)
{
    CancelOperation = false;    // by default, do not cancel operation
    if (BrochureUpload.HasFile)
    {
        // Make sure that a PDF has been uploaded
        if (string.Compare(System.IO.Path.GetExtension(BrochureUpload.FileName), 
            ".pdf", true) != 0)
        {
            UploadWarning.Text = 
                "Only PDF documents may be used for a category's brochure.";
            UploadWarning.Visible = true;
            CancelOperation = true;
            return null;
        }
        const string BrochureDirectory = "~/Brochures/";
        string brochurePath = BrochureDirectory + BrochureUpload.FileName;
        string fileNameWithoutExtension = 
            System.IO.Path.GetFileNameWithoutExtension(BrochureUpload.FileName);
        int iteration = 1;
        while (System.IO.File.Exists(Server.MapPath(brochurePath)))
        {
            brochurePath = string.Concat(BrochureDirectory, fileNameWithoutExtension, 
                "-", iteration, ".pdf");
            iteration++;
        }
        // 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 null;
    }
}
private void DeleteRememberedBrochurePath()
{
    // Is there a file to delete?
    if (deletedCategorysPdfPath != null)
    {
        System.IO.File.Delete(Server.MapPath(deletedCategorysPdfPath));
    }
}

Обработчики событий GridView RowUpdating и RowUpdated используют методы ProcessBrochureUpload и DeleteRememberedBrochurePath, как показано в следующем коде:The GridView s RowUpdating and RowUpdated event handlers use the ProcessBrochureUpload and DeleteRememberedBrochurePath methods, as the following code shows:

protected void Categories_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
    // Reference the RadioButtonList
    RadioButtonList BrochureOptions = 
        (RadioButtonList)Categories.Rows[e.RowIndex].FindControl("BrochureOptions");
    // Get BrochurePath information about the record being updated
    int categoryID = Convert.ToInt32(e.Keys["CategoryID"]);
    CategoriesBLL categoryAPI = new CategoriesBLL();
    Northwind.CategoriesDataTable categories = 
        categoryAPI.GetCategoryByCategoryID(categoryID);
    Northwind.CategoriesRow category = categories[0];
    if (BrochureOptions.SelectedValue == "1")
    {
        // Use current value for BrochurePath
        if (category.IsBrochurePathNull())
            e.NewValues["brochurePath"] = null;
        else
            e.NewValues["brochurePath"] = category.BrochurePath;
    }
    else if (BrochureOptions.SelectedValue == "2")
    {
        // Remove the current brochure (set it to NULL in the database)
        e.NewValues["brochurePath"] = null;
    }
    else if (BrochureOptions.SelectedValue == "3")
    {
        // Reference the BrochurePath FileUpload control
        FileUpload BrochureUpload = 
            (FileUpload)Categories.Rows[e.RowIndex].FindControl("BrochureUpload");
        // Process the BrochureUpload
        bool cancelOperation = false;
        e.NewValues["brochurePath"] = 
            ProcessBrochureUpload(BrochureUpload, out cancelOperation);
        e.Cancel = cancelOperation;
    }
    else
    {
        // Unknown value!
        throw new ApplicationException(
            string.Format("Invalid BrochureOptions value, {0}", 
                BrochureOptions.SelectedValue));
    }
    if (BrochureOptions.SelectedValue == "2" || 
        BrochureOptions.SelectedValue == "3")
    {
        // "Remember" that we need to delete the old PDF file
        if (category.IsBrochurePathNull())
            deletedCategorysPdfPath = null;
        else
            deletedCategorysPdfPath = category.BrochurePath;
    }
}
protected void Categories_RowUpdated(object sender, GridViewUpdatedEventArgs e)
{
    // If there were no problems and we updated the PDF file, 
    // then delete the existing one
    if (e.Exception == null)
    {
        DeleteRememberedBrochurePath();
    }
}

Обратите внимание, что обработчик событий RowUpdating использует ряд условных операторов для выполнения соответствующего действия на основе значения свойства BrochureOptions RadioButtonList s SelectedValue.Note how the RowUpdating event handler uses a series of conditional statements to perform the appropriate action based on the BrochureOptions RadioButtonList s SelectedValue property value.

Используя этот код, можно изменить категорию и использовать ее текущий буклет, не использовать буклет или передать новую.With this code in place, you can edit a category and have it use its current brochure, use no brochure, or upload a new one. Попробуйте. Задайте точки останова в обработчиках событий RowUpdating и RowUpdated, чтобы получить представление о рабочем процессе.Go ahead and try it out. Set breakpoints in the RowUpdating and RowUpdated event handlers to get a sense of the workflow.

Шаг 7. Отправка нового изображенияStep 7: Uploading a New Picture

Интерфейс редактирования Picture Имажефиелд s отображается как текстовое поле, заполненное значением из его свойства DataImageUrlField.The Picture ImageField s editing interface renders as a textbox populated with the value from its DataImageUrlField property. Во время рабочего процесса редактирования GridView передает параметр ObjectDataSource с параметром s, а значение свойства DataImageUrlField Имажефиелд s и значением параметра s, которое было указано в текстовом поле в интерфейсе правки.During the editing workflow, the GridView passes a parameter to the ObjectDataSource with the parameter s name the value of the ImageField s DataImageUrlField property and the parameter s value the value entered into the textbox in the editing interface. Такое поведение подходит при сохранении образа в виде файла в файловой системе, а DataImageUrlField содержит полный URL-адрес изображения.This behavior is suitable when the image is saved as a file on the file system and the DataImageUrlField contains the full URL of the image. В таких обстоятельствах интерфейс правки отображает URL-адрес изображения в текстовом поле, которое пользователь может изменить и сохранить обратно в базу данных.With such circumstances, the editing interface displays the image s URL in the textbox, which the user can change and have saved back to the database. Разумеется, этот интерфейс по умолчанию не разрешает пользователю отправлять новый образ, но позволяет им изменять URL-адрес изображения с текущего на другое.Granted, this default interface doesn t allow the user to upload a new image, but it does let them change the URL of the image from the current value to another. Однако в этом руководстве интерфейс редактирования по умолчанию Имажефиелд s недостаточен, поскольку Picture двоичные данные хранятся непосредственно в базе данных, а свойство DataImageUrlField содержит только CategoryID.For this tutorial, however, the ImageField s default editing interface does not suffice because the Picture binary data is being stored directly in the database and the DataImageUrlField property holds just the CategoryID.

Чтобы лучше понять, что происходит в нашем руководстве, когда пользователь редактирует строку с помощью Имажефиелд, рассмотрим следующий пример: пользователь редактирует строку с CategoryID 10, что приводит к отображению Picture Имажефиелд в виде текстового поля со значением 10.To better understand what happens in our tutorial when a user edits a row with an ImageField, consider the following example: a user edits a row with CategoryID 10, causing the Picture ImageField to render as a textbox with the value 10. Представьте, что пользователь изменяет значение в этом текстовом поле на 50 и нажимает кнопку Обновить.Imagine that the user changes the value in this textbox to 50 and clicks the Update button. Выполняется обратная передача, и GridView изначально создает параметр с именем CategoryID со значением 50.A postback occurs and the GridView initially creates a parameter named CategoryID with the value 50. Однако прежде чем элемент управления GridView отправит этот параметр (и параметры CategoryName и Description), он добавляет значения из коллекции DataKeys.However, before the GridView sends this parameter (and the CategoryName and Description parameters), it adds in the values from the DataKeys collection. Таким образом, параметр CategoryID будет перезаписан с базовым CategoryIDом текущей строки, равном 10.Therefore, it overwrites the CategoryID parameter with the current row s underlying CategoryID value, 10. Коротко говоря, интерфейс редактирования Имажефиелд s не влияет на рабочий процесс редактирования для этого руководства, так как имена Имажефиелд DataImageUrlField свойства, а DataKey Grid имеют одно и то же значение.In short, the ImageField s editing interface has no affect on the editing workflow for this tutorial because names of the ImageField s DataImageUrlField property and the grid s DataKey value are one in the same.

Хотя Имажефиелд упрощает отображение изображения на основе данных базы данных, нам не нужно предоставлять текстовое поле в интерфейсе редактирования.While the ImageField makes it easy to display an image based on database data, we don t want to provide a textbox in the editing interface. Вместо этого мы хотим предложить элемент управления FileUpload, который конечный пользователь может использовать для изменения изображения категории.Rather, we want to offer a FileUpload control that the end user can use to change the category s picture. В отличие от значения BrochurePath, в этих учебных курсах мы решили потребовать, чтобы каждая категория соимела изображение.Unlike the BrochurePath value, for these tutorials we ve decided to require that each category must have a picture. Поэтому нам не нужно, чтобы пользователь указал, что отсутствует связанный рисунок пользователь может либо отправить новую картинку, либо оставить текущее изображение без изменений.Therefore, we don t need to let the user indicate that there is no associated picture the user may either upload a new picture or leave the current picture as-is.

Чтобы настроить интерфейс редактирования Имажефиелд s, необходимо преобразовать его в TemplateField.To customize the ImageField s editing interface, we need to convert it into a TemplateField. Из смарт-тега GridView s щелкните ссылку Edit Columns (изменить столбцы), выберите Имажефиелд и щелкните преобразовать это поле в ссылку TemplateField.From the GridView s smart tag, click on the Edit Columns link, select the ImageField, and click the Convert this field into a TemplateField link.

Преобразование Имажефиелд в TemplateField

Рис. 16. Преобразование Имажефиелд в TemplateFieldFigure 16: Convert the ImageField Into a TemplateField

Таким образом, преобразование Имажефиелд в TemplateField создает TemplateField с двумя шаблонами.Converting the ImageField into a TemplateField in this manner generates a TemplateField with two templates. Как показано в следующем декларативном синтаксисе, ItemTemplate содержит веб-элемент управления Image, свойство ImageUrl которого назначается с помощью синтаксиса DataBinding, основанного на свойствах DataImageUrlField и DataImageUrlFormatString Имажефиелд s.As the following declarative syntax shows, the ItemTemplate contains an Image Web control whose ImageUrl property is assigned using databinding syntax based on the ImageField s DataImageUrlField and DataImageUrlFormatString properties. EditItemTemplate содержит текстовое поле, свойство Text которого привязано к значению, заданному свойством DataImageUrlField.The EditItemTemplate contains a TextBox whose Text property is bound to the value specified by the DataImageUrlField property.

<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>

Чтобы использовать элемент управления FileUpload, необходимо обновить EditItemTemplate.We need to update the EditItemTemplate to use a FileUpload control. Из смарт-тега GridView s щелкните ссылку Edit Templates (изменить шаблоны), а затем выберите Picture TemplateField s EditItemTemplate из раскрывающегося списка.From the GridView s smart tag click on the Edit Templates link and then select the Picture TemplateField s EditItemTemplate from the drop-down list. В шаблоне вы увидите текстовое поле, чтобы удалить это окно.In the template you should see a TextBox remove this. Затем перетащите элемент управления FileUpload из области элементов в шаблон, установив для его ID значение PictureUpload.Next, drag a FileUpload control from the Toolbox into the template, setting its ID to PictureUpload. Кроме того, добавьте текст для изменения рисунка категорий, указав новое изображение.Also add the text To change the category s picture, specify a new picture. Чтобы изображение категории s совпадало, оставьте поле пустым в шаблоне.To keep the category s picture the same, leave the field empty to the template, as well.

добавить элемент управления FileUpload в EditItemTemplateAdd a FileUpload Control to the EditItemTemplate

Рис. 17. Добавление элемента управления FileUpload в EditItemTemplate (щелкните, чтобы просмотреть изображение с полным размером)Figure 17: Add a FileUpload Control to the EditItemTemplate (Click to view full-size image)

После настройки интерфейса редактирования просматривайте ход выполнения в браузере.After customizing the editing interface, view your progress in a browser. При просмотре строки в режиме "только чтение" изображение Category отображается, как и ранее, но нажатие кнопки "Изменить" приводит к отображению столбца "Рисунок" в виде текста с элементом управления FileUpload.When viewing a row in read-only mode, the category s image is shown as it was before, but clicking on the Edit button renders the picture column as text with a FileUpload control.

интерфейс редактирования содержит элемент управления FileUploadThe Editing Interface Includes a FileUpload Control

Рис. 18. интерфейс правки содержит элемент управления FileUpload (щелкните, чтобы просмотреть изображение с полным размером)Figure 18: The Editing Interface Includes a FileUpload Control (Click to view full-size image)

Вспомним, что ObjectDataSource настроен на вызов метода CategoriesBLL класса UpdateCategory, который принимает в качестве входных данных двоичные данные для изображения в виде массива byte.Recall that the ObjectDataSource is configured to call the CategoriesBLL class s UpdateCategory method that accepts as input the binary data for the picture as a byte array. Если этот массив имеет null значение, то вызывается альтернативная перегрузка UpdateCategory, которая выдает инструкцию SQL UPDATE, которая не изменяет столбец Picture, тем самым сохраняя текущее изображение категории s.If this array has a null value, however, the alternate UpdateCategory overload is called, which issues the UPDATE SQL statement that does not modify the Picture column, thereby leaving the category s current picture intact. Таким образом, в обработчике событий GridView s RowUpdating необходимо программно ссылаться на элемент управления PictureUpload FileUpload и определить, передан ли файл.Therefore, in the GridView s RowUpdating event handler we need to programmatically reference the PictureUpload FileUpload control and determine if a file was uploaded. Если он не был передан, мы не хотим указывать значение для параметра picture.If one was not uploaded, then we do not want to specify a value for the picture parameter. С другой стороны, если файл был передан в элемент управления PictureUpload FileUpload, мы хотим убедиться, что это файл JPG.On the other hand, if a file was uploaded in the PictureUpload FileUpload control, we want to ensure that it is a JPG file. Если это так, мы можем отправить его двоичное содержимое в ObjectDataSource через параметр picture.If it is, then we can send its binary contents to the ObjectDataSource through the picture parameter.

Как и в случае с кодом, используемым на шаге 6, часть кода, необходимая здесь, уже существует в обработчике событий DetailsView ItemInserting.Like with the code used in Step 6, much of the code needed here already exists in the DetailsView s ItemInserting event handler. Поэтому мы выполнили рефакторинг общих функций в новом методе, ValidPictureUploadи обновили обработчик событий ItemInserting для использования этого метода.Therefore, I ve refactored the common functionality into a new method, ValidPictureUpload, and updated the ItemInserting event handler to use this method.

Добавьте следующий код в начало обработчика событий RowUpdating GridView s.Add the following code to the start of the GridView s RowUpdating event handler. Важно, чтобы этот код наступил перед кодом, который сохраняет файл буклета, так как мы не хотим сохранять буклет в файловой системе веб-сервера, если передан недопустимый файл изображения.It s important that this code come before the code that saves the brochure file since we don t want to save the brochure to the web server s file system if an invalid picture file is uploaded.

// Reference the PictureUpload FileUpload
FileUpload PictureUpload = 
    (FileUpload)Categories.Rows[e.RowIndex].FindControl("PictureUpload");
if (PictureUpload.HasFile)
{
    // Make sure the picture upload is valid
    if (ValidPictureUpload(PictureUpload))
    {
        e.NewValues["picture"] = PictureUpload.FileBytes;
    }
    else
    {
        // Invalid file upload, cancel update and exit event handler
        e.Cancel = true;
        return;
    }
}

Метод ValidPictureUpload(FileUpload) принимает в качестве единственного входного параметра элемент управления FileUpload и проверяет отправленное расширение файла, чтобы убедиться, что переданный файл является JPG; Он вызывается только при передаче файла изображения.The ValidPictureUpload(FileUpload) method takes in a FileUpload control as its sole input parameter and checks the uploaded file s extension to ensure that the uploaded file is a JPG; it is only called if a picture file is uploaded. Если файл не передается, параметр Picture не задается и, следовательно, использует значение по умолчанию null.If no file is uploaded, then the picture parameter is not set, and therefore uses its default value of null. Если изображение было отправлено и ValidPictureUpload возвращает true, параметру picture присваивается двоичные данные отправленного изображения. Если метод возвращает false, Рабочий процесс обновления отменяется и обработчик событий завершает работу.If a picture was uploaded and ValidPictureUpload returns true, the picture parameter is assigned the binary data of the uploaded image; if the method returns false, the update workflow is cancelled and the event handler exited.

Код метода ValidPictureUpload(FileUpload), который был рефакторингом из обработчика событий DetailsView s ItemInserting, выглядит следующим образом:The ValidPictureUpload(FileUpload) method code, which was refactored from the DetailsView s ItemInserting event handler, follows:

private bool ValidPictureUpload(FileUpload PictureUpload)
{
    // Make sure that a JPG has been uploaded
    if (string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), 
            ".jpg", true) != 0 &&
        string.Compare(System.IO.Path.GetExtension(PictureUpload.FileName), 
            ".jpeg", true) != 0)
    {
        UploadWarning.Text = 
            "Only JPG documents may be used for a category's picture.";
        UploadWarning.Visible = true;
        return false;
    }
    else
    {
        return true;
    }
}

Шаг 8. замена исходных категорий рисунками ЖпгсStep 8: Replacing the Original Categories Pictures with JPGs

Вспомним, что исходные восемь категорий — это файлы точечных рисунков, Упакованные в заголовок OLE.Recall that the original eight categories pictures are bitmap files wrapped in an OLE header. Теперь, когда мы добавили возможность изменения существующего изображения записи, уделите немного времени, чтобы заменить эти точечные рисунки на Жпгс.Now that we have added the capability to edit an existing record s picture, take a moment to replace these bitmaps with JPGs. Если вы хотите продолжить использовать изображения текущей категории, их можно преобразовать в Жпгс, выполнив следующие действия:If you want to continue to use the current category pictures, you can convert them to JPGs by performing the following steps:

  1. Сохраните точечные рисунки на жестком диске.Save the bitmap images to your hard drive. Перейдите на страницу UpdatingAndDeleting.aspx в браузере и для каждой из восьми категорий щелкните изображение правой кнопкой мыши и выберите сохранить изображение.Visit the UpdatingAndDeleting.aspx page in your browser and for each of the first eight categories, right-click on the image and choose to save the picture.
  2. Откройте изображение в любом редакторе изображений.Open the image in your image editor of choice. Например, можно использовать Microsoft Paint.You can use Microsoft Paint, for example.
  3. Сохраните точечный рисунок как изображение в формате JPG.Save the bitmap as a JPG image.
  4. Обновите изображение категории с помощью интерфейса редактирования, используя файл JPG.Update the category s picture through the editing interface, using the JPG file.

После изменения категории и передачи изображения JPG изображение не будет отображаться в браузере, так как DisplayCategoryPicture.aspx страница удаляет первые 78 байт из изображений первых восьми категорий.After editing a category and uploading the JPG image, the image will not render in the browser because the DisplayCategoryPicture.aspx page is stripping the first 78 bytes from the pictures of the first eight categories. Исправьте это, удалив код, который выполняет отбрасывания заголовка OLE.Fix this by removing the code that performs the OLE header stripping. После этого обработчик событий DisplayCategoryPicture.aspx``Page_Load должен иметь только следующий код:After doing this, the DisplayCategoryPicture.aspx``Page_Load event handler should have just the following code:

protected void Page_Load(object sender, EventArgs e)
{
    int categoryID = Convert.ToInt32(Request.QueryString["CategoryID"]);
    // Get information about the specified category
    CategoriesBLL categoryAPI = new CategoriesBLL();
    Northwind.CategoriesDataTable categories = _
        categoryAPI.GetCategoryWithBinaryDataByCategoryID(categoryID);
    Northwind.CategoriesRow category = 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);
}

Note

Интерфейсы вставки и редактирования UpdatingAndDeleting.aspx страниц могут использовать немного больше работы.The UpdatingAndDeleting.aspx page s inserting and editing interfaces could use a bit more work. CategoryName и Description BoundFields в DetailsView и GridView должны быть преобразованы в полей TemplateField.The CategoryName and Description BoundFields in the DetailsView and GridView should be converted into TemplateFields. Поскольку CategoryName не допускает NULL значений, необходимо добавить RequiredFieldValidator.Since CategoryName does not allow NULL values, a RequiredFieldValidator should be added. И текстовое поле Description, возможно, будет преобразовано в многострочное текстовое поле.And the Description TextBox should probably be converted into a multi-line TextBox. Я оставлю эти заключительные штрихи в качестве упражнения.I leave these finishing touches as an exercise for you.

СводкаSummary

В этом руководстве мы рассмотрим работу с двоичными данными.This tutorial completes our look at working with binary data. В этом руководстве и предыдущих трех примерах мы увидели, как двоичные данные могут храниться в файловой системе или непосредственно в базе данных.In this tutorial and the previous three, we saw how binary data can be stored on the file system or directly within the database. Пользователь предоставляет системе двоичные данные, выбирая файл с жесткого диска и отгружая его на веб-сервер, где он может храниться в файловой системе или вставляться в базу данных.A user provides binary data to the system by selecting a file from their hard drive and uploading it to the web server, where it can be stored on the file system or inserted into the database. ASP.NET 2,0 включает элемент управления FileUpload, который предоставляет такой интерфейс простым перетаскиванием.ASP.NET 2.0 includes a FileUpload control that makes providing such an interface as easy as drag and drop. Однако, как отмечалось в руководстве по отправке файлов , элемент управления FileUpload хорошо подходит для относительно небольших отправок файлов, в идеале не превышающий мегабайт.However, as noted in the Uploading Files tutorial, the FileUpload control is only well-suited for relatively small file uploads, ideally not exceeding a megabyte. Мы также изучили, как связать загруженные данные с базовой моделью данных, а также как изменять и удалять двоичные данные из существующих записей.We also explored how to associate uploaded data with the underlying data model, as well as how to edit and delete the binary data from existing records.

Следующий набор руководств посвящен различным методам кэширования.Our next set of tutorials explores various caching techniques. Кэширование предоставляет средства для повышения общей производительности приложения, получая результаты ресурсоемких операций и сохраняя их в расположении, доступном для быстрого доступа.Caching provides a means to improve an application s overall performance by taking the results from expensive operations and storing them in a location that can be more quickly accessed.

Поздравляем с программированием!Happy Programming!

Об автореAbout the Author

Скотт Митчелл, автор семи книг по ASP/ASP. NET и основатель 4GuysFromRolla.com, работал с веб-технологиями Майкрософт с 1998.Scott Mitchell, author of seven ASP/ASP.NET books and founder of 4GuysFromRolla.com, has been working with Microsoft Web technologies since 1998. Скотт работает как независимый консультант, преподаватель и модуль записи.Scott works as an independent consultant, trainer, and writer. Его последняя книга — Sams обучать себя ASP.NET 2,0 за 24 часа.His latest book is Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Он доступен по адресу mitchell@4GuysFromRolla.com.He can be reached at mitchell@4GuysFromRolla.com. или через его блог, который можно найти по адресу http://ScottOnWriting.NET.or via his blog, which can be found at http://ScottOnWriting.NET.

Специальная благодарностьSpecial Thanks To

Эта серия руководств была рассмотрена многими полезными рецензентами.This tutorial series was reviewed by many helpful reviewers. Специалист по интересу для этого руководства был Терезой Мерфи.Lead reviewer for this tutorial was Teresa Murphy. Хотите ознакомиться с моими будущими статьями MSDN?Interested in reviewing my upcoming MSDN articles? Если это так, расположите строку в mitchell@4GuysFromRolla.com.If so, drop me a line at mitchell@4GuysFromRolla.com.