最近在使用 Web Service 時發現了一個有趣的現象,只要按 F5 進偵錯模式時呼叫 Web Service,就常常會發生逾時的情況,但是不進偵錯模式的話又可以正常呼叫。

        雖然程式搬到 Server 上可以正常執行,但在開發時不能進偵錯模式實在很令人頭痛。為了解決這個問題,我把 Web Service 的 Request 交由 Fiddler 去發送,籍由這個工具來看 Request 的內容。程式碼大致上如下:

WebReference.Service1 service = new WebReference.Service1();
service.Proxy = new System.Net.WebProxy("127.0.0.1", 8888);

 

        接著呼叫 Web Service 提供的 WebMethod,Request 就會經由 Fiddler 發送出去。

 

        檢查了一下 Request 的內容,原來在偵錯模式下,HttpHeader 會多加下面這一段

fiddler-webservice-request

 

 

 

        Google之後,找到這篇,看來只要設定 Config,取消這一段自動加入的 Request Header 就行了。加入的 Config 區段如下:

<configuration> 
  <system.diagnostics> 
    <switches> 
       <add name="Remote.Disable" value="1" /> 
    </switches> 
  </system.diagnostics> 
</configuration> 

 

 

 

        可惜的是,手邊其實不止這一個 Web Service 站台,但只有呼叫這支會有這種問題,所以暫時也不知道確切發生的原因,看來只能等日後再慢慢研究、實驗了。

AnferneeHardaway 發表在 痞客邦 留言(0) 人氣()

        之前研究過 pdb 檔案的作用,不過沒找到可以讀取它的方法。這個問題一直擱在心頭上,總算這兩天有看到相關的文章。原來安裝 Visual Studio 的時候就會連同 DIA Dump 範例一起安裝進來。只要把這個範例的方案檔打開,編譯,接著就可以做 Dump 的動作囉!

範例的路徑是:
%ProgramFiles(x86)%\Microsoft Visual Studio 8\DIA SDK\Samples\DIA2Dump\dia2dump.sln

        編譯完之後,開啟命令提示字元,將目錄切到 Debug 底下(跟 Dia2Dump.exe 同一層),然後輸入 Dia2Dump.exe "pdb檔案的路徑 + 檔名",再按下 Enter 就可以看到 pdb 的內容了。

 

執行 Dia2Dump.exe 命令

dumppdb1

 

 

果然有行號及原始碼檔案的位置

dumppdb2

 

[參考資料]

http://msdn.microsoft.com/en-us/library/b5ke49f5(v=VS.80).aspx

http://msdn.microsoft.com/en-us/library/ms229861.aspx

http://sourceforge.net/projects/pdbdump/

http://blogs.msdn.com/jmstall/archive/2005/08/25/pdb2xml.aspx

AnferneeHardaway 發表在 痞客邦 留言(0) 人氣()

    今天寫 ASP.NET MVC 時,想抓目前的 RouteName 出來看,但 RouteCollection 好像在跟我唱反調一般,就是不讓我看到目前的 RouteName。

    心一橫,乾脆進 Source Code 去找看看,果然找到可以直接抓出來的辦法。原來 RouteCollection 是用一個 Dictionary 在儲存這些 RouteTable 的資訊。知道這點之後,要抓出來就不是什麼難事了。

public static string getRouteName() { 
    RouteData currentRoute = RouteTable.Routes.GetRouteData(
            new HttpContextWrapper(System.Web.HttpContext.Current));
 
 
    if (currentRoute == null) return null;
 
 
    FieldInfo field = typeof(RouteCollection).GetField(
        "_namedMap", 
        BindingFlags.Instance | BindingFlags.NonPublic); 
 
 
    Dictionary<string, RouteBase> namedMap =
        field.GetValue(RouteTable.Routes)
        as Dictionary<string, RouteBase>;
 
 
    return namedMap.First(
        route => { return route.Value == currentRoute.Route; }
    ).Key; 
}

 

    這樣一來,只要呼叫這個方法就可以知道目前所走的 RouteName 囉!

AnferneeHardaway 發表在 痞客邦 留言(0) 人氣()

    今天收到包裹了,這算是這兩個月來最開心的事了吧。

    順便跟資深 MVP 比爾叔道謝,拿到這個包裹有點手足無措,不知道下一步怎麼做,還好有這位熱心的前輩提點。

DSC_2129

AnferneeHardaway 發表在 痞客邦 留言(0) 人氣()

    這一段 SQL Statment 是為了套用javascript實做出 Menu 的巡覽列。之前都是在 Code Behind 的地方用遞迴去組 html,這次突然想用 SQL 的 FOR XML 來做看看,於是寫了這段出來。

    本來是想用 CTE 做看看,但仔細想想,CTE 似乎做不到這種感覺,所以最後還是用 FUNCTION 去做遞迴的效果。

    直接執行就可以看到效果了,有興趣的朋友不妨複製下去跑看看!

 

CREATE TABLE [dbo].[Pages](
    PageID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
    PageName NVARCHAR(50) NOT NULL DEFAULT '',
    [Action] VARCHAR(50) NOT NULL DEFAULT '',
    Controller VARCHAR(50) NOT NULL DEFAULT '',
    ParentID INT NOT NULL DEFAULT 0,
    Sort TINYINT NOT NULL DEFAULT 1
)
GO 
INSERT INTO dbo.Pages VALUES
('檔案','','',0,1),
('連接物件總管','Connect','File',1,1),
('中斷與物件總管的連接','Disconnect','File',1,2),
('新增','Create','File',1,3),
('專案','Project','File',4,1),
('使用目前的連接查詢','Search','File',4,2),
('編輯','','',0,2),
('復原','Undo','Edit',7,1),
('取消復原','Restore','Edit',7,2),
('檢視','','',0,3),
('物件總管','Explorer','View',10,1),
('查詢','Search','Query',0,4)
GO
CREATE FUNCTION [dbo].[f_GetMenuRecursive]
(
    @parentID INT 
)
RETURNS XML
AS
BEGIN 
    DECLARE @value XML 
    
    IF EXISTS(
        SELECT * 
        FROM dbo.Pages
        WHERE ParentID = @parentID
    )
    BEGIN 
        SELECT @value = (
            SELECT  'nfSubC nfSubS' AS '@class',
                    CONVERT(XML,(
                        SELECT  'nfItem' AS '@class',
                                ISNULL('/' + NULLIF(Controller,'') 
                                + '/' + 
                                NULLIF([Action],''),
                                'javascript:void(0)') 
                                AS 'a/@href',
                                'nfLink' AS 'a/@class',
                                PageName AS a,
                                dbo.f_GetMenuRecursive(PageID)
                        FROM dbo.Pages
                        WHERE ParentID = @parentID
                        ORDER BY Sort
                        FOR xml PATH('li')
                    ) )
            FOR XML PATH('ul')
        )
    END
    
    RETURN (@value)
END 
GO 
CREATE PROCEDURE [dbo].[s_GetMenu] 
 
AS
BEGIN
    SET NOCOUNT ON 
    
    SELECT  'nfItem' AS '@class',
            ISNULL('/' + NULLIF(Controller,'') + '/' + 
            NULLIF([Action],''),'javascript:void(0)') AS 'a/@href',
            'nfLink' AS 'a/@class',
            PageName AS a,
            dbo.f_GetMenuRecursive(PageID)
    FROM dbo.Pages
    WHERE ParentID = 0
    ORDER BY Sort
    FOR xml PATH('li'),ROOT('ul')
END 
GO 
 
/*
    測試 sp 的執行結果
    EXEC [s_GetMenu]
    
    刪除剛建立的資料庫物件
    DROP FUNCTION dbo.[f_GetMenuRecursive]
    DROP PROCEDURE [dbo].[s_GetMenu]
    DROP TABLE [dbo].[Pages]
*/

AnferneeHardaway 發表在 痞客邦 留言(0) 人氣()

    這個錯誤的長相是這樣的

ControlCollection.Add

    因為字太長,所以標題就刪掉了一些。

 

    原因是當開發人員在aspx中寫了inline code,例如<%=…%>之後,ControlCollection集合就會變成唯讀的,所以不能使用ControlCollection類別中的Add()方法 or AddAt()方法。以下是測試的程式碼: 

<form id="form1" runat="server">
<div id="div1" runat="server">
    <%="readonly" %>
</div>
<div id="div2" runat="server">
</div>
</form>

 


protected void Page_Load(object sender, EventArgs e)
{
    try {
        div1.Controls.Add(new Button());
    } catch { Response.Write("div1不能加入控制項"); }
 
    try {
        div2.Controls.Add(new Button());
    } catch { Response.Write("div2不能加入控制項"); }
}

測試結果:

    div1不能動態加控制項進去,但div2可以。

解決辦法:

    1.不要動態加控制項 (廢話)

    2.不要用<%…%> (還是廢話)

    3.用其它容器處理。這個方法說穿了也是1跟2的變形。以上面的情況來說,會變成下面這樣

 


<form id="form1" runat="server">
<div id="div1" runat="server">
    <span id="span1" runat="server">
        <%="readonly"%>
    </span>
</div>
<div id="div2" runat="server">
</div>
</form>

 


protected void Page_Load(object sender, EventArgs e)
{
    try {
        div1.Controls.Add(new Button());
    } catch { Response.Write("div1不能加入控制項"); }
 
    try {
        div2.Controls.Add(new Button());
    } catch { Response.Write("div2不能加入控制項"); }
}

如此一來,div1.Controls就不再是唯讀集合,所以可以任意的呼叫Controls.Add()方法。

AnferneeHardaway 發表在 痞客邦 留言(0) 人氣()

    網友問了個問題,我天真的認為是UrlEncode()就可以解決,直到心冷大指點後,才知道事情不是我想的那麼簡單。經過測試後,不管用什麼方法都沒辦法正確的用WebClient去抓資料,google了老半天也找不到有用的資訊。結果無意中發現這篇,看來跟這個名為punycode的東西有關,有了關鍵字,呵~~人生就變彩色的了,一下子就找到有用的文章。

    看起來是因為大多數的 DNS 伺服器僅支援 ASCII 字元,所以用中文字下去解析時就會發生錯誤,因此才出現punycode這個東西。以下是google到的解法

 

1.自己將中文轉成punycode

   1: public partial class Form1 : Form {
   2:     public Form1() {
   3:         InitializeComponent();
   4:     }
   5:  
   6:     private void Form1_Load(object sender, EventArgs e) {
   7:         string strPunycode = "http://xn--" + Punycode.encode("正負2度c") + ".tw/";
   8:         WebClient client = new WebClient();
   9:         string html = client.DownloadString(strPunycode);
  10:     }
  11: }
  12:  
  13: class Punycode {
  14:     /* Punycode parameters */
  15:     static int TMIN = 1;
  16:     static int TMAX = 26;
  17:     static int BASE = 36;
  18:     static int INITIAL_N = 128;
  19:     static int INITIAL_BIAS = 72;
  20:     static int DAMP = 700;
  21:     static int SKEW = 38;
  22:     static char DELIMITER = '-';
  23:     /**
  24:      * Punycodes a unicode string.
  25:      *
  26:      * @param input Unicode string.
  27:      * @return Punycoded string.
  28:      */
  29:     public static String encode(String input) {
  30:         int n = INITIAL_N;
  31:         int delta = 0;
  32:         int bias = INITIAL_BIAS;
  33:         StringBuilder output = new StringBuilder();
  34:         // Copy all basic code points to the output
  35:         int b = 0;
  36:         for (int i = 0; i < input.Length; i++) {
  37:             char c = input[i];
  38:             if (isBasic(c)) {
  39:                 output.Append(c);
  40:                 b++;
  41:             }
  42:         }
  43:         // Append delimiter
  44:         if (b > 0) {
  45:             output.Append(DELIMITER);
  46:         }
  47:         int h = b;
  48:         while (h < input.Length) {
  49:             int m = int.MaxValue;
  50:             // Find the minimum code point >= n
  51:             for (int i = 0; i < input.Length; i++) {
  52:                 int c = input[i];
  53:                 if (c >= n && c < m) {
  54:                     m = c;
  55:                 }
  56:             }
  57:             if (m - n > (int.MaxValue - delta) / (h + 1)) {
  58:                 throw new Exception("OVERFLOW");
  59:             }
  60:             delta = delta + (m - n) * (h + 1);
  61:             n = m;
  62:             for (int j = 0; j < input.Length; j++) {
  63:                 int c = input[j];
  64:                 if (c < n) {
  65:                     delta++;
  66:                     if (0 == delta) {
  67:                         throw new Exception("OVERFLOW");
  68:                     }
  69:                 }
  70:                 if (c == n) {
  71:                     int q = delta;
  72:                     for (int k = BASE; ; k += BASE) {
  73:                         int t;
  74:                         if (k <= bias) {
  75:                             t = TMIN;
  76:                         } else if (k >= bias + TMAX) {
  77:                             t = TMAX;
  78:                         } else {
  79:                             t = k - bias;
  80:                         }
  81:                         if (q < t) {
  82:                             break;
  83:                         }
  84:                         output.Append((char)digit2codepoint(t + (q - t) % (BASE - t)));
  85:                         q = (q - t) / (BASE - t);
  86:                     }
  87:                     output.Append((char)digit2codepoint(q));
  88:                     bias = adapt(delta, h + 1, h == b);
  89:                     delta = 0;
  90:                     h++;
  91:                 }
  92:             }
  93:             delta++;
  94:             n++;
  95:         }
  96:         return output.ToString();
  97:     }
  98:     /**
  99:      * Decode a punycoded string.
 100:      *
 101:      * @param input Punycode string
 102:      * @return Unicode string.
 103:      */
 104:     public static String decode(String input) {
 105:         int n = INITIAL_N;
 106:         int i = 0;
 107:         int bias = INITIAL_BIAS;
 108:         StringBuilder output = new StringBuilder();
 109:         int d = input.LastIndexOf(DELIMITER);
 110:         if (d > 0) {
 111:             for (int j = 0; j < d; j++) {
 112:                 char c = input[j];
 113:                 if (!isBasic(c)) {
 114:                     throw new Exception("BAD_INPUT");
 115:                 }
 116:                 output.Append(c);
 117:             }
 118:             d++;
 119:         } else {
 120:             d = 0;
 121:         }
 122:         while (d < input.Length) {
 123:             int oldi = i;
 124:             int w = 1;
 125:             for (int k = BASE; ; k += BASE) {
 126:                 if (d == input.Length) {
 127:                     throw new Exception("BAD_INPUT");
 128:                 }
 129:                 int c = input[d++];
 130:                 int digit = codepoint2digit(c);
 131:                 if (digit > (int.MaxValue - i) / w) {
 132:                     throw new Exception("OVERFLOW");
 133:                 }
 134:                 i = i + digit * w;
 135:                 int t;
 136:                 if (k <= bias) {
 137:                     t = TMIN;
 138:                 } else if (k >= bias + TMAX) {
 139:                     t = TMAX;
 140:                 } else {
 141:                     t = k - bias;
 142:                 }
 143:                 if (digit < t) {
 144:                     break;
 145:                 }
 146:                 w = w * (BASE - t);
 147:             }
 148:             bias = adapt(i - oldi, output.Length + 1, oldi == 0);
 149:             if (i / (output.Length + 1) > int.MaxValue - n) {
 150:                 throw new Exception("OVERFLOW");
 151:             }
 152:             n = n + i / (output.Length + 1);
 153:             i = i % (output.Length + 1);
 154:             output.Insert(i, (char)n);
 155:             i++;
 156:         }
 157:         return output.ToString();
 158:     }
 159:     public static int adapt(int delta, int numpoints, bool first) {
 160:         if (first) {
 161:             delta = delta / DAMP;
 162:         } else {
 163:             delta = delta / 2;
 164:         }
 165:         delta = delta + (delta / numpoints);
 166:         int k = 0;
 167:         while (delta > ((BASE - TMIN) * TMAX) / 2) {
 168:             delta = delta / (BASE - TMIN);
 169:             k = k + BASE;
 170:         }
 171:         return k + ((BASE - TMIN + 1) * delta) / (delta + SKEW);
 172:     }
 173:     public static bool isBasic(char c) {
 174:         return c < 0x80;
 175:     }
 176:     public static int digit2codepoint(int d) {
 177:         if (d < 26) {
 178:             // 0..25 : 'a'..'z'
 179:             return d + 'a';
 180:         } else if (d < 36) {
 181:             // 26..35 : '0'..'9';
 182:             return d - 26 + '0';
 183:         } else {
 184:             throw new Exception("BAD_INPUT");
 185:         }
 186:     }
 187:     public static int codepoint2digit(int c) {
 188:         if (c - '0' < 10) {
 189:             // '0'..'9' : 26..35
 190:             return c - '0' + 26;
 191:         } else if (c - 'a' < 26) {
 192:             // 'a'..'z' : 0..25
 193:             return c - 'a';
 194:         } else {
 195:             throw new Exception("BAD_INPUT");
 196:         }
 197:     }
 198: }

 

  2.直接使用config檔的設定

.在C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG\machine.config加入這一段


   1: <section name="uri" type="System.Configuration.UriSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

 

.在app.config中加入


   1: <configuration>
   2:   <uri>
   3:   <idn enabled="All" />
   4:   <iriParsing enabled="true" />
   5:   </uri>
   6: </configuration>

 

 

然後程式碼完全不用動


   1: public partial class Form1 : Form {
   2:     public Form1() {
   3:         InitializeComponent();
   4:     }
   5:  
   6:     private void Form1_Load(object sender, EventArgs e) {
   7:         string url = "http://正負2度c.tw/";
   8:         WebClient client = new WebClient();
   9:         string html = client.DownloadString(url);
  10:     }
  11: }

 

 

 

 

 

以上就屬第二種最方便,也說明了.NET真的是好用到一個不行啊~~

AnferneeHardaway 發表在 痞客邦 留言(0) 人氣()

    最近在設定 SQL 2008 的 Linked Server ,設定起來還蠻方便的,三兩下就可以用了,但是如果在 Trigger 中使用就會跳出錯誤:協力電腦異動管理員已經停用了對遠端/網路異動的支援。試了好久,終於搞定了這個問題,只要設定好 DTC 就行了,以下是這十幾個小時以來的心得:

1.開始 -- 控制台 -- 系統管理工具 -- 服務,啟動 Distributed Transaction Coordinator 服務。
2.開始 -- 控制台 -- 系統管理工具 -- 元件服務,展開元件服務 -- 電腦,在「我的電腦」上按右鍵 -- 內容,切到 MSDTC 標籤,按下「安全性設定」,依下圖設定

local-dtc

3.開啟 Windows防火牆,切到例外標籤 -- 新增程式,加入C:\WINNT\system32\msdtc.exe,接著再新增連接埠,開啟 Port 135 。
4.編輯regedt32.exe,我直接把需要編輯的部份寫下來,有需要的朋友可以直接複製以下文字,另存成 reg 檔案即可使用。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Rpc\Internet]
"Ports"=hex(7):35,00,30,00,30,00,30,00,2d,00,35,00,30,00,32,00,30,00,00,00,00,\
  00
"PortsInternetAvailable"="Y"
"UseInternetPorts"="Y"

設定大致上這樣就完成了,呼叫端跟被呼叫端都必須開啟 DTC 才行。

 

[參考資料]

http://dotnetmis91.blogspot.com/2008/09/windows-server-2008msdtc.html

http://support.microsoft.com/?kbid=873160

http://chattingprogram.blogspot.com/2008/04/sql-2005.html

http://blogs.msdn.com/mab/archive/2005/12/30/508273.aspx

http://technet.microsoft.com/zh-tw/magazine/2007.07.howitworks.aspx

http://support.microsoft.com/kb/250367/

http://itknowledgeexchange.techtarget.com/sql-server/how-to-configure-dtc-on-windows-2008/

AnferneeHardaway 發表在 痞客邦 留言(0) 人氣()

    最近才發現LIKE語法原來可以用中括號來表示任一種可能性,寫了近三年的T-SQL,到現在才知道有這種好方法。例如下面這張表:

CREATE TABLE #tmp(Names NVARCHAR(10))
GO
INSERT INTO #tmp VALUES(N'阿尼')
INSERT INTO #tmp VALUES(N'路飛')
INSERT INTO #tmp VALUES(N'喬巴')
INSERT INTO #tmp VALUES(N'索隆')
INSERT INTO #tmp VALUES(N'娜美')
INSERT INTO #tmp VALUES(N'香吉士')
INSERT INTO #tmp VALUES(N'烏索普')
INSERT INTO #tmp VALUES(N'羅賓')
INSERT INTO #tmp VALUES(N'佛朗基')
INSERT INTO #tmp VALUES(N'忘記名字的骨頭人')

   

如果我想找出第一個字是或者的話,我之前會這樣寫 


SELECT * FROM #tmp WHERE Names LIKE N'阿%' OR Names LIKE N'路%'

  

但後來發現可以這樣寫 


SELECT * FROM #tmp WHERE Names LIKE N'[阿路]%'

  

語法看起來更簡潔有力,帥!!

AnferneeHardaway 發表在 痞客邦 留言(0) 人氣()

    最近把一些 .NET2.0 的專案從 x86 的 Server 搬到 x64 的 Server 上,一直都相安無事,直到今天才發現使用 Oledb 讀取 Excel 的時候會跳出「'Microsoft.Jet.Oledb.4.0' 未登錄於本機電腦上」的錯誤。

    印象中在論壇上看到過幾篇「x64 + Oledb」的討論,再回頭去翻了一下,原來還蠻容易解決的,只要把 AppPool 設為 32 位元模式就行了。

x32mode

 

    有論壇真好~~~

AnferneeHardaway 發表在 痞客邦 留言(0) 人氣()

«12 3