Friday, April 20, 2007 - 文章

KB-小心Web Deployment Project的Class名稱重覆陷阱

昨天在Web上遇到以下的錯誤:

Event message: A compilation error has occurred. Exception type: HttpCompileException

Exception message: c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\MyApp\868561b0\194f3b40\App_Web_datawriter.aspx.d119a6e4.lfyr98zw.0.cs(106): error CS0433: The type 'Darkthread.Common.DataWriter' exists in both 'c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\itrq\868561b0\194f3b40\assembly\dl3\f1f147bd\ee5abd33_e17cc701\DRARWeb.Folder1.DLL' and 'c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\itrq\868561b0\194f3b40\assembly\dl3\28ccdec0\9f367833_e17cc701\DARKWeb.Folder2.DLL'

錯誤發生的時機是使用者呼叫Folder1\DataWriter.aspx時,系統傳回了物件名稱重覆的問題。追了一下,原來問題是這麼發生的:

在ASP.NET 1.1時代,我寫了一個DataWriter網頁,在過去的專案模式下,元件預設會用Folder名稱宣告成結構化的命名空間(Namespace),它被放在Darkthread Web Application的Common目錄下,因此理所當然Class Name就是Darkthread.Common.DataWriter。

在一個ASP.NET 2.0專案中,我將這個1.1時代的DataWriter.aspx搬到Folder1與Folder2底下改寫出兩個網頁。VS 2005在新增網頁時預設會將兩個網頁的CodeBeside Class命名為Folder1_DataWriter及Folder2_DataWriter,所以不會有名稱重覆的問題。而由外面Copy過來的ASPX,VS 2005則保留了原來的Class Name。此時就出現危機了: Folder1/DataWriter.aspx與Folder2/DataWriter.aspx的Code Beside Class,名字都叫做Darkthread.Common.DataWriter。

不過炸彈並不會馬上爆炸,在Debug與測試階段,Web Site Project的.cs會獨立即時編譯,因此不會有撞名的情況。等到我們使用Web Deployment Project並選擇每顆Folder Build成一顆DLL時,由於兩個同名Class分屬不同的DLL(Folder1.dll及Folder2.dll),所以編譯及部署時也不會發生錯誤。真正炸彈引爆的時機是當我們呼叫Folder1/DataWriter.aspx或Folder2/DataWriter.aspx時,由於BIN下有兩個DLL包含相同名稱的Class,ASP.NET不知要載入哪一顆,就導致了前述的HttpComplieException。

【結論】
由其他地方拖入ASPX檔案時,記得要更改Class Name以避免重覆。至於要怎麼檢查Web Deployment Project中有沒有重覆的類別名稱? 我想到最簡單的方法是先將Web Deployment Project的設定改成Build成單一DLL(如下圖),此時如果Build時出現"aspnet_merge.exe" exited with code 1.的錯誤訊息,多半就代表Project中有Class名稱重覆的問題,去找出來吧! (如果專案很大,尋人的難度直追阿亮的超級任務,多半得靠始作俑者的記憶追查,不知有沒有其他更簡便的方法? 還是該寫台潛盾機?)

補充: 關於Web Deployment Project的使用說明,可以參考文章-ASP.NET 2.0專案部署問題研究

AJAX: Trigger UpdatePanel With Javascript

有時候,我們需要透過JavaScript觸發UpdatePanel的更新動作。我個人偏好在UpdatePanel中放一顆按鈕,然後由Javascript來"按"下這顆按鈕。如此,更新邏輯就可大大方方地寫在Button的Server-Side Click事件中,跟標準的ASP.NET AJAX設計方式相同,程式區塊一目瞭然,對寫程式跟看程式的人來說,都很直覺易懂。

例如以下的範例:

<form id="form1" runat="server">
<div>
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <asp:Label ID="Label2" runat="server" Text="Label"></asp:Label><br />
    <a href="BLOCKED SCRIPT$get('Button1').click();void(0);">
    Trigger UpdatePanel With JavaScript&nbsp;</a><br />
    <asp:UpdatePanel ID="UpdatePanel1" runat="server">
        <ContentTemplate>
            <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
            <br />
            <asp:Button ID="Button1" runat="server" Text="Button" 
            OnClick="Button1_Click" />
        </ContentTemplate>
    </asp:UpdatePanel>
</div>
</form>

Server-Side Code如下:

protected void Page_Load(object sender, EventArgs e)
{
    Label2.Text = DateTime.Now.ToString("HH:mm:ss");
}
protected void Button1_Click(object sender, EventArgs e)
{
    Label1.Text = DateTime.Now.ToString("HH:mm:ss");
}

UpdatePanel上方有個Label2, UpdatePanel中則有Label1及Button1,按下Button1就可觸發UpdatePanel的動態更新Label1的時間顯示,形成Label1.Text!=Label2.Text的效果當作動態更新的驗證。Javascript觸發Button1按下動作,則用$get("Button1").click()的寫法完成。

用IE開啟,測試OK! 一切看來都很順利,不是嗎?

美好的假像可以維持到你使用FireFox測試為止(當然你也可以選擇當鴕鳥,禁止User使用IE以外的瀏覽器)。在FireFox中,$get("Button1").click()會觸發POSTBACK~~~ AJAX破功了!

依據這串討論文章,似乎指向FireFox並未依W3C的規範實作click()行為。不過,不Work就是不Work,誰對誰錯又如何? 很幸運地,討論串中就有解決方案,我們可以利用UseSubmitBehavior="false"改變Button的HTML呈現:

由原來的
<input type="submit" name="Button1" value="Button" id="Button1" />
變成以下形式
<input type="button" name="Button1" value="Button" 
onclick="BLOCKED SCRIPT__doPostBack('Button1','')" id="Button1" />

搞定收工!

搜尋

Go

<April 2007>
SunMonTueWedThuFriSat
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345
 
RSS
【工商服務】
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


BlogLook Score and Rank

Syndication