2011/4/28

[KB] SVN 出現 『有缺陷的檔案』時的處理方式

這兩天在使用 Subversion 控管中某個專案的 "Show Log" 功能時,每每出現要我選擇是否使用離線查看Log的功能,如果一直選擇否的話,最後就跳出一個『有缺陷的檔案』的錯誤訊息,當時直覺認為可能是自已的專案目錄有什麼問題而造成,因為Update 和 commit 是正常的,由於正在趕著把某項功能做出來,所以暫時沒有花時間去解決它。沒多久,其他同事也發現Check Out整個專案的目錄也無法成功,最後出現一樣的錯誤訊息『有缺陷的檔案』。由於訊息並不明確,所以問題為何實在令人費解。 上網搜尋了一下『有缺陷的檔案』,發現有相關訊息的文章實在少得可憐,而且似乎都沒有較好的解決方式。如果直接還原備份的Subversion檔案,這樣一定是可以解決的(前提是必需先知道從哪天開始就出現這樣的問題),且必需冒著遺失這幾天Commit版本的風險,所以當下並不是最好的解決方式。既然中文訊息找不到,那改成搜尋英文的訊息看看呢?前題是得先知道對應的英文訊息是什麼(通常這種程式訊息,直接用翻譯軟體是找不出正確的對應訊息的,千萬別犯傻了!);所幸找到一段svn的程式碼如下:

 #: include/svn_error_codes.h:951
msgid "Malformed file"
msgstr "有缺陷的檔案"
網址:http://svn.apache.org/repos/asf/subversion/branches/artem-soc-work/subversion/po/zh_TW.po
由此可以得知對應的英文訊息為『Malformed file』,上網再搜尋一下,馬上發現有許多的文章,看了幾篇文章以後,發現錯誤的問題可能在於某個版本Commit上去的時候出現了問題,導致一遇到該版本時,SVN就會回出這樣的錯誤訊息,不過因為事情已經發生了幾天,中間大概也commit了上百個版本,所以實在不知道問題在哪裡。後來找到StackOverflow中也有人提出這樣的問題,原來svnadmin 工具有個Dump的功能,可以藉此跑過所有的版本,來確認問題是出在哪一版本上,用法如下:
 
svnadmin dump /SVN目錄/MyProject > nul 

這樣就會依序Dump所有的版本號碼出來,如果遇到錯誤的版本,就會跳出錯誤訊息,這麼一來要解決問題就不是難事了(看了幾篇討論的結果,也知道File Base的 Repository 能夠針對該版本進行修改(最好是用前一版覆蓋錯誤的版本,以免改錯)。 Dump 的結果,錯誤發生在10號版本,到SVN Server的目錄下(\SVN目錄\MyProject\db\revprops\)一查,果然該版本內容都是NULL Code,顯然檔案內容不正確(經過確認,在Client端,如果使用離線瀏覽Log時,最早的版本也停在11)刪掉該版本檔案後,用前一版9複製成10以後,問題解決,免除掉一段可能遺失的回憶修改記錄的感覺真好!
特別提醒一下,由於操作SVN檔案事關重大,為免發生不可回復的問題,務必事先備份SVN檔案再進行俢復的操作。

2011/2/24

[Fix] 在 Chrome/Firefox 開啟 ASP.NET Development server 下網站速度緩慢問題

已經忘了大約有多久的時間,可能是在升級到win7當作開發環境以後,就遇到了使用Chrome/Firefox 來開啟開發中的網站載入網頁速度非常緩慢的問題;開啟或重新整理一個網頁的時間絕對超過10秒鐘以上,加上網頁上載入的javascript檔案非常多,所以之前常常在Debug時直接改用IE來作業,但是如果一遇到Bug是發生在非IE瀏覽器上時,就非常頭痛了。

之前遇到這樣的情況是,使用fiddler來觀察時,發現只要網頁一開,同時載入5個資源(js,css or image)時,browser就會卡住,所以當時朝著加大max connection數量的方向在想,但是由於在趕案子(好吧,我承認這只是一個藉口..現在看來只是一個不要先入為主的認定,再加上花5分鐘google一下就可以解決的事),所以一直沒能解決這件事。

今天因為在開發新的網站,又遇到了一樣的情況;這回真的忍不住了,直覺的打了幾個keyword搜尋一下,沒想到困擾已久的問題就迎刃而解,前後絕對花不到5分鐘..Orz

原來問題出在IPv6的問題造成DNS解析速度緩慢,有以下3種解決方式

  • 修改hots,將localhost指定為127.0.0.1
  • 如果使用Firefox,關掉它的IPv6設定,Chrome的話似乎還沒有解法
  • 關掉系統的IPv6功能


在以不改變系統設定的情況下,我選擇修改hosts來處理掉這件事,相關文章可以參考下列網址:
ASP.NET Development server slow on Windows Vista/7 with Firefox or Chrome
Fixing Firefox Slowness with localhost on Vista (or XP with IPv6)

2011/2/16

[CODE] 解決反序列化時 『__type』 造成的錯誤訊息

這兩天在工作上遇到一個需求,要將原來A網站上用Ajax-Enabled WCF做出來的Ajax查詢ID的功能提供給Server上的B網站使用。本來這樣的需求可以透過直接在B網站上呼叫A網站的WCF服務來達成,但是考慮一旦這樣做下來,後續2個網站將必需放在同一台Server上,且在開發時期得另外架一個網站提供這樣的服務,並不便宜開發及除錯。於是改為在B網站上,直接透過在B網站的查詢字串在Server端以Post方式直接連到A網站的WCF服務來取得資料,而不在Client端完成(不過這樣相對的也有它的成本需要考量,必需自行取捨)。

這樣的方式之前在其他Case也有做過,不過當時A網站並不是直接透過WCF的做法來提供Ajax的查詢,而是自行寫一隻.AXD(Http Handler)的方式來完成。本以為類似的模式只要依樣畫葫蘆就可以完成,但卻在將WCF轉出的JSON Data再轉回B網站程式中的物件時出現了錯誤『System.ArgumentNullException: 值不能為 null。參數名稱: type』,遇到這樣的問題,先查了一下A網站出來的JSON,格式如下:『 {"__type":"SimpleType:#TestTypeResolver", "ID":"00001","Name":"Jacky"}"』,其中SimpleType 為A網站Server轉出來的物件type; 看到這裡,直覺認為是『__type』這個欄位在作崇,於是先從程式Replace掉再試一次看看...!!果不其然,只要拿掉了__type 欄位,錯誤訊息則不再出現。

得到了這樣的線索之後,問題就變得明確多了;拜了一下G大神之後,雖然沒有得到直接的解決方式,但卻找到一個解決問題的關鍵訊息...『JavaScriptTypeResolver 物件 』! 這個物件依名稱大概可以猜出七八成它的意義,主要是 JavaScriptSerializer 在遇到__type 這樣的meta資訊時,如何將物件Type轉成string 存到__type中,以及反解時將__type中的字串轉回type的功能。 有了這樣的訊息以後問題就容易解了,只要在建立 JavaScriptSerializer ,傳入自行實作的 JavaScriptTypeResolver ,就可以避開這樣的問題。

 //訂為private,以免外部程式誤用
 private class SimpleTypeResolver : JavaScriptTypeResolver
        {
            public override Type ResolveType(string id)
            {
                return typeof(SimpleType); //在這個例子中只做SimpleType的轉換,暫不考慮其他物
            }

            public override string ResolveTypeId(Type type)
            {
                throw new NotImplementedException(); //在這個例子中不做Serialize,所以不會被呼叫
            }
        }


public SimpleType Deserialize(string json){
     JavaScriptSerializer serial = new JavaScriptSerializer(new SimpleTypeResolver());
     var myType = serial.DeserializeObject(@"{""__type"":""SimpleType:#TestTypeResolver"",""ID"":""00001"",""Name"":""Jacky""}");
}
結論:
原本在使用 JavaScriptSerializer 並沒有留意到內部是透過 JavaScriptTypeResolver 來做 Reflection 的工作,所以遇到這樣情況時,可能一時無法理解原因為何,只能多花時間再去追可能造成問題的原因,若是在開發上能針對使用的物件多花一點心思,不只懂如何使用它,還要瞭解背後的原理或細節,這樣對於各種問題的狀況應該能夠更快的掌握才是!

2010/3/18

[Code] 建立Template式的 User Controls

在ASP.NET網站上使用UserControl是常見的一種使用方法,不過Clark以往比較少在User Control上使用到Template的方法。前陣子遇到一個需求:要將所有的Grid的下方加上不同的廣告;由於這些Grid統一都是使用了一個GridControl 的UserControl,而廣告加的位置正好就在Grid和分頁器的中間。一遇到這種情況時,在還不知道Template的作法時,直覺式的用法可能就是在UserControl中加一個MultiView的ServerControl,再利用外部帶參數進入UserControl來決定要顯示什麼廣告。這樣的方式的確可以達到功能,但是每次一改廣告,就要去改動這個GridControl,似乎不是長久之計,也不是良好的設計方法。

但是一想到Panel or Repeater這種Server Control明明可以使用<xxxTemplte/>的方法來從元件外部指定內部的Control 或HTML,那一定有種方法可以讓UserControl也達到這種功能囉!?沒錯!只要宣告 ITemplate 型別的屬性就可以讓UserControl多了指定內部Control的功能,再加上實作部份程式碼就能夠將外部指定的元素Render出來。

MyBox.ascx:

<%@ Control Language="C#" ClassName="MyBox" %>
<script runat="server">
   1:  
   2:     public String Title { get; set; }
   3:  
   4:     private ITemplate _contentTemplate = null;
   5:         /// <summary>
   6:     /// 宣告ContentTemplate,供外部指定(e.g.<ContentTemplate>xxxx</ContentTemplate>)
   7:     /// </summary>
   8:     [TemplateContainer(typeof(ControlContainer))]
   9:     [PersistenceMode(PersistenceMode.InnerProperty)]
  10:     [TemplateInstance(TemplateInstance.Single)]
  11:     /* TemplateInstance.Single 表示Template只實例化一次,
  12:      * 此時才可以用ID直接指定Template中的Control, 否則 Compile 會失敗 */
  13:     public ITemplate ContentTemplate
  14:     {
  15:         get
  16:         {
  17:             return _contentTemplate;
  18:         }
  19:         set
  20:         {
  21:             _contentTemplate = value;
  22:         }
  23:     }
  24:  
  25:     void Page_Init()
  26:     {
  27:         ///一旦外部有指定<ContentTemplate>內容則將內容實例化到Container中
  28:         ///再加入PlaceHolder中
  29:         if (_contentTemplate != null)
  30:         {
  31:             ControlContainer container = new ControlContainer();
  32:             _contentTemplate.InstantiateIn(container);
  33:             placeHolder1.Controls.Add(container);
  34:         }
  35:     }
  36:  
  37:     public class ControlContainer : Control, INamingContainer
  38:     {
  39:         public ControlContainer()
  40:         {
  41:         }
  42:     }
  43:  
</script>
<fieldset>
    <legend><%
   1: =Title 
%></legend>
    <asp:PlaceHolder ID="placeHolder1" runat="server" />
</fieldset>

Demo.aspx

<%@ Page Language="C#" %>
<%@ Register Src="~/MyBox.ascx" TagName="MyBox" TagPrefix="My" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<script runat="server">
   1:  
   2:     protected void OnbtnSearch_Click(object sender, EventArgs e)
   3:     {
   4:         txtSearch.Text = DateTime.Now.ToString();
   5:     }
</script>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>demo templated User Control</title>
    <style type="text/css">
        fieldset
        {
            border: 2px;
            border-style:solid;
            border-color: Black;
            padding: 2px;
            width: 250px;
        }
    </style>    
</head>
<body>
    <form id="form1" runat="server">
    <div class="leftBlock">
        <My:MyBox ID="mybox1" Title="SearchBox" runat="server">
            <ContentTemplate> 
                <!-- 測試Template中的Server Control是否可用 -->
                <asp:TextBox ID="txtSearch" runat="server" />
                <asp:Button ID="btnSearch" runat="server" Text="送出" OnClick="OnbtnSearch_Click" />
            </ContentTemplate>
        </My:MyBox>
    </div>
    </form>
</body>
</html>

執行結果:

template

當然,Template的用法不僅僅這麼簡單,當然也可以做到類似Repleater這樣的Server Control的功能。不過在這樣的實作方式中,已經可以解決Clark的問題了,如此一來只要在原來的GridControl上加上該功能,廣告就可以一率從外部指定,這樣一來元件就會更加具有彈性!

更多說明請參考MSDN 上的範例:How to: Create Templated ASP.NET User Controls

※此範例和MSDN上的範例在VS2008上均有無法在Design Mode中檢視UserControl的問題 (Error Creating Control – xxxx Type 'System.Web.UI.UserControl' does not have a public property named 'ContentTemplate' ),這個問題目前Clark還未找到解法,但是不影響執行的結果,若有網友有解決方式也請不吝賜教,造福大家一下~

2010/3/8

[Memo] 當Memcached 安裝成為 Windows Service 時如何指定參數

前陣子由於負責的網站Loading越來越重,加上前端Web Server Load Balance 有N台主機,在原本只有.NET Cache當作Cache的情況下,每個網頁仍需要Query N次資料才能夠全部進Cache。這個情況下只好求助Memcached這樣的Cache Server機製,好讓多台Web Server能夠共用Cache資料。達到『一人吃N人補』的境界。

Memcached 應該算是目前還滿流行的Cache Server,除了功能強大之外使用上也算簡單。安裝部份則只要下二行參數

memcached -d install
memcached -d start 

就能夠將memcached安裝並啟動成為Windows Service。不過有一個比較垢病的地方是無法指定服務啟動時的參數,導致memcached啟動時只能使用預設的最大64MB的記憶體。

就目前為止查到的解決這個問題的方法只有一種,進Regstry修改。

修改的路徑為:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\memcached 下的 ImagePath中的值,將原來『"N:xxxx\xxx\xxx\memcached.exe" -d runservice』後方下參數(e.g. –m1024),像這樣
『"N:xxxx\xxx\xxx\memcached.exe" -d runservice -m1024』。

memcached-1 修改完成後,重新啟動即可。

Memcached相關文章:

2010/3/6

[Tips] Runtime JavaScript and Css 壓縮方式

上回有提到過壓縮JavaScript來加快網頁載入速度的方式,而實際導入專案中使用的方式有許多種;一般來說我們在開發期間會以未壓縮的js&css檔案進行開發工作,以方便修改及Debug工作,等到了測試期間才會改用壓縮格式的檔案來測試。所以通常會採用的作法是在Debug和Release version時Include不同的檔案來達成,並透過類似MSBuild的方式在Build Release版本時將.js的檔案做壓縮,產出類似xxx-min.js這樣的壓縮檔案。

產出不同版本檔案的方式其實很直覺,但是Clark覺得這樣還不夠彈性,由於YUI Compressor for .Net版本也和YUI Compressor一樣支援css檔案的壓縮,自然就可以考慮如果是在輸出js及css檔案時即時做壓縮。不過前提是即時壓縮需要搭配一些機製來減少Server的負擔,例如Client Side 和 Server Side 的Cache。不然光是應付每個User Request的js檔案就讓你的Server吃不消了!

Clark是以Handler的方式實作即時壓縮的功能,只要頁面在include js時指定"Scripts/js.ashx?path=jquery-1.3.2.js",handler就會將該js檔讀入並使用YUI Compressor for .Net做壓縮,並吐回壓縮過的檔案到Client端。為了針對Debug and Release version而吐出不同的js,使用#if DEBUG來區分版本。

以下是程式碼:

js.ashx.cs:

/// <summary>
/// js.ashx.cs
/// </summary>
public class js : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        String filePath = context.Request["path"];
        String fullPath = Path.Combine(Path.GetDirectoryName(context.Request.PhysicalPath), filePath);
        String ext = Path.GetExtension(fullPath);
        if (String.IsNullOrEmpty(ext)  ext.ToLower() != ".js") return;
        String orgJS = "";
        //TODO: 將原始資料存入Cache,不必每次做載入
        using (FileStream fs = new FileStream(fullPath, FileMode.Open))
        {
            StreamReader reader = new StreamReader(fs);
            orgJS = reader.ReadToEnd();
            reader.Close();
        }
        //TODO: 將壓縮過的資料存入Cache,不必每次壓縮
#if DEBUG
        String outputJS = orgJS;
#else
        String outputJS = JavaScriptCompressor.Compress(orgJS);            
#endif
        //TODO: 判斷Browser是否支援GZip, 支援則將資料做GZip壓縮等..
        context.Response.ContentType = "text/javascript";
        context.Response.ContentEncoding = Encoding.UTF8;
        context.Response.ExpiresAbsolute = DateTime.Today.AddYears(1);
        context.Response.Write(outputJS);
    }
 
    public bool IsReusable
    {
        get
        {
            return true;
        }
    }
}

css.ashx.cs:

/// <summary>
/// css.ashx.cs
/// </summary>
public class css : IHttpHandler
{
 
    public void ProcessRequest(HttpContext context)
    {
        String filePath = context.Request["path"];
        String fullPath = Path.Combine(Path.GetDirectoryName(context.Request.PhysicalPath), filePath);
        String ext = Path.GetExtension(fullPath);
        if (String.IsNullOrEmpty(ext)  ext.ToLower() != ".css") return;
        String orgCss = "";
        //TODO: 將原始資料存入Cache,不必每次做載入
        using (FileStream fs = new FileStream(fullPath, FileMode.Open))
        {
            StreamReader reader = new StreamReader(fs);
            orgCss = reader.ReadToEnd();
            reader.Close();
        }
        //TODO: 將壓縮過的資料存入Cache,不必每次壓縮
#if DEBUG
        String outputCss = orgCss;
#else
        String outputCss = CssCompressor.Compress(orgCss);
#endif
        //TODO: 判斷Browser是否支援GZip, 支援則將資料做GZip壓縮等..
        context.Response.ContentType = "text/css";
        context.Response.ContentEncoding = Encoding.UTF8;
        context.Response.Write(outputCss);
    }
 
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebSite._Default"%>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Css & JavaScript Compressor Test</title>
    <!-- 借用ZenGarden的css sample. 所有要include的css檔都丟給 css.ashx處理 -->
    <link type="text/css" rel="stylesheet" href="Styles/css.ashx?path=zengarden-sample.css" />
    <!-- 所有include的js檔都丟給js.ashx處理 -->
    <script type="text/javascript" src="Scripts/js.ashx?path=jquery-1.3.2.js"></script>
   1:  
   2:     <script type="text/javascript">
   3:         $(function () {
   4:             //test for jquery
   5:             $('#pageHeader h1').fadeOut().fadeIn();
   6:         });
   7:     
</script>
</head>
<body>
    <form id="form1" runat="server">
    <div id="pageHeader">
        <h1>
            Css And Javascript Test
        </h1>
    </div>
    </form>
</body>
</html>

這樣一來,只要在include css & js時都照此方式來處理,那在Release Version時,Client就會自動改用壓縮過的js和css檔,而不需要另外做設定,也不會產出多個檔案減少檔案管理上的負擔。

※以上的範例僅為Demo壓縮的概念,若要架構在Production Site上還有一些功能需要去實作,以免造成Server Loading過重,以及沒有Client端Cache導致User Experience不佳等問題。

2010/3/3

[Memo] 『無法辨認的逸出序列』的另一種可能性

Compile不知道幾千次以上的Web Project突然蹦出一則錯誤 Unrecognized escape sequence (中文訊息:無法辨認的逸出序列)。打開出現錯誤的Souce Code,發現都是在中文字的後方才會出現這樣的問題;照MSDN上的解釋為"在字串中反斜線 (\) 的後方出現未預期的字元",不過光從Editor上看來該行根本沒有出現"\"的字元。

遇到這個問題,加上錯誤出現在中文字串後面,所以直覺認為是Encoding問題。在另存檔案看了一下檔案的Encoding方式為big5。由於之前沒有仔細在看是否所有檔案都是以utf-8編碼,所以猜想難道是什麼動作改了檔案的編碼方式?!先不管是否這樣,在將檔案改以utf-8格式存檔以後,該行的錯誤已經沒出現了,但是其他行陸續出現這個問題,顯示錯誤的檔案不止一個,這樣改下去不是辦法。

在看了一下Subversion中的Log之後,確認近期並沒有動到該檔案,本地的檔案時間也一樣停留在上次修改的時間,所以證實並非檔案編碼遭到修改而造成。

回頭想想這幾天修改了什麼設定,想到昨天為了測試某個Open Source Project而將根目錄改成該Project的目錄。但是心想遇到問題的Project是以虛擬目錄的方式存在,應該不會被影響才對!為了證實問題,將目錄改回原來的C:\inetpub\wwwroot下,重新Compile一次…鏘鏘!錯誤訊息不見了,Compile完成!

既然知道了問題,本著求知的精神將Web.config打開來看,發現有一段設定相當可疑..

<globalization
        requestEncoding="utf-8"
        responseEncoding="utf-8"
        fileEncoding="utf-8"
/>

fileEncoding 指定為utf-8,但是剛剛出現錯誤的Project中不少檔案是以big5編碼的,所以問題應該是虛擬目錄繼承了根目錄的設定,使用utf-8當作檔案的編碼格式。回頭將Project中的Web.config加了一行。

<!-- 如果是線上環境,千萬別直接照著修改,請先確認自已的encoding方式 -->
<globalization fileEncoding="" />

不指定fileEncoding的值,讓Compiler直接以檔案的BOM記錄的編碼方式來當作檔案編碼,重新Compile一次,問題解決!

結論:

  1. 虛擬目錄中如果未指定Encoding方式,會直接繼承根目錄"指定"的Encoding。
  2. 若未指定fileEncoding的值,Compiler會以檔案的BOM值來作為檔案Encoding格式。
  3. 光從錯誤訊息不一定能直接看出問題,但是可以懷疑造成問題的可能性!

.NET Reflector Pro 讓你遇到問題不再"不知所謂"

在Coding的不歸路上,隨著專案越來越大,歷史越來越久遠,用到的Library也越多,這個時候每當到問題時,常常發生的一個情況是在Debug到某個Library的method時由於無法再追下去(沒有Source Code或者就算有也不一定能確定版本是否正確)。這個時候常常需要用到.NET Reflector 加上"充份的相像力"來查看Library的method裡頭哪裡有可能會出問題。而越是分散式的寫法,在看Code時就更需要來回切換在不同的Class裡頭。

而這種刻苦的情節在Clark發現了.NET Reflector Pro提供了Debug Assembly這樣的好物之後將不再會發生了!幾乎所有的Library都可以被Decompile然後攤在陽光下,讓你 一步一步的追出問題的根源,或者瞭解它的實作方式,來提供做為問題分析的依據。

不過這套工作是需要付費的,請花195美元來享用它的功能。但大家可以先下載並執行最新版本的.NET Reflector 來將.NET Reflector Pro自動安裝到Visual Studio 2008 or Visual Sutdio 2010上做15天的試用。

Debug Code or Assembly 的方式官網已經說明得很清楚了,所以就不再多做說明,有圖有真相。

 ReflectorPro