October 2009 - 文章

CODE-將CSV檔案內容轉換成ADO.NET DataTable物件

寫給同事用的工具函數,隨手貼文分享:

/// <summary>
/// 將CSV內容字串轉成DataTable(全部欄位都視為string,有需要請自行轉換), 不支援""包夾格式
/// </summary>
/// <param name="csvContent">整個CSV的內容</param>
/// <param name="firstRowAsHeader">第一列是否為欄位名稱,若無則以C1, C2自動命名</param>
/// <returns></returns>
public static DataTable ConvertCSVtoDataTable(string csvContent, 
    bool firstRowAsHeader)
{
    DataTable t = new DataTable();
    using (StringReader sr = new StringReader(csvContent))
    {
        string line = null;
        bool colCreated = false;
        while ((line = sr.ReadLine()) != null)
        {
            string[] p = line.Split(',');
            if (!colCreated)
            {
                int idx = 1;
                foreach (string s in p)
                {
                    try
                    {
                        t.Columns.Add(
                            //第一欄是否有欄位名稱? 無則用C1, C2自動編號
                            firstRowAsHeader ? s : "C" + idx.ToString(),
                            typeof(string)
                            );
                    }
                    catch (Exception e1)
                    {
                        throw new ApplicationException(
                            "新增欄位失敗! 欄位名稱=" + s + "\n" + e1.ToString());
                    }
                    idx++;
                }
                colCreated = true;
                //首欄若為欄名,則不當資料處理
                if (firstRowAsHeader) continue;
            }
            try
            {
                t.Rows.Add(p);
            }
            catch (Exception e2)
            {
                throw new ApplicationException("資料匯入失敗!\n資料=" + line + 
                    "\n錯誤訊息:" + e2.ToString());
            }
        }
    }
    return t;
}
 
private static void test()
{
    string csv = "ID,Name,Score\n1,Jeffrey,200\n2,Darkthread,999";
    //Or you can read data from file
    //string csv = System.IO.File.ReadAllText("B:\\mydata.csv");
    DataTable t = ConvertCSVtoDataTable(csv, true);
    Debug.WriteLine("RowCount=" + t.Rows.Count);
    foreach (DataRow r in t.Rows)
        Debug.WriteLine(string.Format("{0}=>{1}", r["Name"], r["Score"]));
    csv = "1,Jeffrey,200\n2,Darkthread,999";
    t = ConvertCSVtoDataTable(csv, false);
    Debug.WriteLine("RowCount=" + t.Rows.Count);
    foreach (DataRow r in t.Rows)
        Debug.WriteLine(string.Format("{0}=>{1}", r["C2"], r["C3"]));
}
【茶包射手筆記】Office 2007人生變黑白

同事遇到的狀況,使用PCAnywhere後,再開啟Excel 2007/Word 2007/Outlook 2007等Office家族軟體,操作介面就會變成如下貧血的怪樣子,但重開機就恢復正常。

Google到相關的討論,雖然情境不完全相同,但裡面提到了VGA Driver的因素,感覺上PCAnywhere在使用遠端搖控時,確實會與顯示層有些關聯,有可能是其更動了某些顯示設定導致。

靈機一動,想到調整解析度或許能重置相關設定,Bingo!! 只要隨便變一下解析度,問題就消失了。(大家這才發現,Office這怪樣子,似乎就是在256色模式下的長相。)

徹底解決Vista/Windows 7半夜自動開機問題

之前談過Vista閙鬼半夜自動開機的問題,當時並未找到根本解決之道,只能在Sleep前留意將待安裝的更新先裝好避免。

前幾天網友pig在另一篇文章留言提到了電源管理的選項,我這才恍然大悟,原來該行為是可以被關閉的!

由【控制台/電源選項/變更計劃設定/變更進階電源設定】可以找到【睡眠/允許喚醒計時器】,將其停用,就可避免被Windows因為更新排程而被叫起床囉~~~

【提醒】停用此選項,將會延後Windows Update重大更新發佈後套用的時點。預設的設計雖然擾人,但比較安全,調整後就要自己多留意、更勤於安裝Windows更新哦! 另外,計時器喚醒還被應用在電視卡預約錄影等其他功能上,停用會使其失效,也要注意。

偵錯IIS7 ASP.NET程式時被強行關閉

使用VS2008偵錯IIS7上的Web Application,程式中斷後,在你Line-By-Line埋首抓蟲之際,如果沒法在90秒內克敵制勝,IIS就會不耐久候,跳出以下訊息,偵錯過程跟著就中斷了。

The web server process that was being debugged has been terminated by Internet Information Services (IIS).  This can be avoided by configuring Application Pool ping settings in IIS.  See help for further details.

所幸IIS7雖然耐性不好,卻提供了明確的解決方案指引。按下Help按鈕,就會被引導到詳細的操作說明:

英文版: http://msdn.microsoft.com/en-us/library/bb763108.aspx
中文版: http://msdn.microsoft.com/zh-tw/library/bb763108.aspx

原來是在Debug期間,IIS會被監控機制(WAS)判定為無回應,一到了逾時上限(預設90秒),Worker Process就會被重啟。官方文件已詳細說明將Ping停用的步驟(不過大家覺得第10-12點跑去設定Terminal Service是在搞笑,根本是多餘的,請忽略之),這裡只補一下操作示意圖:

【注意】Ping功能可用來監測IIS Application Pool的狀態,偵測到無回應時會自動重啟Worker Process,有助於改善IIS穩定性,在正式運作的環境中,建議開啟。

瀏覽器對不合法XML元素名稱的處理

因程式Bug搞出一個包含無效元素名稱的XML,發現IE的反應很有趣,XML物件的firstChild屬性會傳回null。

XML規格,XML元素名稱必須符合以下條件:

  • 包含字母、數字及其他字元
  • 不可以數字或標點符號起始
  • 開頭不可以是xml, XML或Xml
  • 不能包含空白

我寫了個範例挑戰了這個禁忌,順手試了一下不合法XML格式在各瀏覽器下的反應。

var xmlStr ="<data><0>A</0></data>";
var xd = null;
if (window.ActiveXObject) {
    xd = new ActiveXObject("Microsoft.XMLDOM");
    xd.async = false; xd.loadXML(xmlStr);
}
else if (typeof DOMParser != "undefined")
    xd = new DOMParser().parseFromString(xmlStr, "text/xml");
var elem = xd.firstChild;
if (elem == null) 
    alert("firstChild is null");
else
    alert(elem.xml || (new XMLSerializer()).serializeToString(elem));

【測試結果】

IE:
傳回null

Firefox:
得到<?xml-stylesheet href="chrome://global/locale/intl.css" type="text/css"?>

Chrome/Safari:
<data><parsererror xmlns="http://www.w3.org/1999/xhtml" style="display: block; white-space: pre; border: 2px solid #c77; padding: 0 1em 0 1em; margin: 1em; background-color: #fdd; color: black"><h3>This page contains the following errors:</h3><div style="font-family:monospace;font-size:12px">error on line 1 at column 8: StartTag: invalid element name
</div><h3>Below is a rendering of the page up to the first error.</h3></parsererror></data>

Opera:
<parsererror xmlns="http://www.mozilla.org/newlayout/xml/parsererror.xml">Error<sourcetext>Unknown source</sourcetext></parsererror>

本次最人性化回應獎,我決定頒給Chrome/Safari~~

Posted 23 October 2009 03:55 PMJeffrey | no comments
Filed under: ,
TextBox.ReadOnly、Attributes["readonly"]及Disabled

幫同事解答了關於TextBox.ReadOnly的特性,順便自己也溫習一下。

TextBox.ReadOnly = true;
TextBox.Attributes.Add("readonly", "readonly");
TextBox.Enabled = false;

以上三種寫法都可在網頁呈現唯讀的<INPUT>,但後端的行為有些差異。

我寫了一段示範,建立TextBox1, TextBox2, TextBox3分別用以上三種寫法設為前端唯讀。接著在網頁裡透過按鈕觸發Javascript將三者的值改成"ABC",按鈕Postback回後端,再檢查三者的Text屬性及Post回傳值。程式如下:

<%@ Page Language="C#" %>
<script runat="server">
    void Page_Load(object sender, EventArgs e) 
    {
        if (!IsPostBack)
        {
            TextBox1.ReadOnly = true;
            TextBox2.Attributes.Add("readonly", "readonly");
            TextBox3.Enabled = false;
            TextBox1.Text = TextBox2.Text = TextBox3.Text = "123";
        }
    }
    void Button1_Click(object sender, EventArgs e)
    {
        Label1.Text = string.Format(@"
Control: TextBox1={0}, TextBox2={1}, TextBox3={2}<br />
Request: TextBox1={3}, TextBox2={4}, TextBox3={5}",
            TextBox1.Text, TextBox2.Text, TextBox3.Text,
            Request["TextBox1"], Request["TextBox2"], Request["TextBox3"]);
    }
</script>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script src="js/jquery-1.3.2.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function() {
            $("#btnSetValue").click(function() {
                $("#TextBox1,#TextBox2,#TextBox3").val("ABC");
            });
        });
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
    <asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
    <asp:TextBox ID="TextBox3" runat="server"></asp:TextBox>
    <br />
    <input type="button" id="btnSetValue" value="Set Value" />
    <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Submit"/>
    <br />
    <asp:Label ID="Label1" runat="server"></asp:Label>
    </form>
</body>
</html>

操作完成結果如下:

整理一下三者的差異:

  • TextBox.ReadOnly, TextBox.Enabled設定後,不管前端的值如何變更,Server端讀取到的還是原本賦與的值。
  • TextBox.ReadOnly = true時,前端仍會Post回修改過的新值,但會被TextBox Control忽略
  • TextBox.Enabled=false時,前端的內容根本不會被回傳(<input disabled>的關係)
觀察LINQ to SQL DataContext的連線開啟時機

昨天的文章發表後,有兩位網友提到了DataContext是否要加using的議題。

我接觸LINQ to SQL是由Scott Gu的這幾篇文章開始入門的,在他的範例中沒有特別提到using,我也自始就忽略DataContext有實做IDispose這件事。雖然用using包住絕對有益無害(只要小心using中間過程如將DataContext傳到外部,要留意using結束後外部就不可再繼續叫用),但我倒認為DataContext裡的Connection應該不是一new DataContext就建立一條連線不放,直到Dispose()才結束釋出,而是Query時開啟連線,用完即關閉;Update時再重新開啟連線,用完再度關閉。如此【最後一刻開啟、用完立即關閉】的寫法,較符合珍貴資源應用的Best Practice;換句話說,在DataContext裡應該就己實踐了類似using (SqlConnection)的做法,盡可能讓連線時間最短化。不過,畢竟只是猜想,不如做個實驗觀察一下。

static void log(string msg)
{
    Console.WriteLine("{0:HH:mm:ss.fff} {1}", DateTime.Now, msg);
    System.Threading.Thread.Sleep(2000);
}
 
static void Main(string[] args)
{
    MyLabDataContext db = new MyLabDataContext();
    log("DataContext created");
    var q = from o in db.Players
            select o;
    log("LINQ defined");
    Console.WriteLine(q.Count());
    log("Get the row count");
    Player p = new Player()
    {
        UserName = "Darkthread",
        RegDate = new DateTime(2009, 1, 1),
        HiScore = 65536
    };
    db.Players.InsertOnSubmit(p);
    log("InsertOnSubmit");
    db.SubmitChanges();
    log("SubmitChanges");
    db.Dispose();
    log("Disposed");
    Console.Read();
}

在以上的程式範例中,每個動作間相隔兩秒鐘,並記錄發生時間,以便跟SQL Profiler記錄的時間對照。依我的推論,連線開啟關閉應該會發生在q.Count()與db.SubmitChanes()。

00:55:50.285 DataContext created
00:55:52.292 LINQ defined
0
00:55:54.730 Get the row count
00:55:56.736 InsertOnSubmit
00:55:58.850 SubmitChanges
00:56:00.850 Disposed

接著我們對照一下SQL Profiler看到的結果:

 

 

EventClass StartTime EndTime TextData
Audit Login 2009-10-19 00:55:54.587 NULL -- network protocol: TCP/IP
Audit Logout 2009-10-19 00:55:54.587 2009-10-19 00:55:54.647 NULL
RPC:Completed 2009-10-19 00:55:54.647 2009-10-19 00:55:54.647 exec sp_reset_connection
Audit Login 2009-10-19 00:55:54.647 NULL -- network protocol: TCP/IP
SQL:BatchCompleted 2009-10-19 00:55:54.647 2009-10-19 00:55:54.647 SELECT COUNT(*) AS [value] FROM [dbo].[Player] AS [t0]
Audit Logout 2009-10-19 00:55:54.647 2009-10-19 00:55:58.720 NULL
RPC:Completed 2009-10-19 00:55:58.720 2009-10-19 00:55:58.720 exec sp_reset_connection
Audit Login 2009-10-19 00:55:58.720 NULL -- network protocol: TCP/IP
RPC:Completed 2009-10-19 00:55:58.810 2009-10-19 00:55:58.810 exec sp_executesql N'INSERT INTO [dbo].[Player]([UserName], [RegDate], [HiScore]) VALUES (@p0, @p1, @p2)',N'@p0 nvarchar(10),@p1 datetime,@p2 int',@p0=N'Darkthread',@p1='2009-01-01 00:00:00',@p2=65536
Audit Logout 2009-10-19 00:55:58.720 2009-10-19 00:59:25.550 NULL

我這麼解讀,00:55:50建立DataContext時並沒有開啟任何連線,q.Count()發生於00:55:54,此時有了第一次Audit Login。基於Connection Pooling的特性,每次SqlConnection.Open()時,會產生一個Audit Logout、exec sp_reset_connection再加一個Audit Login,而在此發生了兩次,第一次在00:55:54,第二次在00:55:58,剛好就是q.Count()及SubmitChanges()發生的時點。

最後一個Audit Logout則是出現在程式完全結束,Process終止,Connection Pool的連線真的被關閉時(00:59:25)。

由以上觀察,應可印證我的推論"LINQ to SQL裡DataContext內部對SqlConnection的處理原則為: 必要時才開啟,用後立即關閉"。進一步延伸,加上using應不會產生顯著影響,但對於實作IDispose的物件使用using絕對是良好的習慣。

小試LINQ to Oracle

上回針對Oracle使用LINQ做了一番評估,打算為Oracle相關專案導入LINQ預做暖身。無意發現除了Entity Framework外,還有另外的選擇---devart的LINQ to Oracle!

基於工作專案常偏向RAD性質,對我來說,LINQ to Oracle的輕巧優於EF的龐大嚴謹功能完整。而LINQ to Oracle雖然也找得到免費解決方案,但畢竟要用在工作上,產品有廠商支援撐場,總是比Open Source多幾分穩當,何苦為了幫公司省錢砸了飯碗。

devart的產品看來符合我的需要,因此決定著手試用逐步體驗。到devart的網站下載dotConnect for Oracle安裝後,Visual Studio 2008在新增項目時就會多出一些選項。我試做了第一個LINQ to ORACLE範例:

1.在專案裡新增一個Devart LINQ to SQL Model--HRDataContext.lqml。(相當於LINQ to SQL的dbml)

2.在Solution Explorer裡點兩下lqml檔案,並不會在Visual Studio裡開出編輯畫面,而是另外啟動Entity Developer for dotConnect。雖然沒有內嵌在VS2008 IDE內,但它仿效LINQ to SQL dbml的視覺化操作,透過拖拉就可對應資料表Schema建立Class。
如下圖,先 (1) 建立與Oracle DB的連線 (2) 選取資料表,拖拉到設計區,接著就出現跟LINQ to SQL一樣的設計畫面 (3) 記得要改一下Context Namespace,預設會跟lqml同名,而二者名稱相同容易混淆 (4) 不要忘記按一下儲存,設計結果才會回寫到VS2008專案裡。

3.回到VS2008,會發現Entity Developer for dotConnect會依設計結果產生多個額外檔案。

4.LINQ to Oracle的程式寫起來跟LINQ to SQL完全相同:

    class Program
    {
        static void Main(string[] args)
        {
            HRDataContext db = new HRDataContext();
            Player p = new Player()
            {
                Playerid = "Darkthread",
                Name = "黑暗執行緒",
                Email = "admin@darkthread.net"
            };
            db.Players.InsertOnSubmit(p);
            db.SubmitChanges();
            Console.WriteLine("Done!");
        }
    }

5.執行後用SQL lPlus查詢,資料真的被寫入DB了。

可以把LINQ to SQL的開發經驗直接移植到Oracle上,實在是件快意無比的事! 啾咪 ^.<

Oracle Distributed Transaction Error

寫了以下的程式做SQL與Oracle的分散式交易:

using (System.Transactions.TransactionScope tx = 
    new System.Transactions.TransactionScope())
{
    //... Update data on SQL Server ...
    using (OracleConnection cn = new OracleConnection(cnStr))
    {
        cn.Open();
        OracleCommand cmdOra = new OracleCommand(
            "INSERT INTO MYLOG VALUES (SYSDATE, 'DEBUG')", cn);
        cmdOra.ExecuteNonQuery();
        cmdOra.CommandText = 
            "SELECT LOGTIME FROM MYLOG WHERE REMARK='DEBUG'";
        OracleDataReader dr = cmdOra.ExecuteReader();
        dr.Read();
        Response.Write(dr["LOGTIME"].ToString());
        dr.Close();
        cn.Close();
    }
    //tx.Complete();
    Response.Write("OK");
}

執行環境為Windows 2008 x64 + ASP.NET 2.0 + System.Data.OracleClient + Oracle 11.1.0.6 x64 Client。程式一直傳回ORA-00161: transaction branch length 105 is illegal (maximum allowed 64) 錯誤。

網路上有不少相關的討論,大都指向Oracle的官方說法:

Windows Vista and Windows Server 2008 introduce new MSDTC changes that do not interoperate with older versions of Oracle Services for MTS. Oracle Services for MTS 10.2.0.4 and higher, with the exception of 11.1.0.6, support these new changes on Windows Vista Service Pack 1 and Windows Server 2008 or higher.

敢情我是中了籤王,10.2.0.4以後版本都可以支援,但11.1.0.6例外。到OTN下載 11107_w2k8_x64_production_client.zip (523MB)。

下載安裝,藥到病除!

黑暗尋寶完全攻略

歷經漫長的六日(原本預估只想撐住四小時,結果百米賽跑變成馬拉松... orz),紀念黑暗執行緒網站百萬人次的黑暗尋寶活動,終於由CCN在2009/10/15 00:36達陣成功,抱走VX-7000! 恭喜CCN之外,也向各位參賽者說聲辛苦了。

【以下有雷,會提及解題關鍵,還想保留闖關樂趣的人回頭是岸】

依粗略統計,這次比較威的關卡是第1.5關(Seednet網頁空間免費加贈的URL Redirect及HTTP 404)、第3關、第6關,跟許多人想破頭卡很大的第7關。其實比賽開始後10小時(16:38)就有人(Orange)抵達第7關,排除1.5關的干擾後,又有很多高手紛紛來到第7關,但卡在一張達文西密碼電影海報的照片的JPG就沒了頭緒。於是分別在10/11 22:22追加公告關於EXIF的提示,10/12 22:00公告變位字提示,10/13 22:00公告CLSID提示,10/14 22:00公告"茶包射手的獵物提示",終於由CCN率先破解謎語,抵達終點。

現在來分享一下本次關卡設計的背後祕辛及破解說明:

  1. Why GUID?
    由一個網頁找到下一個網頁的遊戲,URL是關鍵,如果URL的組合可以被預期或猜測,就可能發在不知線索的情況下,直接猜中下一關檔名。很長的亂數名稱是一種做法,但身為MS Developer,我更愛GUID。
  2. Why Seednet Homepage?
    我猜有些人可能會動起"暴力破解"的歪腦筋(qwert0533,對!! 就是在說你 XD),使用程式大量嚐試可能的GUID組合,因此我選擇將網頁放在平日閒置的Seednet個人網頁空間而非我的部落格平台,以防止受到暴力破解攻擊對Blog本身的波及。但Seednet怪異的分流機制,在一開始造成一些人的困擾,在此說聲抱歉。
  3. 為什麼遊戲網頁都要放廣告?
    廣告就真的只是廣告而已,跟解題沒有任何關聯。不過我想有很多人會花時間去查它,足以產生不錯的欺敵拖延效果。後來證明 1) 的確很多人被廣告迷惑 2) 其實根本沒必要"拖延"...
    當然,說不想藉趁機賺一下廣告收入絕對是騙人滴。對不起,我又貪財了。
  4. 第1關怎麼解?
    這... 這... 這... 按連結,好嗎? 這明明是送分題。
  5. 第2 關怎麼解?
    畫面看不到東西,任何Web Developer的下一步反射動作就是View Source,看HTML原始碼,原則上這也是送分題。
  6. 更! 第3關是在搞什麼鬼?
    用第2關的View Source技巧,可以發現第3關的圖檔URL變成darkthre”e”dbanner.jpg,然後就卡住了。 設計思維是用"檔名不同"突顯"第3關故意用了一張不一樣的圖",所以圖是關鍵,應"仔細檢查"兩張圖有什麼不同。(瀏覽器的Zoom In功能可以幫上大忙!)
  7. 第4關,Palette調色盤? 跟解題有什麼關係?
    白色黑色"Palette”的GIF圖檔,說明了"跟調色盤有關",而GIF圖檔是內建256色調色盤的,所以就用Hex Viewer類的工具看一下,GUID果然藏在裡面:
     
    感覺上有些朋友較少接觸GIF圖檔的格式細節,看GIF檔案找到答案進了第5關,居然還是不知道"Palette”與解題有何關聯? (城市還有鹽,對! 我就是在說你~~)
  8. 第5關,其實是最簡單的!
    A1 A2 A3 A4 A5 A6 A7 A8 - B1 B2 B3 B4 - C1 C2 C3 C4 - D1 D2 D3 D4 - E1 E2 E3 E4 E5 E6 E7 E8 E9 E10 E11 E12
    Hint Formulas:
    1.A7 - E10 - E8 = -8
    2.B4 * A5 * D1 = 726
    ... 略 ...
    511.A8 * B4 + B2 = 12
    512.E9 / E4 - E1 = –2   
    題目一清二楚,完全不拐彎,也沒陷阱。我寫了程式用亂數出題,偷懶沒去細數如何包含最少的聯立方程式就有解,所以放上512個公式以求保險,順便也展現我的氣度與佛心。(謎之聲: 去~~~)。
    這題可以寫程式解,不過因為隨機產生的公式有些很白目(例如:  507.C1 - A5 / A5 = 3),我想直接用人工分析應該比較快,花點耐性不用太久就可以解開!
    這關完全沒被抱怨,看來是大家最愛的一題;相對地,也意味難度跟趣味性不足呀! XD
  9. 第6關,向本站的忠實讀者致意!
    提示只有一行編碼: orOiuKK3otGi0aKzorWizwCjwqOyo7SjswAU/yL/I/8Z/wDvvJnvvKXvvKbvvKMAIf8X/xH/Gf8W/xb/E/8i/xP/Jf8Q/yX/ 程式寫多的人都看得出來是Base64編碼,用Convert.FromBase64String()解開,可以得到:
    A2 B3 A2 B8 A2 B7 A2 D1 A2 D1 A2 B3 A2 B5 A2 CF 00 A3 C2 A3 B2 A3 B4 A3 B3 00 14 FF 22 FF 23 FF 19 FF 00 EF BC 99 EF BC A5 EF BC A6 EF BC A3 00 21 FF 17 FF 11 FF 19 FF 16 FF 16 FF 13 FF 22 FF 13 FF 25 FF 10 FF 25 FF
    抓一下00的位置當成分割點,可以分成16, 8, 8, 12, 24個Byte,看起來跟GUID的8-4-4-4-12結構有點關聯。把byte[]轉成string試試,不管用哪一種Encoding,會發現string中有一部分會變成全型的0-9A-F字元。忠實讀者一定不會忘記中文編碼是本站的特色主題之一,而打開中文編碼解析工具,照著BIG5, GB2312, UCS-2, UTF-8(一個中文字會變3個byte,4*3=12解開了第四節長度不合群之謎), UTF-16編碼GetString(byte[]),分開取得五節全型16進位數,換成半型就是前往第七關的鑰匙!
  10. 第7關終極殺陣
    眾多高手被卡在此動彈不得,逼得只能陸續增加提示,降低難度後,才有人闖關成功。
    最初只有一張達文西密碼的電影照片JPEG檔,就是全部的提示。圖片是網路上找到的,被我加了工,藏了資訊在EXIF裡,不過似乎很多人沒有料到EXIF裡會藏東西。(當然,也有高手在第二天連海報照原始圖都找到,還用工具做過比對,驗證圖片內容完全一致,鎖定了線索就在Metadata中,了不起!)
    有鑑於讓眾家高手卡著無解也不是辦法,於是我先釋出第一波提示,讓大家聚焦在EXIF的Camera欄位:
    * It's not a GIF
    * Metadata
    * Canon 10D, Canon 5D2, Nikon D70

     
    不過即便鎖定了EXIF,似乎有些朋友還沒體會到”tri linen St. RCP(三亞麻布聖RCP?)"是一段變位字,照片之所以選擇達文西密碼,就是希望電影一開始的變位字謎團可以形成暗示... 照片暗示效果不彰,索性祭出第二波提示:
    * 啊,嚴峻的魔鬼! 啊,跛足的聖人!
    明確指明是變位字後,大家知道到重組,但大多還是沒什麼頭緒(其實可以用字典暴力攻擊把變位字可能的組合試出來,但成本看來不低),再釋出第三波提示:
    * CLSID
    其實,"tri linen St. RCP”是RSClient.Print的變位結果,這顆Reporting Service附屬的由網頁直接印報表元件在黑暗小站亮相過好多次(1 2 3),提示是CLSID,期望勾起大家的回憶,很快聯想到是它。(後來我發現這個提示很糟,因為Google CLSID + darkthread就可以直接連到相關網頁,幸好沒人藉此破關)
    無奈大家跟我還是沒能心有靈犀,於是得再丟出第四波提示,直接說Reporting Service太明了,苦思許多,想了個"茶包射手的獵物",暗示它在本站演過茶包。公告後不久,CCN終於闖關成功,我終於不必再為第五波提示傷透腦筋了。

謝謝所有參賽的朋友,希望大家在解題過程中有享受到樂趣。歡迎大家在留言版留下感想或意見,覺得哪一關最有趣,哪一關卡最久,哪一關最欠揍,什麼感想都可以。

我超愛這類動腦解題的比賽,下回各位如有舉辦,記得要通知我!

最後,再次恭喜CCN!!

【茶包射手專欄】VS2005有時無法Debug

同事遇到的問題,並自己找到解答,順手做一下筆記。

Windows XP + VS2005 + IE8,發現"有時候"會發生無法Debug ASP.NET網頁的狀況,按下F5後,IE開啟,但未進入Debug模式。(Debug鈕變回綠色可按狀態)

網路搜尋後,找到MSDN論壇上的這篇論。比對之後,發現與該文情境完全吻合: 昇級IE8後發生,而"有時候"的關鍵的確在於有另開一個IE視窗時。而依照文中的解法,加上Registry後,問題就排除了。

謝謝你,9527 Thinker, Lolota!

取代DTCPing的新工具-DTCTester

今天要設定Windows 2008防火牆使用SQL分散式交易,查資料時發現了檢測MSDTC連線的新工具--DTCTester

簡單來說,它是用以取代DTCPing的改良版。主要優點是只需在Client執行,不像DTCPing得在Client,Server都跑互連,而且由於它會真的連線SQL Server進行一些讀寫測試,相較於DTCPing只檢查網路傳輸層,更能正確反應SQL連線的真實狀態。換句話說,過去DTCPing成功但SQL分散交易不通的"偽陽性"失真情況可望改善。

使用時要先在ODBC設定DSN(注意: x64 OS要記得設定32bit的ODBC),然後傳入DSN名稱、User Id、Password作為參數(密碼會明碼顯示是一大缺點)執行dtctester即可。

測試結果還算易讀,失敗時的訊息範例如下(故意不開防火牆):

X:\Tools\DTCTester>dtctester labdb user pwd
Executed: dtctester
DSN:  labdb
User Name: user
Password: pwd
tablename= #dtc27984
Creating Temp Table for Testing: #dtc27984
Warning: No Columns in Result Set From Executing: 'create table #dtc27984 (ival int)'
Initializing DTC
Beginning DTC Transaction
Enlisting Connection in Transaction
Error:
SQLSTATE=25S12,Native error=0,msg='[Microsoft][SQL Server Native Client 10.0]The
 transaction has already been implicitly or explicitly committed or aborted
'
Error:
SQLSTATE=24000,Native error=0,msg=[Microsoft][SQL Server Native Client 10.0]Inva
lid cursor state
Typical Errors in DTC Output When
a.  Firewall Has Ports Closed
-OR-
b.  Bad WINS/DNS entries
-OR-
c.  Misconfigured network
-OR-
d.  Misconfigured SQL Server machine that has multiple netcards.
Aborting DTC Transaction
Releasing DTC Interface Pointers
Successfully Released pTransaction Pointer.

成功時的訊息範例:

X:\Tools\DTCTester>dtctester labdb user pwd
Executed: dtctester
DSN:  labdb
User Name: user
Password: pwd
tablename= #dtc30032
Creating Temp Table for Testing: #dtc30032
Warning: No Columns in Result Set From Executing: 'create table #dtc30032 (ival
int)'
Initializing DTC
Beginning DTC Transaction
Enlisting Connection in Transaction
Executing SQL Statement in DTC Transaction
Inserting into Temp...insert into #dtc30032 values (1)
Warning: No Columns in Result Set From Executing: 'insert into #dtc30032 values
(1) '
Verifying Insert into Temp...select * from #dtc30032 (should be 1): 1
Press enter to commit transaction.

Commiting DTC Transaction
Releasing DTC Interface Pointers
Successfully Released pTransaction Pointer.
Disconnecting from Database and Cleaning up Handles

建議可用它取代DTCPing。

【延伸閱讀】

OracleCommand.CommandText的換行符號處理

使用System.Data.OracleClient連線Oracle執行指令,發現以下的程式寫法會引發奇怪的錯誤:

using (OracleConnection cn = new OracleConnection(cnStr))
{
    cn.Open();
    OracleCommand cmd = new OracleCommand(@"
begin
update myJobQueue set SendFlag = 'X' where JobId = 'XXX';
update myJobQueue set SendFlag = 'D' where JobId = 'ZZZ';
end;
", cn);
    cmd.ExecuteNonQuery();
    cn.Close();
}

ORA-06550: line 1, column 1:
PLS-00103: Encountered the symbol "" when expecting one of the following:

  begin case declare exit for function goto if loop mod null
  package pragma procedure raise return select separate type
  update while with <an identifier>
  <a double-quoted delimited-identifier> <a bind variable> <<
  form table call close current define delete fetch lock insert
  open rollback savepoint set sql execute commit forall merge
  library OPERATOR_ <a single-quoted SQL string> pipe

Oracle抱怨遇到**空字串**??

指令非常單純,看不出任何可疑之處! 不意外地,將指令文字Copy到Toad或其他Oracle Client程式測試,都可以成功執行。

幾經推敲,終於找出原因來,原來錯誤是字串夾帶的換行符號所引起的(這讓我想起上次在SSMS也有類似遭遇)。測試結果,對字串來個Replace("\r", ""),只留下\n,問題就可以排除囉!

jQuery-將網頁元素名稱標示出來

不知有沒有人跟我有一樣的困擾? 步入中年後,腦容量似有變小趨勢,每次寫網頁Client Script時,<input>, <select>的欄位名稱老記不住,要用時總是要回HTML Source或用IE Dev Tools去查,查完了一轉頭又忘了,再不然就是切換回Script Editor時搞不清楚剛才寫到哪裡。幾番折騰下來,效率很差,心情很糟。(老了,不中用了~~ 不中用了~~ 不~中~用~了了了了~~~)

於是我寫了以下的jQuery Plugin,試圖把所有的輸入元素id直接顯示在網頁上,方便對照,也試圖挽救我岌岌可危的事業。

$.fn.showElemId = function(bgc, fc) {
    return this.each(function() {
        var $elem = $(this);
        //if (!$elem.is(":visible")) return;
        var pos = $elem.offset();
        var html = "<span style='position: absolute; z-index: 999; top: " +
        (pos.top - 15) + "px; left: " + pos.left +
        "px; background-color: " + (bgc || "red") + "; color: " +
        (fc || "white") + "; font-size: 11px; line-height: 12px; padding: 1px;'>" +
        (this.id || "----") +
        "</span>";
        $("body").append(html);
    });
};
/* 使用範例: $(":text,select").showElemId(); */

用Hotmail的註冊畫面做個示範(只查:text,select,不包含:button,:password),如下圖所示,輸入欄位元素名稱一目瞭然,列印出來,就不用每次去HTML Source裡找半天了,真是中年程式開發人員的福音呀~~~

Oracle LINQ之路

自從學會LINQ to SQL後,我就愛死那種忘記SqlConnection、丟掉SqlCommand、抛下SqlParameter的簡潔,乾乾淨淨幾行Code就搞定查詢、新增、修改、刪除的感覺。

無奈在公司,就算先撇開專案團隊成員是否已具備LINQ技能的問題,面對Oracle林立的工作專案環境,抬出LINQ to SQL無疑是張飛打岳飛,只能乖乖回去用OracleCommand、OracleParameter行禮如儀。

不過,我還是沒有放棄在Oracle專案使用LINQ的念頭。ADO.NET Entity Framework(以下簡稱EF)被視為微軟在資料庫存取層的明日之星,在架構上可以跨料庫,只要找到對的Provider,原則上程式不用修改就可以抽換資料庫。

由目前的走向來看,LINQ to SQL將會漸漸淡出。但對於一些時桯緊迫的專案來說,我始終覺得用LINQ to SQL更加簡便直覺,才是快速開發的王道。(有點遺憾在這個議題上,微軟的想法並不像WebForm vs MVC,採二者並行而不互相排擠,幾乎確定未來會由EF一統江山,LINQ to SQL將不會再持續發展)

"快速建置系統,滿足User急如星火的需求"是我工作上大部分專案的特性,因此在系統開發上不太容易爭取空間好反覆琢磨、精心打造出以嚴謹架構取勝的系統。換句話說,工具愈輕巧,愈能在較侷限的空間中有所發揮。在只有40坪大小的地基,就不要妄想打造台北101,凡事都要因地制宜;在必須低著頭走路的坑道,倚天劍還不如一把瑞士刀,如此才是生存之道。(謎之聲: 這樣也要押韻,你夠了沒?)

回到要在Oracle上使用LINQ的議題上,EF未內建Provider for Oracle,而微軟跟Oracle不知是否基於業務策略考量,都遲遲沒有任何補足這塊的計劃,所以現階段要實現結合LINQ與Oracle的理想,需仰賴3rd Party廠商的解決方案。在網路上爬找了一陣子,挖到一些解決方案: (包含我不負責任的簡短評論)

  • Open Source專案: Linq to Oracle
    免費且有原始碼,但看來並未被廣泛應用,改版次數有限,而上次版本更新是近一年前的事。真要使用得負擔自行Debug及修改的責任。少了廠商背書,應用在工作專案裡,除了搞Business Logic外,還要留意問題是否為DAL層Bug引起,個人認為較不妥當。
  • Open Source專案: DB_Linq
    最後一個版本是2008/09,存在的疑慮跟Linq to Oracle差不多,而且它的目標訂成跨平台,Oracle、PostgreSQL, MySQL, Ingres, SQLite通吃,但相對的,對Oracle的專精度只怕會更低。
  • Sample ENtity Framework Provider for Oracle
    一樣是一年多前發表的,而且文章上已經表明了"This is an unsupported sample and should be treated as such. Although reasonable effort has been put to make sure that basic EF scenarios work with Oracle, there are certain limitations. Use in production environment is strongly discouraged.",那... 玩玩就好,別把自己的事業押在上面!
  • DataDirectADO.NET OracledevartdotConnect for Oracle
    這兩個總算是廠商持續支援的解決方案了,是目前市場上公認兩大EF Oracle Provider商業產品。二者在產品特性上不相上下,都支援免裝Oracle Client直接連線、LINQ to Entity、Entity SQL。至於採購成本,有趣的是DataDirect在網站並未列出明確授權價格,只留下一支電話;而dotConnector則是單一授權$299.95,四個開發者優待價$699.95,企業吃到飽方案$1799.95。

看來,現階段若真想在Oracle玩LINQ,應該就是在DataDirect與devart兩個產品擇一。Survey過程中,最讓我驚喜的一件事,莫過於dotConnect for Oracle支援所謂的LINQ to Oracle。也就是說,devart比照LINQ to SQL實作出了對應於ORACLE的版本,讓深愛輕巧工具的我十分動心。

皮卡丘LINQ to Oracle,就決定是你了!

後續,我打算花點時間試用一下,先應用在一些小專案上,有心得再跟大家報告。

更多文章 下一頁 »

搜尋

Go

<October 2009>
SunMonTueWedThuFriSat
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567
 
RSS
【工商服務】
最新回應

Tags 分類檢視
關於作者

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

文章典藏
其他功能

這個部落格


BlogLook Score and Rank

Syndication