濮阳杆衣贸易有限公司

主頁(yè) > 知識(shí)庫(kù) > 在ASP.NET 2.0中操作數(shù)據(jù)之五十五:編輯和刪除現(xiàn)有的二進(jìn)制數(shù)據(jù)

在ASP.NET 2.0中操作數(shù)據(jù)之五十五:編輯和刪除現(xiàn)有的二進(jìn)制數(shù)據(jù)

熱門標(biāo)簽:聯(lián)通官網(wǎng)400電話辦理 外呼電話機(jī)器人成本 臨沂智能電話機(jī)器人加盟 400電話辦理怎么樣 西寧呼叫中心外呼系統(tǒng)線路商 蘇州如何辦理400電話 網(wǎng)絡(luò)電話外呼系統(tǒng)上海 百應(yīng)電話機(jī)器人外呼系統(tǒng) 地圖標(biāo)注軟件免費(fèi)下載

導(dǎo)言:

  在前面的3章里我們?yōu)樘幚矶M(jìn)制數(shù)據(jù)添加了很多的功能。我們首先在表Categories里添加BrochurePath列,并更新了體系結(jié)構(gòu)。同樣,為了處理表Categorie里現(xiàn)有的Picture列,我們?cè)跀?shù)據(jù)訪問(wèn)層和業(yè)務(wù)邏輯層里增加了相應(yīng)的方法。同時(shí)我們創(chuàng)建一個(gè)頁(yè)面,在GridView控件里顯示二進(jìn)制數(shù)據(jù)——包含一個(gè)指向說(shuō)明小冊(cè)子的下載鏈接,并將每個(gè)類的圖片顯示在img>元素里。同時(shí)我們添加一個(gè)DetailsView控件,供用戶添加新的類,并上傳其圖片和小冊(cè)子數(shù)據(jù)。

  剩下的就是添加編輯和刪除功能,本章我們將通過(guò)GridView控件內(nèi)建的編輯和刪除功能來(lái)實(shí)現(xiàn)。當(dāng)編輯一個(gè)類時(shí),我們?cè)试S用戶用任意指定的圖片將原來(lái)的換掉;也可以用新的小冊(cè)子將現(xiàn)有的替換掉,甚至不再包含小冊(cè)子文件。讓我們開(kāi)始吧!

第1步:更新數(shù)據(jù)訪問(wèn)層

  雖然數(shù)據(jù)訪問(wèn)層包含自動(dòng)生成的Insert, Update和Delete方法,但它們都基于CategoriesTableAdapter的主查詢,因此并不包含Picture列。自然,Insert和Update方法也不包含picture列的相應(yīng)參數(shù)。就像56章做的那樣,我們需要為更新Categories表而創(chuàng)建新的TableAdapter方法。

  右鍵點(diǎn)擊CategoriesTableAdapter的頂部,選擇“添加查詢”,打開(kāi)TableAdapter查詢?cè)O(shè)置向?qū)?,我們首先選擇“使用SQL語(yǔ)句”,點(diǎn)Next,再選“UPDATE”,再點(diǎn)Next.


圖1:選擇“UPDATE”選項(xiàng)

我們現(xiàn)在需要指定UPDATE SQL語(yǔ)句。向?qū)ё詣?dòng)創(chuàng)建一個(gè)基于TableAdapter主查詢的UPDATE語(yǔ)句(它更新CategoryName, Description和BrochurePath值)。更新該語(yǔ)句以包含Picture列,以及@Picture參數(shù),像如下這樣:

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

最后,向?qū)б笪覀優(yōu)樾碌腡ableAdapter方法命名,我們?nèi)閁pdateWithPicture,再點(diǎn)Finish。


圖2:為新方法命名為UpdateWithPicture

第2步:添加新的業(yè)務(wù)邏輯方法

除了更新DAL外,我們需要更新BLL以包含更新、刪除類的方法。以下是表現(xiàn)層需要調(diào)用的方法:

為了刪除一個(gè)類,我們使用CategoriesTableAdapter的自動(dòng)生成的Delete方法,在類CategoriesBLL里添加如下的方法:

[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;
}

  本教程,為了更新一個(gè)類,我們將創(chuàng)建2個(gè)方法。一個(gè)方法接受picture值,并調(diào)用我們剛剛添加到CategoriesTableAdapter里的UpdateWithPicture方法。另一個(gè)方法只接受CategoryName, Description和BrochurePath值, 并調(diào)用CategoriesTableAdapter類里自動(dòng)生成的Update語(yǔ)句。為什么要使用2種方法呢?某些情況下,用戶更新類時(shí)同時(shí)更新其圖片,這時(shí)就需要上傳一張新圖片。上傳圖片的數(shù)據(jù)將在UPDATE語(yǔ)句里用到;另一種情況,用戶只想更新類的name和description信息,因此我們需要使用2種更新方法。業(yè)務(wù)邏輯層會(huì)根據(jù)是否傳入picture值來(lái)判斷使用哪種方法。

  為達(dá)該目的,我們要在CategoriesBLL類里添加2個(gè)方法,名字都是UpdateCategory,第一個(gè)方法接受的參數(shù)包括3個(gè)string,1個(gè)byte數(shù)組和1個(gè)int;第二個(gè)方法接受的參數(shù)包括3個(gè)string和1個(gè)int。3個(gè)字符串參數(shù)代表類的name, description和brochure文件路徑,byte數(shù)組包含的是類的picture數(shù)據(jù),int代表類記錄的CategoryID,我們注意到,當(dāng)傳入的byte數(shù)組為null時(shí),第一個(gè)方法將調(diào)用第二個(gè)方法。

[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步:拷貝功能

  在上一章里,我們創(chuàng)建了一個(gè)UploadInDetailsView.aspx頁(yè)面,在一個(gè)GridView控件列出所有的類,再通過(guò)一個(gè)DetailsView控件來(lái)添加新的類。在本教程,我們將擴(kuò)展GridView控件以支持編輯和刪除功能。不過(guò)我們不再使用UploadInDetailsView.aspx頁(yè)面,讓我們?cè)趡/BinaryData文件夾里創(chuàng)建一個(gè)新頁(yè)面,UpdatingAndDeleting.aspx,將UploadInDetailsView.aspx頁(yè)面的聲明代碼復(fù)制并粘貼到頁(yè)面UpdatingAndDeleting.aspx.

  打開(kāi)UploadInDetailsView.aspx頁(yè)面,將其asp:Content>元素里的聲明代碼復(fù)制下來(lái),就像圖3那樣。接下來(lái),打開(kāi)UpdatingAndDeleting.aspx頁(yè)面,把代碼粘貼在asp:Content>元素里。同樣的,將UploadInDetailsView.aspx頁(yè)面的后臺(tái)代碼拷貝到UpdatingAndDeleting.aspx。


圖3:將UploadInDetailsView.aspx頁(yè)面的聲明代碼拷貝下來(lái)

完成后,登錄UpdatingAndDeleting.aspx頁(yè)面,你將會(huì)看到相同的輸出效果。感覺(jué)用起來(lái)和UploadInDetailsView.aspx頁(yè)面一樣。

第4步:添加ObjectDataSource和GridView的刪除功能

  就像在教程16《概述插入、更新和刪除數(shù)據(jù)》里探討的一樣,只要GridView控件綁定的數(shù)據(jù)源支持“刪除”功能,我們就可以為GridView控件啟用刪除功能。不過(guò),GridView控件綁定的ObjectDataSource(也就是CategoriesDataSource)目前并不支持刪除。

  為支持刪除,在ObjectDataSource的智能標(biāo)簽里點(diǎn)“配置數(shù)據(jù)源”,一直點(diǎn)到“定義數(shù)據(jù)方法”界面。雖然當(dāng)前只指定了ObjectDataSource控件InsertMethod屬性和SelectMethod屬性,但向?qū)ё詣?dòng)地分別為UPDATE標(biāo)簽和DELETE標(biāo)簽指定UpdateCategory方法和DeleteCategory方法。為什么呢?因?yàn)槲覀冊(cè)贑ategoriesBLL類里為上述2種方法使用了DataObjectMethodAttribute屬性,作用是分別使其成為默認(rèn)的“更新”和“刪除”方法。

  不過(guò)現(xiàn)在我們?cè)赨PDATE標(biāo)簽的下拉列表里選“(None)”, 而 DELETE標(biāo)簽里仍然為DeleteCategory方法。我們將在第6步添加更新功能。


圖4:設(shè)置ObjectDataSource控件使用DeleteCategory方法

  注意:完成設(shè)置后,Visual Studio會(huì)問(wèn)你是否“刷新列和主鍵”,選擇No,因?yàn)檫x擇Yes將會(huì)把我們自己定制的任何列覆蓋掉。

  現(xiàn)在,ObjectDataSource控件將包含DeleteMethod屬性和對(duì)應(yīng)的DeleteParameter參數(shù)。我們記得在以前的教程提到過(guò),當(dāng)使用向?qū)е付ǚ椒〞r(shí),Visual Studio會(huì)自動(dòng)的將ObjectDataSource控件的OldValuesParameterFormatString屬性設(shè)置為original_{0},這將導(dǎo)致更新和刪除時(shí)出現(xiàn)問(wèn)題。為此,要么將清除該屬性,要么將其設(shè)置為默認(rèn)的{0}值。對(duì)該屬性的更詳細(xì)討論見(jiàn)教程16《概述插入、更新和刪除數(shù)據(jù)》

完成后,ObjectDataSource控件的聲明代碼看起來(lái)應(yīng)該像下面的一樣:

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>

設(shè)置完ObjectDataSource后,就可以啟用GridView的刪除功能了,方法是點(diǎn)擊其智能標(biāo)簽里的“刪除選項(xiàng)”。這將使GridView增加一個(gè)CommandField,其ShowDeleteButton屬性為true。


圖5:?jiǎn)⒂肎ridView控件的刪除功能

  花幾分鐘測(cè)試刪除功能。由于表Products和表Categories之間有一個(gè)外鍵CategoryID,當(dāng)你刪除現(xiàn)有的8個(gè)類中的任何一個(gè)時(shí),你會(huì)得到一個(gè)外鍵約束沖突異常。為順利的實(shí)現(xiàn)測(cè)試,我們需要添加一個(gè)附帶圖片和說(shuō)明小冊(cè)子的新類,如圖6所示,小冊(cè)子為Test.pdf,圖7為添加了測(cè)試類的GridView控件界面。


圖6:添加一個(gè)附帶Brochure和Image文件的測(cè)試類


圖7:添加測(cè)試類后,顯示在GridView控件里

在Visual Studio里刷新解決資源管理器,你會(huì)在文件夾~/Brochures里看到Test.pdf文件(見(jiàn)圖8)

下一步,點(diǎn)擊Test類的Delete鏈接,頁(yè)面回傳,引發(fā)CategoriesBLL的DeleteCategory 方法,該方法又調(diào)用DAL層的Delete方法,向數(shù)據(jù)庫(kù)發(fā)送適當(dāng)?shù)腄ELETE命令。最后數(shù)據(jù)重新綁定到GridView控件,Test類將不再顯示出來(lái)。

雖然已經(jīng)成功地將Test類從Categories表刪除,但存儲(chǔ)在文件系統(tǒng)的對(duì)應(yīng)小冊(cè)子仍舊存在,刷新解決資源管理器,你將發(fā)現(xiàn)Test.pdf依然放在~/Brochures文件夾里。


圖8:Test.pdf文件并沒(méi)有從文件系統(tǒng)刪除

第5步:刪除殘存的Brochure文件

  未將二進(jìn)制數(shù)據(jù)存儲(chǔ)進(jìn)數(shù)據(jù)庫(kù)時(shí)面臨的一個(gè)問(wèn)題便是:當(dāng)刪除一條數(shù)據(jù)庫(kù)記錄時(shí),我們需要另外采取步驟來(lái)刪除該記錄對(duì)應(yīng)的二進(jìn)制數(shù)據(jù)文件。當(dāng)執(zhí)行delete命令時(shí),會(huì)發(fā)生一些事前事件和事后事件(pre- and post-action events),我們需要?jiǎng)?chuàng)建對(duì)應(yīng)的事件處理器。在Categories表的記錄被刪除之前,我們需要確定對(duì)應(yīng)PDF文件的路徑,但在刪除記錄之前我們不會(huì)刪除其對(duì)應(yīng)的PDF文件,以防發(fā)生異?;蛴涗涀罱K未被刪除的情況。

  從事件發(fā)生的時(shí)間先后順序來(lái)看,GridView控件的RowDeleting事件在調(diào)用ObjectDataSource控件的delete命令前發(fā)生;而RowDeleted事件在調(diào)用ObjectDataSource控件的delete命令之后再發(fā)生。創(chuàng)建這2個(gè)事件處理器,代碼如下:

// 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事件處理器里,從GridView控件的DataKeys集合里獲取被刪記錄的CategoryID值,而在這里,我們通過(guò)e.Keys來(lái)訪問(wèn)DataKeys集合。接著,調(diào)用類CategoriesBLL的GetCategoryByCategoryID(categoryID)方法來(lái)返回被刪記錄的信息,若返回的BrochurePath值不為NULL,那么將其賦值給頁(yè)面參數(shù)deletedCategorysPdfPath,再在RowDeleted事件處理器里刪除文件。

  注意:在RowDeleting事件處理器里,我們沒(méi)有返回被刪記錄的BrochurePath信息,而是將BrochurePath添加到GridView的DataKeyNames屬性,再通過(guò)訪問(wèn)e.Keys來(lái)獲取該記錄的值。這樣做雖然稍微增大了GridView的視圖狀態(tài),但減少了必要的代碼,也省了一步訪問(wèn)數(shù)據(jù)庫(kù)。

  調(diào)用ObjectDataSource控件的delete命令后,緊接著發(fā)生GridView控件的RowDeleted事件,如果刪除過(guò)程沒(méi)有異常且deletedCategorysPdfPath值不為空,那就將對(duì)應(yīng)的PDF文件從文件系統(tǒng)刪除。我們注意到,代碼沒(méi)有刪除類的picture,那是因?yàn)閜icture數(shù)據(jù)是直接存儲(chǔ)在數(shù)據(jù)庫(kù)里的,當(dāng)刪除記錄時(shí)就一起刪除了。

  添加完上述2個(gè)事件處理器后,再次測(cè)試刪除。當(dāng)刪除某個(gè)類時(shí),其對(duì)應(yīng)的PDF文件也刪除了。

  下面我們深入研究添加更新功能以應(yīng)對(duì)類的brochure和picture.第6步探討更新brochure信息的技術(shù),第7章探討更新picture。

第6步:更新類的Brochure

  就像在教程16《概述插入、更新和刪除數(shù)據(jù)》里探討的一樣,如果GridView的數(shù)據(jù)源控件支持編輯,那么我們就可以啟用GridView控件的編輯功能。當(dāng)前,名為CategoriesDataSource的ObjectDataSource控件并不支持編輯,那讓我們添加吧。

  點(diǎn)擊ObjectDataSource控件的“設(shè)置數(shù)據(jù)源”鏈接,一直點(diǎn)到“定義數(shù)據(jù)方法”界面。由于在CategoriesBLL里對(duì)重載的UpdateCategory方法使用了DataObjectMethodAttribute屬性,UPDATE標(biāo)簽的下拉列表自動(dòng)的選擇了該方法,它包含4個(gè)輸入?yún)?shù)(不包含Picture)。我們選擇另一個(gè)包含5個(gè)輸入?yún)?shù)的重載的UpdateCategory方法。


圖9:設(shè)置ObjectDataSource控件使用包含Picture參數(shù)的UpdateCategory方法

  ObjectDataSource控件現(xiàn)在包含了UpdateMethod屬性以及相應(yīng)的UpdateParameters參數(shù)集。就像在第4步提到的一樣,當(dāng)使用設(shè)置向?qū)r(shí),Visual Studio會(huì)將ObjectDataSource控件的OldValuesParameterFormatString屬性設(shè)置為original_{0},這導(dǎo)致調(diào)用update和delete方法時(shí)出現(xiàn)問(wèn)題。因此,要么將該屬性清除,要么設(shè)該屬性為{0}。

完成后,ObjectDataSource控件的聲明代碼看起來(lái)應(yīng)該和下面的差不多:

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控件的智能標(biāo)簽里選“編輯”。這將設(shè)置CommandField的ShowEditButton屬性為true,結(jié)果是為每行添加一個(gè)Edit按鈕(當(dāng)記錄處于編輯狀態(tài)時(shí),將呈現(xiàn)為Update和Cancel按鈕)


圖10:?jiǎn)⒂肎ridView控件的編輯功能

  從瀏覽器查看該頁(yè)面,點(diǎn)某條記錄的Edit按鈕。CategoryName和Description列呈現(xiàn)為一個(gè)文本框。由于BrochurePath TemplateField沒(méi)有EditItemTemplate模板,所以它依舊呈現(xiàn)其ItemTemplate模板——一個(gè)指向brochure的鏈接。Picture列呈現(xiàn)為一個(gè)文本框,并且該P(yáng)icture ImageField的Text屬性被指派為DataImageUrlField值,在這里,即CategoryID.


圖11:BrochurePath列沒(méi)有編輯界面

定制BrochurePath編輯界面

我們可以為BrochurePath TemplateField創(chuàng)建一個(gè)編輯界面,我們可以選擇:

.維持原樣
.上傳新的brochure以作更新
.將brochure刪除(這樣一來(lái),類就沒(méi)有對(duì)應(yīng)的brochure了)

我們也應(yīng)該更新Picture ImageField的編輯界面,不過(guò)我們將放在第7步來(lái)討論。

  在GridView控件的智能標(biāo)簽里選擇“編輯模板”,再?gòu)南吕斜砝镞xBrochurePath TemplateField的EditItemTemplate模板。在模板里添加一個(gè)RadioButtonList Web控件,其ID為BrochureOptions;AutoPostBack屬性為true.再在屬性窗口里點(diǎn)Items屬性的橢圓型區(qū)域,進(jìn)入ListItem Collection Editor界面,分別添加值為1,2,3的選項(xiàng):

.Use current brochure
.Remove current brochure
.Upload new brochure

設(shè)第一個(gè)ListItem的Selected屬性為true.


圖12:為RadioButtonList控件添加3個(gè)ListItems

在RadioButtonList控件下面,添加一個(gè)FileUpload控件,ID為BrochureUpload,設(shè)其Visible屬性為false。


圖13:在EditItemTemplate模板里添加RadioButtonList和FileUpload控件

RadioButtonList控件為用戶提供了3個(gè)選擇,只有當(dāng)選擇“Upload new brochure”時(shí), FileUpload控件才會(huì)展現(xiàn)出來(lái)。為此,我們?yōu)镽adioButtonList控件的SelectedIndexChanged事件創(chuàng)建事件處理器,如下:

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控件同時(shí)出現(xiàn)在一個(gè)模板里,我們需要通過(guò)編程來(lái)訪問(wèn)這2個(gè)控件。在SelectedIndexChanged事件處理器里,我們通過(guò)輸入?yún)?shù)sender來(lái)引用RadioButtonList控件。為了獲取FileUpload控件,我們需要使用RadioButtonList的父控件(parent control),并使用FindControl("controlID")方法。一旦我們同時(shí)獲取了RadioButtonList和FileUpload控件時(shí),只要RadioButtonList控件的SelectedValue值等于3,即“Upload new brochure” ListItem的值時(shí),將FileUpload控件的Visible屬性設(shè)置為true 。

  添加完上述代碼后,花幾分鐘時(shí)間來(lái)測(cè)試編輯頁(yè)面。點(diǎn)擊某行的Edit按鈕,默認(rèn)是選中“Use current brochure”項(xiàng),改選另一項(xiàng),頁(yè)面產(chǎn)生回傳,如果是選擇第3項(xiàng),則FileUpload控件將會(huì)顯示出來(lái),否則處于隱身狀態(tài)。圖14顯示點(diǎn)擊Edit按鈕的情形,而圖15則是選擇“Upload new brochure”時(shí)的情形。


圖14:默認(rèn)選擇“Use current brochure”項(xiàng)


圖15:選擇“Upload new brochure”時(shí)FileUpload控件顯示出來(lái)

保存Brochure文件并更新BrochurePath列

當(dāng)點(diǎn)擊GridView控件的Update按鈕時(shí),觸發(fā)RowUpdating事件,調(diào)用ObjectDataSource控件的update命令,然后觸發(fā)GridView控件的RowUpdated事件。跟deleting流程類似,我們需要?jiǎng)?chuàng)建這些事件的處理器。在RowUpdating事件處理器里,我們需要根據(jù)RadioButtonList的SelectedValue值來(lái)判斷下一步怎么做。

.如果SelectedValue值為1,我們將保持rochurePath不變。所以我們將ObjectDataSource控件的brochurePath參數(shù)設(shè)置為當(dāng)前處于編輯狀態(tài)記錄的BrochurePath值,方法為e.NewValues["brochurePath"] = value.

.如果SelectedValue值為2,意味著將BrochurePath設(shè)為NULL。為此,我們需要將ObjectDataSource控件的brochurePath參數(shù)設(shè)為Nothing,結(jié)果就是在UPDATE命令里使用NULL。如果存在對(duì)應(yīng)的brochure文件,我們必須將其刪除,前提是沒(méi)有拋出任何的異常。

.如果SelectedValue值為3,我們必須確保用戶已經(jīng)上傳了一個(gè)PDF文件并將其保存在文件系統(tǒng),然后更新記錄的BrochurePath值。我們要先將被替換的前一個(gè)文件刪除掉,當(dāng)然前提是沒(méi)有引發(fā)異常。

在上一章里,當(dāng)在DetailsView控件里添加新記錄時(shí),觸發(fā)DetailsView控件的ItemInserting事件。在本章,當(dāng)RadioButtonList控件的SelectedValue為3時(shí)(即我們選擇Upload new brochure時(shí)),接下來(lái)要采取的步驟實(shí)際上與DetailsView控件的ItemInserting事件處理器實(shí)現(xiàn)的功能相似。根據(jù)實(shí)現(xiàn)的功能,我劃分為2個(gè)方法:

.ProcessBrochureUpload(FileUpload, out bool):它以一個(gè)FileUpload控件實(shí)例為輸入?yún)?shù),結(jié)果為一個(gè)布爾值(Boolean)。根據(jù)該布爾值判斷是否繼續(xù)更新或刪除操作,抑或取消操作。如果存在上傳文件該方法就返回其路徑,反之返回null。

.DeleteRememberedBrochurePath:如果頁(yè)面變量deletedCategorysPdfPath不為null,則刪除該參數(shù)指定的文件。

下面是上述2種方法的代碼。注意ProcessBrochureUpload方法和DetailsView控件的ItemInserting事件處理器有某些相似性,在本章,我們更新DetailsView控件的事件處理器以使用這些新方法。下載本章的代碼,查看我們對(duì)DetailsView控件的事件處理器所做的修改。

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事件處理器里使用上面2個(gè)方法,如下:

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事件處理器是如何根據(jù)SelectedValue值的不同而使用一系列的條件語(yǔ)句來(lái)實(shí)現(xiàn)相應(yīng)的功能。

使用上面的代碼,我們就可以編輯一個(gè)類了,使用其當(dāng)前的brochure,或不使用brochure,再或者使用一個(gè)新的brochure。在RowUpdating和RowUpdated事件處理器里設(shè)置斷點(diǎn)(breakpoints)吧,以便更好的理解處理流程。

第7步:上傳新圖片

  Picture ImageField的編輯界面呈現(xiàn)為一個(gè)文本框,里面顯示的是DataImageUrlField 屬性的值。在編輯流程,GridView控件向ObjectDataSource傳入一個(gè)參數(shù),參數(shù)名為ImageField的DataImageUrlField屬性;參數(shù)值為在編輯界面輸入文本框里的值。當(dāng)圖片是存儲(chǔ)在文件系統(tǒng),且DataImageUrlField屬性包含的是訪問(wèn)該圖片的完整URL時(shí),這樣做是恰當(dāng)?shù)?。在這種情況下,在編輯界面里,文本框?qū)?huì)顯示圖片的URL。毫無(wú)疑問(wèn),默認(rèn)的界面不允許用戶上傳新的圖片,但用戶卻可以修改圖片的URL值。不過(guò),在本教程不會(huì)出現(xiàn)這種情況,因?yàn)镻icture數(shù)據(jù)是直接存儲(chǔ)在數(shù)據(jù)庫(kù)的,且DataImageUrlField屬性被設(shè)為CategoryID值。

  為了更好的理解在本教程里編輯某行的ImageField時(shí)將會(huì)發(fā)生上什么,我們做如下假設(shè):用戶編輯一個(gè)CategoryID值為10行,Picture ImageField呈現(xiàn)為一個(gè)文本框,顯示10,假設(shè)用戶將其改為50后點(diǎn)Update按鈕,頁(yè)面回傳,GridView控件最初產(chǎn)生一個(gè)名為CategoryID,值為50的參數(shù)。在GridView傳遞此參數(shù)(連同參數(shù)CategoryName和參數(shù)Description一起)以前,對(duì)DataKeys集添加值。因此,將當(dāng)前行的CategoryID值10,覆蓋掉。簡(jiǎn)言之,ImageField的編輯界面沒(méi)有對(duì)本章教程的編輯流程產(chǎn)生任何影響,因?yàn)镮mageField的DataImageUrlField屬性和DataKey值都是同一個(gè)值。

  當(dāng)圖片存儲(chǔ)在數(shù)據(jù)庫(kù)時(shí),ImageField將其顯示出來(lái)也很容易。不過(guò)在編輯界面里我們不需要使用文本框,而提供一個(gè)FileUpload控件供最終用戶更改圖片時(shí)使用。與BrochurePath不同,我們不允許類的圖片為空——用戶要么提供新圖片要么使用當(dāng)前的圖片。

  為定制ImageField的編輯界面,我們需要將其轉(zhuǎn)化為一個(gè)TemplateField。在GridView控件的智能標(biāo)簽里點(diǎn)擊“編輯列”,進(jìn)入后選中ImageField,再點(diǎn)擊“Convert this field into a TemplateField”鏈接。


圖16:將ImageField轉(zhuǎn)換為TemplateField

  轉(zhuǎn)換后的TemplateField由2個(gè)模版構(gòu)成。就像下面的聲明代碼顯示的那樣,ItemTemplate模版包含一個(gè)Image Web控件,其ImageUrl屬性由一個(gè)數(shù)據(jù)綁定語(yǔ)法指定,該數(shù)據(jù)綁定語(yǔ)法基于ImageField的DataImageUrlField和 DataImageUrlFormatString屬性。而EditItemTemplate模版則包含一個(gè)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模版以包含一個(gè)FileUpload控件。從GridView控件的智能標(biāo)簽點(diǎn)“編輯模版”,再在下拉列表選擇Picture TemplateField的EditItemTemplate模版。在模版里你會(huì)看見(jiàn)一個(gè)TextBox,將其刪除。從工具箱里拖一個(gè)FileUpload控件到頁(yè)面,設(shè)其ID為PictureUpload。同時(shí)在模版里添加如下的文本:“To change the category's picture, specify a new picture. To keep the category's picture the same, leave the field empty”。


圖17:在EditItemTemplate模版里添加一個(gè)FileUpload控件


完成定制該編輯界面后,在瀏覽器里查看。在只讀模式里,類的圖片和以前沒(méi)什么兩樣,當(dāng)點(diǎn)擊Edit按鈕時(shí),picture列將呈現(xiàn)一段文本和一個(gè)FileUpload控件。


圖18:編輯界面包含一個(gè)FileUpload控件

  記得我們?cè)O(shè)置ObjectDataSource控件調(diào)用CategoriesBLL的UpdateCategory方法,該方法的一個(gè)輸入?yún)?shù)為數(shù)組,用于處理圖片的數(shù)據(jù)。如果該數(shù)組為null值,則調(diào)用另一個(gè)重載的UpdateCategory方法,該重載的UpdateCategory方法的UPDATE SQL語(yǔ)句不會(huì)更改Picture列,因此類的圖片不會(huì)由任何變化。在GridView控件的RowUpdating事件處理器里,我們編程訪問(wèn)名為PictureUpload的FileUpload控件,判斷是否上傳了文件。如果沒(méi)有文件上傳,我們將不會(huì)為參數(shù)picture指定值;反之,如果上傳了文件,我們將確保其為JPG格式的文件,并通過(guò)參數(shù)picture將其傳給ObjectDataSource控件。

  就像第6步里的代碼一樣,我們此時(shí)將要用到的絕大多數(shù)的代碼已經(jīng)存在于DetailsView控件的ItemInserting事件處理器里了?,F(xiàn)在我們創(chuàng)建一個(gè)新的方法ValidPictureUpload,并更新ItemInserting事件處理器以使用該方法。

  在GridView控件的RowUpdating事件處理器的開(kāi)頭部分添加如下的代碼,這很重要,因?yàn)槲覀儾幌M麑⒁粋€(gè)不符合條件的上傳文件存儲(chǔ)在文件系統(tǒng)。

// 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)方法只有一個(gè)FileUpload控件類型的輸入?yún)?shù),通過(guò)檢查上傳文件的擴(kuò)展符以確保上傳的文件為JPG格式。只有當(dāng)上傳了文件時(shí)才會(huì)調(diào)用該方法;如果沒(méi)有文件上傳,參數(shù)picture就只能使用其默認(rèn)值—null。如果上傳了圖片,且ValidPictureUpload方法返回值true,將用圖片的二進(jìn)制數(shù)據(jù)對(duì)參數(shù)picture賦值。如果ValidPictureUpload方法返回值false,則取消更新,并退出事件處理器。

ValidPictureUpload(FileUpload)方法的代碼如下:

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步:將原始幾個(gè)類的圖片替換為JPG格式

回想起最開(kāi)始的那8個(gè)類的圖片為位圖文件其包含一個(gè)OLE報(bào)頭?,F(xiàn)在我們添加了新功能以編輯現(xiàn)有記錄的圖片,花幾分鐘將這些位圖文件替換為JPG文件。如果你想使當(dāng)前類的圖片不變,你可以通過(guò)下面的布置將其轉(zhuǎn)換為JPG格式:

1.將這些位圖保存在硬盤。在瀏覽器里訪問(wèn)UpdatingAndDeleting.aspx頁(yè)面,對(duì)這8個(gè)類的圖片,點(diǎn)右鍵,選則保存圖片。

2.在一個(gè)圖片編輯器(比如Microsoft Paint)軟件里打開(kāi)圖片。

3.將圖片保存為JPG格式

4.在編輯界面里,用JPG圖片更新類的picture

完成更新并上傳JPG圖片之后,圖片不會(huì)呈現(xiàn)在瀏覽器里,原因是DisplayCategoryPicture.aspx將嘗試對(duì)最開(kāi)始8個(gè)類的圖片剝離OLE報(bào)頭。怎樣修正呢?我們將剝離OLE報(bào)頭的代碼移除。這樣,DisplayCategoryPicture.aspx頁(yè)面的Page_Load事件處理器的代碼如下:

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);
}

注意:UpdatingAndDeleting.aspx頁(yè)面的編輯和添加界面要稍微復(fù)雜一點(diǎn)。DetailsView和GridView控件里的CategoryName和Description  BoundFields應(yīng)當(dāng)轉(zhuǎn)換成TemplateFields;另外由于CategoryName不能為NULL值,應(yīng)對(duì)其添加一個(gè)RequiredFieldValidator控件。此外,Description應(yīng)修改為允許換行的的文本框(multi-line TextBox),我將這些留給讀者作為練習(xí)。

總結(jié):
  本篇為處理二進(jìn)制數(shù)據(jù)的完結(jié)篇,在本章以及前3章我們考察了如何將二進(jìn)制數(shù)據(jù)存放在文件系統(tǒng)或直接存儲(chǔ)在數(shù)據(jù)庫(kù)里。用戶在硬盤里選擇一個(gè)文件并將其上傳到服務(wù)器,再存放在文件系統(tǒng)或數(shù)據(jù)庫(kù)。ASP.NET 2.0的FileUpload控件提供了上傳的界面。然而,就像在教程《使用FileUpload上傳文件》里提到的那樣,F(xiàn)ileUpload控件控件只適合于上傳小于1MB的文件。我們也探討了如何編輯和刪除當(dāng)前記錄的二進(jìn)制數(shù)據(jù)。

在接下來(lái)的一系列教程里,我們探討各種緩存技術(shù)。使用緩存可以提升應(yīng)用程序的整體性能。

  祝編程快樂(lè)!

作者簡(jiǎn)介

  本系列教程作者 Scott Mitchell,著有六本ASP/ASP.NET方面的書(shū),是4GuysFromRolla.com的創(chuàng)始人,自1998年以來(lái)一直應(yīng)用 微軟Web技術(shù)。大家可以點(diǎn)擊查看全部教程《[翻譯]Scott Mitchell 的ASP.NET 2.0數(shù)據(jù)教程》,希望對(duì)大家的學(xué)習(xí)ASP.NET有所幫助。

您可能感興趣的文章:
  • 在ASP.NET 2.0中操作數(shù)據(jù)之五十六:使用ObjectDataSource緩存數(shù)據(jù)
  • 在ASP.NET 2.0中操作數(shù)據(jù)之五十七:在分層架構(gòu)中緩存數(shù)據(jù)
  • 在ASP.NET 2.0中操作數(shù)據(jù)之五十八:在程序啟動(dòng)階段緩存數(shù)據(jù)
  • 在ASP.NET 2.0中操作數(shù)據(jù)之五十九:使用SQL緩存依賴項(xiàng)SqlCacheDependency
  • 在ASP.NET 2.0中操作數(shù)據(jù)之六十:創(chuàng)建一個(gè)自定義的Database-Driven Site Map Provider
  • 在ASP.NET 2.0中操作數(shù)據(jù)之六十一:在事務(wù)里對(duì)數(shù)據(jù)庫(kù)修改進(jìn)行封裝
  • 在ASP.NET 2.0中操作數(shù)據(jù)之六十二:GridView批量更新數(shù)據(jù)
  • 在ASP.NET 2.0中操作數(shù)據(jù)之六十三:GridView實(shí)現(xiàn)批量刪除數(shù)據(jù)
  • 在ASP.NET 2.0中操作數(shù)據(jù)之六十四:GridView批量添加數(shù)據(jù)
  • 在ASP.NET 2.0中操作數(shù)據(jù)之六十五:在TableAdapters中創(chuàng)建新的存儲(chǔ)過(guò)程

標(biāo)簽:聊城 清遠(yuǎn) 甘肅 慶陽(yáng) 臨夏 中衛(wèi) 海西

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《在ASP.NET 2.0中操作數(shù)據(jù)之五十五:編輯和刪除現(xiàn)有的二進(jìn)制數(shù)據(jù)》,本文關(guān)鍵詞  在,ASP.NET,2.0,中,操作,數(shù)據(jù),;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《在ASP.NET 2.0中操作數(shù)據(jù)之五十五:編輯和刪除現(xiàn)有的二進(jìn)制數(shù)據(jù)》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于在ASP.NET 2.0中操作數(shù)據(jù)之五十五:編輯和刪除現(xiàn)有的二進(jìn)制數(shù)據(jù)的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    新野县| 万山特区| 乳源| 南丹县| 岑溪市| 滨海县| 西城区| 类乌齐县| 江孜县| 开封市| 元江| 凤阳县| 蓝田县| 普兰县| 苍南县| 平远县| 定边县| 崇明县| 宁河县| 柳林县| 鹤岗市| 竹溪县| 治县。| 云阳县| 双流县| 临泽县| 屏东县| 嘉义市| 麦盖提县| 秦安县| 巴南区| 五指山市| 安福县| 陈巴尔虎旗| 涿鹿县| 宜州市| 博客| 通榆县| 庆云县| 南岸区| 沈丘县|