無法載入檔案或組件 System.Web.Extensions 1.0.61025.0

12/28/2010
用 Visual Studio 開啟一個 ASP.NET 2.0 網站時,編譯器顯示錯誤訊息:

無法載入檔案或組件 System.Web.Extensions, Version=1.0.61025.0

Crystal Reports 錯誤:記憶體不足(0x80041004)

12/22/2010
印象中,這個錯誤碰到好多次了,還是記錄一下吧。

錯誤訊息

System.Runtime.InteropServices.COMException (0x80041004):記憶體不足,無法進行作業。

除了上述訊息,使用者可能還會看到「0x800003E5:無法開啟報表。」的錯誤訊息。

Subversion 送交檔案時被入侵偵測系統誤判為壞人

12/22/2010
這是好幾個月前發生的事了,當時沒空整理,現在要整理,手邊卻沒有資料(圖片)了。

簡單地說,有些 .aspx 或 .aspx.vb 檔案在送交至版本儲存庫時,Subversion 會顯示 commit 失敗的訊息。和網管反映之後,回答是我們的程式有 SQL injection 的 pattern,所以被入侵偵測系統攔下來。

CollapsiblePanelExtender 應用:控制 master/detail grid 分頁大小

12/13/2010
問題

在同一頁面顯示 master/detail 資料表,利用 AJAX Control Toolkit 的 CollapsiblePanelExtender 來展開/隱藏 master 資料表的 GridView 時,希望能依 master GridView 展開/隱藏的狀態來控制 detail GridView 的分頁大小(PageSize)。例如,master GridView 展開時,detail GridView 每頁只顯示 5 筆資料,但若將 master GridView 隱藏起來,detail GridView 每頁就要顯示 15 筆資料。

ASP.NET 狀態管理機制比較表

12/10/2010
從書上整理下來的,印出來正好一張 A4 紙(橫式),方便參考。
內容預覽:
下載 pdf 檔:ASP.NET 狀態管理機制比較表

ASP.NET Web.config 內容轉換

12/10/2010
當你在 Visual Studio 2010 中建立一個 ASP.NET Web 應用程式專案時,預設會產生三個組態檔:Web.config、Web.Debug.config、Web.Release.config(無專案式網站無此功能)。

IIS 7.5 的 ApplicationPoolIdentity 帳戶

12/07/2010
如果 ASP.NET 應用程式所屬之應用程式集區的帳戶為 ApplicationPoolIdentity (而不是我們熟悉的 NETWORK SERVICE),如下圖所示:

給 Visual Basic 用的 Console.WriteLine 程式片段

12/03/2010
底下是給 Visual Basic 用的 Console.WriteLine 程式片段,安裝到 Visual Studio 之後,寫程式時就可以像 C# 那樣,只要輸入 "cw" 再按個 Tab 鍵就能帶出 "Console.WriteLine()",且游標會停在兩個左右括弧的中間。

使用 Windows API Code Pack 取得影片長度

11/29/2010
注意:此方法僅適用於 Windows Vista、Windows 7、Windows Server 2008。

先下載 Windows API Code Pack,將套件解壓縮至某個資料夾,例如:D:\Lib\WindowsApiCodePack。

在程式中重新設定 SQL Server 資料表的流水號欄位

11/25/2010
在程式中呼叫 DBCC CHECKIDENT 命令來重設 SQL Server 資料表的流水號欄位。
範例:
string cnstr = "server=.;Database=MyDB;uid=sa;pwd=";
using (SqlConnection cn = new SqlConnection(cnstr))
{
    cn.Open();
    SqlCommand cmd = new SqlCommand("DBCC CHECKIDENT('MY_TABLE', RESEED, 0)", cn);
    cmd.ExecuteNonQuery();
}

Visual Studio 2010 剪貼文字時,中文出現亂碼的解法

11/24/2010
經常需要在 Visual Studio 2010 複製程式碼,再貼到 Word 文件,但經過複製剪貼文字,中文的部分都會夾雜亂碼。每次都要手動修正,還挺麻煩的。這個小 bug,目前至少有兩個現成的工具可以解決。

如何將 ASP.NET 應用程式專案轉換成無專案式網站?

11/17/2010
昨天有位朋友問到,原本的 ASP.NET 應用程式專案,要怎麼轉成網站?

這有點小麻煩。MSDN 上面有一份文件教你如何把 ASP.NET 網站轉換成應用程式專案,可是如果要反過來,把應用程式專案轉成網站,卻沒看到類似的說明文件。

以 KDiff3 取代 TortoiseSVN 內建的差異比對、合併工具

11/17/2010
之前使用 TortoiseSVN 內建的 TortoiseMerge 來做版本差異比對時,有時竟會出現比對錯誤的情形,例如:顯示有差異的地方,卻根本沒有差異。

後來,就把 TortoiseMerge 換掉,改用 WinMerge(在 TortoiseSVN 的 Settings > External Programs 選項底下可以自行設定 Diff Viewer 和 Merge Tool)。

剛剛發現另一個開放源碼的差異比對、合併工具:KDiff3。看起來,它又比 WinMerge 更強一些。

App Pool vs. App Domain

11/14/2010
整理兩個容易混淆的概念:Application Pool 和 Application Domain。

ASP.NET 4.0 的 ViewStateMode 屬性

11/08/2010
ASP.NET 網頁的 view state 機制雖然很方便我們保存網頁的狀態,但有些情況並不需要 view state,若能針對特定控制項關閉 view state,將有助於減少網路傳輸的資料量,並提升網頁的回應速度。

EnableViewState

在 ASP.NET 4.0 之前,網頁和控制項雖然有 EnableViewState 屬性可以控制要不要啟用 view state,但是卻不夠彈性,因為一旦將頁面(或父層控制項)的 EnableViewState 設為 False,那麼無論其子控制項是否啟用 view state 都沒有用,一律為關閉。因此,以往如果要針對特定幾個控制項啟用 view state,就只能讓整個頁面的 EnableViewState 維持預設的啟用狀態,然後再去個別設定控制項的 EnableViewState 為 False。

First We Read, The We Write

11/04/2010
First We Read, Then We Write: Emerson on the Creative Process
by Robert D. Richardson, University of Iowa Press, 2009, 112 pages.

I've just read the first three chapters, and I found it really insightful. For example, why are we reading? In the first chapter (Reading), Richardson says "Emerson did not read in order to pick up the common coin of his culture or class, ....Emerson read for personal gain, for person use." He also says that "Emerson himself read almost entirely in order to feed his writing." Indeed, we should read actively, with some useful objectives, not for showing off, not for chitchatting. It's a plain and simple idea, but I just didn't think of it. Now I  get it, "read was just the means, the end -- the purpose -- was writing."

ASP.NET 4.0 使用 SQL Server 儲存 session 資料的改進

11/04/2010
ASP.NET 4.0 和 ASP.NET 2.0 在使用 SQL Server 來儲存 session 狀態資料時,使用的 table schema 是一樣的,主要的差別在於這個 SQL Server 預儲程序:DeleteExpiredSessions。此預儲程序的用途是刪除過期的 sessions,它會由 SQL Server Agent 定期執行,預設的執行頻率為一分鐘一次。

使用 SmtpClient 發送大量郵件時出現 net_io_connectionclosed 錯誤

11/03/2010
之前曾寫過一篇〈寄送郵件的封包被防火牆檔掉〉的文章,把當時的問題狀況和解決過程記錄下來。事隔一年多,應用程式的郵件發送 log 又出現頻繁的網路 IO 中斷的狀況。上次的情形是只有特定內容的信件才會被防火牆誤判而擋掉,這次則是沒有特定規則,也就是說,同一封郵件,有些人有收到,有些人則因為網路 IO 中斷而沒收到。

Collective Code Ownership

11/01/2010
程式碼共有(collective code ownership)這個概念指的是每個人都有責任修正瑕疵,而且團隊中的任何人也都可以修改應用程式的任何部分。這種做法不僅有助於提升軟體品質,也能鼓勵團隊成員主動發現問題、解決問題(而不是主管有交代才做)。

謹慎使用 Web Garden

10/28/2010
有句話說,東西沒壞,就別去動它。只是做系統管理的,有時會忍不住手癢,想多改善一些、讓系統跑快一些。這個小小的善念,有時卻會給自己和團隊帶來不小的麻煩。我自己就有個慘痛的親身經驗可供借鏡。

《軟體構築美學》已出版

10/22/2010

今天收到出版社的通知,Brownfield Application Development in .NET 的中文版上市了,書名是《軟體構築美學》。

這本書的第 1~6 章是由我負責翻譯,其餘 7~13 章以及前言、附錄等內容則是由另一位譯者張簡才祿翻譯。

內容簡介

本書將大部分專案團隊所面臨到的既有、且難以維護及改善的應用程式,稱為棕地應用程式(Brownfield Application),它具有三項特點:既有程式碼;因差勁的實務作法而造成汙染;仍有改善或重複使用的潛力。

回覆有關 C#「參數陣列」的疑問

10/18/2010
由於留言回覆的內容太長,超過 Blogspot 允許儲存的字數限制,所以移到單獨的文章裡回覆。

七本免費的 .NET 電子書

10/15/2010

Visual Studio 負載測試筆記

10/01/2010
這是之前做負載測試時的隨手筆記... (2010-10-1 更新:補一些操作步驟和貼圖)

7-ELEVEN 裡的隨想

9/28/2010
第一次在 7-ELEVEN 裡面的「吧檯」吃早餐,小口小口喝著燙嘴的抹茶奶綠,一邊讀唐諾《閱讀的故事》,偶爾抬頭看看玻璃窗外的行人車輛。這樣悠閒的感覺真好。

我對《閱讀的故事》第一印象並不怎麼好,反覆出現的「喬張作致」破壞了閱讀興致,心想:怎麼這人講話如此「喬張作致」?要把訊息傳達給別人應該用詞簡單明瞭而非東拉西扯裝模作樣刻意展現自己調動文字多麼厲害高明不是這樣子嗎?

啊,我不禁學起唐諾的口氣了 :)

[短片] 使用 Office 文件影像工具的字元辨識功能

9/24/2010

移除 Acronis True Image 之後,陷入重開機的無窮迴圈

9/17/2010
有句話說:「禍不單行。」真是一點不差。

早上由於防火牆掛掉,整個單位對外網路不通,想說也辦不了什麼事(現在不能上網還能做什麼 :p),就來做系統備份吧。同事告知,我們單位有買新版的 Acronis True Image for Server。好,那就來裝吧。哪知道,竟是噩夢的開始......

BugTracker.NET 3.4.4 安裝指南

9/15/2010
BugTracker.NET 是一套開放原始碼的 issue tracking 系統,使用了一陣子之後,覺得還挺不錯的(原本的 BugNET 就不再使用了)。它的設計風格可說是簡約樸素--此風格不僅表現在操作介面上,程式碼也是如此。作者並未使用「程式碼後置」(code-behind)撰寫模型,而是以單一 aspx 頁面搭配一些資料存取和其它工具類別的方式來撰寫。

這篇是我的安裝筆記。

[VB] 盡量不要用 IIf 函式,改用 If

9/13/2010
Visual Basic 的 IIf 函式會評估所有傳入的引數,這有時會造成問題,例如:

'   示範 IIf 函式的陷阱
    Dim i = 0
    Dim j = IIf(i <> 0, 10 \ i, 0)  '執行時會出錯! 

    Dim x As Integer?
    Dim y = IIf(x.HasValue, x.Value, 0)   '執行時會出錯!

《生命的壯闊》:演化不見得是朝進步的方向

9/12/2010
書名:生命的壯闊 (Life's Grandeur)
作者:Steven Jay Gould
譯者:范昱峰
出版:時報,1999/7/5

小引

本以為這本書只是單純講生物演化的知識,讀了以後才發現裡面還有一個很重要的部份,是在推翻一些生物演化的謬論與迷思。作者在書中以各種實例(包括自身罹患癌症、棒球的平均打擊率等)來點出以「平均數」或「中數」來導出結論的錯誤之處,這個部分的論述讓我想到了另一本書:《黑天鵝效應》。

《最後的知識分子》書摘

9/12/2010
書名:最後的知識分子 (The Last Intellectuals)
作者:Russell Jacoby
譯者:傅達德
出版:左岸文化,2009

之前明明說過不貼書摘上來了,結果還是忍不住....Orz

想說既然整理了一些筆記,有些內容或許還是有值得分享的地方。但這本書,我想還是只貼書摘的部分就好,心得筆記就免了,反正目前也只有一些模糊的想法,不值一哂。

博士論文是謀求一份像樣的學術職位以及可以過知識分子生活的門票,必得消磨多年的心力精神,遑論研究與寫作所耗費的苦工。對於許多青年知識分子來說,博士論文就是他們生命裡最重要的文化大事與競賽。

我的 Visual C# 2010 按鍵速查表

9/02/2010
微軟網站上提供的 Visual Studio 2010 Keybinding Posters 雖然挺方便的,只是印出來之後,發現文件的 size 還是嫌大了點,其中有些按鍵對我來說也不是很常用,因此依自己的習慣整理了一份比較小的按鍵速查表,如下圖:

技術上做不到?你當我是 XX 啊...

8/28/2010
標題似乎有點過火,卻是親身見聞。相信從事軟體專案開發的人,多少也都碰過類似的場景:

客戶:「這個地方我想要這樣....這樣.....可以嗎?」
開發團隊:「不好意思,這個做不到喔。」

接下來,客戶可能會有以下幾種反應:

.NET 程式難寫?不是南北拳的問題...

8/26/2010
話說有一天,主管湯米轉達老闆指示,要吉娜規劃程式設計的訓練課程,目標是大約 40 小時內讓所有員工都會寫 ASP.NET 程式(其中包括一套既有的應用程式框架和程式設計模型),以便投入既有專案的程式撰寫與維護工作。學員當中,有的寫過 ASP,少數寫過 ASP.NET 程式,另外還有一些則是完全沒有 .NET 或 Web 應用程式的開發經驗,甚至連物件導向程式語言都沒用過。

Step into the fray 是甚麼意思?

8/21/2010
好多字典都查不這個片語,但 Google 可以找到一堆,例如:Foreign banks step into the fray

Google 網頁翻譯是將它譯為「加入競爭」,Bing 網頁翻譯則是「步入....」或「單步執行戰鬥」。

Put sth. in perspective 是甚麼意思?

8/17/2010
每當翻譯時碰到 put something in perspective 這個片語,腦袋都會卡住,所以把這個片語的相關資料整理一下,方便日後參考。

甚麼是 quirk?

8/17/2010
最近碰到一個不知怎麼翻譯的字:quirk。原文如下:
In some ways, it's easier than a straight greenfield project because you're starting from scratch, yet you still have a reference application that defines the functionality. At the same time, you're essentially rebooting an application that, from the users' perspective, may work. Maybe it has some quirks but few users will trade the devil they know for the devil they don't.
又是一個字典查得到字義,卻不太有把握能譯對的例子。

I'll Be Back

7/24/2010
這陣子想辦法把拖延很久的學業做個收尾,目前看來是有點眉目了。心上這塊石頭壓了這麼久,總算可以卸下。嗯,七年.....都可以念醫學院了 >_<|||

ASP.NET 4.0 的 HTML 編碼語法,和背後的故事

5/01/2010
在 .NET 2.0,要將 .aspx 網頁中顯示的字串編碼時,要這樣寫:

    <%= HttpUtility.HtmlEncode(str) %>

到了 ASP.NET MVC 1.0,可以用 HtmlHelper 類別的 Encode 方法:

    <%= Html.Encode(ViewData["Message"]) %>

Visual Studio 2010:在活動圖中繪製隔間(水道)

4/26/2010
用 Visual Studio 2010 Ultimate 製作 UML 模型時,發現活動圖並未提供繪製隔間(partition;我還是喜歡叫它之前的名字:水道[swimlane])的功能。如果一定要畫水道,似乎就只好繞個彎,用矩形加物件節點做出水道的樣子,像這樣:

無法安裝 Visual Studio 2010:VC 9.0 Runtime (x86) 安裝失敗

4/21/2010
在一台 Windows Server 2008 機器上 Visual Studio 2010 時出現錯誤:


點 View Error Log 連結,看到錯誤訊息是:

VC 9.0 Runtime (x86): [2] Error: Installation failed for component VC 9.0 Runtime (x86). MSI returned error code 1603

嘗試把機器上原本已經安裝的 Visual C++ 2008 Redistributable 移除掉,重開機,然後就可以順利安裝 Visual Studio 2010 了。

註:移除 Visual C++ 2008 Redistributable 時並沒有提示要重開機,所以當時我是立刻接著安裝 VS2010,發現錯誤依舊,又到 VS2010 安裝光碟的 WCU\VC90SP1\ 目錄下手動執行命令:

msiexec /i vc_red.msi USING_EXUIH=1

發現錯誤訊息是:


看起來是資料夾權限的問題。試試看進入資料夾 c:\Program Files\Common Files\microsoft shared\VC,果然出現 Access denied 訊息。我目前登入的帳戶是系統管理員,但無法查看該資料夾的安全性,也無法顯示和修改 VC 這個資料夾的擁有者。這才想到資料夾被鎖住了,重開機後,再安裝 VS2010 就沒問題了。

[影片] Visual Studio 2010 新功能 (2)

4/14/2010
這次主要是介紹:
  • Data Tip
  • Nagivate To...
  • View Hierachy

[影片] Visual Studio 2010 新功能 (1)

4/14/2010
其實網路上已經有不少 Visual Studio 2010 的教學影片,只是自己看著也想動手做看看,於是趁夜深人靜時,在家裡用簡陋的設備錄製了第一個短片,介紹 Visual Studio 2010 的一些新功能。

內容:
  • Startup Page(起始頁面)的改進
  • New Project(建立新專案)對話窗的新功能
  • Add Reference(加入參考)對話窗的速度改善
  • IntelliSense 功能的 Toggle Completion Mode(切換完成模式與建議模式)
影片在此(播放時建議用全螢幕模式,會比較清楚):


小小心得:才短短幾分鐘的影片,NG 重錄了好多次 >_<||| 錄製的音量似乎也太小聲了....下次改進。

Visual Studio 2010: 解決 Target Framework 不同所造成的問題

4/11/2010
Visual Studio 2010 裝好之後,我把之前寫的一個 Visual Studio 2008 類別庫專案開起來,跟以往一樣,VS2010 會出現專案升級精靈。專案完成升級之後,我在解決方案中加入了一個新專案(Demo01),嘗試寫點範例程式,去呼叫既有的類別庫(MyLib)。結果編譯此新專案時出現底下的警告和錯誤訊息:

談笑間,需求灰飛煙滅

4/11/2010
記得在新兵訓練中心,班長經常玩這種把戲:「稍息之後開始著裝,一分鐘後樓下集合,話說完還有 30 秒。稍息!」然後就是一陣兵荒馬亂。從事軟體開發,有時竟會和這件事一起聯想。

IIS 7.5 Application Warm-Up Module

4/03/2010
開發 ASP.NET 應用程式的人多少都碰過這樣的困擾:使用者經常抱怨,每天早上一進辦公室,登入我們的網站時都好慢好慢,要等好久才出現第一個頁面。

回答總是千篇一律:第一個存取網站的人,因為有一些初始化的動作,所以會等比較久。(使用者內心對話:原來最早開始工作的人反而會被懲罰 >_<)


使用 TortoiseSVN 處理版本的分支與合併(二)

4/01/2010
上一篇提過,主線和支線的版本可分頭進行,然後視需要將二者(或多個支線版本)合併。
這篇就是在介紹如何使用 TortoiseSVN 來處理主線與支線版本的合併(當然,支線之間也可以合併)。

合併

延續上一篇的例子,分支建立好之後,實際開發的過程可能類似這樣:
  1. 將工作目錄 MyProject\Web\HR 切換到某個分支,然後修改程式碼。改完之後 commmit 至檔案庫。
  2. 將工作目錄 MyProject\Web\HR 切回主線,修改程式碼。改完之後 commmit 至檔案庫。
  3. 前兩個步驟可能反覆數次,然後有一天,發現需要將分支合併至主線,此時請先確定工作目錄 MyProject\Web\HR 底下的修改都已經 commit,並且切換至主線。接著對此目錄點右鍵,選 TortoiseSVN > Merge。
TSVN 的 Merge 對話窗會先問你要用哪一種合併方式,這裡我們假設已開發完成一個功能分支(feature branch),欲將此分支的所有 revisions 整合到主線,因此選第二種:Reintegrate a branch。如下圖:


下一步, 選擇合併的來源 URL,亦即那個要合併至主線的分支的 URL:


下一步,設定合併選項,這裡都使用預設值。在真正進行合併前,最好先點「Test merge」鈕,測試看看能否合併。


Oops! 測試合併時果然出錯了:


它告訴我,本機工作目錄的檔案還沒 commit 至檔案庫,不可合併。這也是為什麼前面一直強調修改完分支或主線之後都要 commit 的緣故。

若合併成功,會看到如下畫面:


解決衝突

當然,實際不可能每次都這麼順利完成合併,因為各版本之間難免有衝突的情況,此時就得手動合併了。當 TSVN 在執行合併時發現版本衝突,會顯示對話窗讓你選擇處理方式:



我打算手動合併,因此選擇「Edit conflict」 ,接著會開啟 TortoiseMerge 視窗:


你可以從左右兩個面板當中找到衝突的程式碼,並利用滑鼠右鍵(或工具列按鈕)指定哪些區塊的程式碼要用左邊/右邊的版本。每做一次選擇,都可以從下方面板預覽合併的結果。所有衝突都解決後,儲存並關閉此視窗,回到上一個對話窗,點「Resolve」鈕即可。

使用 TortoiseSVN 處理版本的分支與合併(一)

4/01/2010
在介紹 Subversion 分支與合併的觀念時,常見以 trunk 代表主線版本、以 branch 代表支線版本的方式來組織檔案庫的目錄結構。例如,資料夾 MyProject\trunk 底下用來存放專案的主線版本,而 MyProject\branches 底下則存放支線的版本。

主線只一條,支線可能很多,當然,這些路線可以「分進合擊」。這篇講分(分支),下一篇講合(合併)。

一個例子

有時候,我們可能不會一開始就建立 trunk 目錄--也許是因為在建立檔案庫時,覺得專案沒那麼複雜,不需要這麼做;也可能是因為其他特殊原因,例如:團隊制定的規範。這裡用一個 ASP.NET Web 專案為例,說明可能碰到的問題與應變方法。

假設這個 ASP.NET Web 專案的目錄結構類似這樣:


這裡我們把文件、網站應用程式的原始碼、商業邏輯層的類別庫原始碼都加入檔案庫,並分別放在 Document、Web、和 BusinessLogicLib 資料夾底下。其中 Web 資料夾除了固定的 Bin 目錄,還依子系統分出庫存(Inventory)、會計(Account)、人事(HR)等子目錄。

現在假設碰到一種狀況:人事系統的使用者不斷提出新需求,導致該系統的程式需要大改。問題是,這些修改需要好幾月的時間才能完成,期間勢必還有一些更要緊的 bug 修正或細部的程式修改必須同時進行,怎麼辦?

一個直覺的辦法,是讓開發人員自行管理兩個版本,也就是說,把整個 HR 資料夾複製成另一份,目錄名稱也許叫做「HR_NewFeature」,代表這是為了開發新功能而特別建立的目錄。如此一來,開發人員就可以分頭修改 HR 和 HR_NewFeature 底下的檔案,等到需要交付時,才把兩邊的程式碼整合起來。如果你曾這麼做,應該能體會它有多麻煩--程式碼裡面到處得加上註解,標示哪一段是為何修改、哪一段必須等到所有新版功能完成時才能交付……諸如此類的,以便在整合兩邊的程式碼時不會亂掉。但事實上,這種動作既麻煩,又容易出錯,到最後可能連程式設計師自己都分不清楚哪個部分要交付,哪個部分不要。

既然如此,何不試試用工具來減輕自己的負擔?

廉價複製

與其用手動複製檔案、剪貼程式碼的方式管理版本,我們可以用 Subversion 的廉價複製功能來建立那些支線的版本(如上例中的 HR_NewFeature),並視需要進行版本之間的合併。

這裡的廉價複製,指的是 svn copy 指令。但為什麼說「廉價複製」呢?因為當你利用 svn copy 來複製資料夾的時候,雖然在本機的工作目錄中確實可以看到兩個資料夾和複製的檔案,但檔案庫裡面並沒有真正複製所有的檔案內容,而只是類似建立連結的動作,或者只儲存有變動的部分而已。這種複製動作的負載輕、成本低,故曰廉價。
可以這麼說:Subversion 檔案庫裡面其實沒有「分支」這種東西,所有的變動都只是之前版本的另一個廉價複製版本。我們只是利用廉價複製的功能,產生一個資料夾的複本,並將它視為分支,如此而已。

決定分支的存放位置

延續前面的案例,現在我們要開始為人事系統(HR)建立支線版本。首先碰到的問題,是支線的目錄要放哪?以 Web 專案來說,分支的目錄顯然不能放在 Web 資料夾底下,因為如此一來,同一個 Web 專案將會因為建立分支(廉價複製)的緣故而產生類別名稱衝突的情形。

我們可以這樣安排分支的目錄:



也就是在專案(檔案庫)的根目錄下建立 branches 資料夾,以便將各類原始碼的支線納入此目錄下管理。這種方式的一個優點,是在主線與支線的版本之間來回切換與合併時,從檔案的 URL 前面幾個字元就能分辨是主線還是支線,不容易搞混。當然這只是一種選擇,不代表最好的方式。如果你一開始就想到將來可能需要建立分支,在建立檔案庫時,就可以在根目錄下各建一個 trunk 和 branches 來存放主線與支線的檔案。

或許有點多餘,但還是註明一下:分支的目錄不可跨檔案庫(即不能將分支建立在別的檔案庫中)。
建立分支

首先把作為分支的上層目錄建立妥當,也就是 branches\Web,並確認這些目錄及主線的工作複本(working copy)都已送交至檔案庫。

接著在想要產生分支的資料夾(HR)上點右鍵 > TortoiseSVN > Branch/tag...。參考下圖:


你會看到如下圖的對話窗:


注意:若 HR 分支的上層目錄 branches\Web 沒有事先建立且送交至檔案庫,在建立 HR 分支時會失敗(路徑不存在)。

對話窗左下角的「Switch working copy to new branch/tag」核取方塊若有勾選,表示分支建立完成後,順便把目前的工作目錄切換至支線版本--通常會這麼做。當然你也可以選擇先不要切換到支線版本,等你開始要在工作目錄下開發支線版本的程式時,再利用 TortoiseSVN 的 switch 功能來切換。

Tip:第一次建立分支,可能會有點疑慮,擔心哪個步驟做錯了,或者不清楚發生了什麼事。這裡有個小建議,分支建好之後,不妨對 branches 目錄做 SVN Update,你會看到 branches\Web 底下多出一份 HR 目錄的拷貝(啊!原來分支就是 copy 嘛)。當然,你不用直接修改 branches 底下的檔案,而應當把主線的工作目錄(MyProject\Web\HR)切換到支線,再對其修改。原因很簡單:這樣你做的修改才會被編譯、執行。

在撰寫程式時,都是針對本機的同一個工作目錄,以此例而言,就是 MyProject\Web\HR。當你要修改主線的版本時,就用 switch 功能切換到主線;同樣的,要修改支線版本時,就切到支線。你所要牢記的,就是每次修改程式之前,先確認目前的工作目錄是對應到主線還是支線版本(否則你還是可能會亂掉)。

Q & A

問:我們公司有一台測試用的 app server,有時候,使用者或測試人員想要測試目前正式環境的版本,有時候又要切換到新開發的版本,這種情況也能用分支的方式解決嗎?

答:一樣適用。看他們想測哪個版本,就在測試 server 上用 SVN 的 switch 功能切換主線或支線版本。
================================================
問:這樣來回切換版本,如果支線版本很多,我的腦袋可能還是會亂掉,造成版本錯亂,這樣.....用工具來管理真的有比較省力?

答:如果真的碰到支線版本眾多,又常得來回切換的情形,不用工具管理只會更糟(想像你要記住的版本有多少種、將來如何合併)。若情況嚴重到連工具都幫不了忙,或許該停下腳步,回頭想想為什麼那些需求會如此反覆無常。
================================================
問:我怎麼知道目前工作目錄下的版本是主線還是支線?

答:TortoiseSVN 的 relocate 或 switch 功能都會顯示目前所對應的版本的 URL。


下一篇: 使用 TortoiseSVN 處理版本的分支與合併(二)

程式過客

2/24/2010
這是一件很多年前的往事,不知為什麼印象很深,最近又突然想起。

那年,我在一間不算大的公司裡寫程式,用的工具是 Borland Delphi,偶爾也寫點 C++。記得全公司好像就兩個人用 Delphi,一個是我,另一個就是老闆(我一直很佩服他)。有一次,幾個同事在辦公室閒聊,說到自己在對軟體開發這條路的想法,頗有「盍各言爾志」的 味道。某位仁兄的計畫是「打算先寫兩年程式,然後轉作 SA,磨練個兩三年的經驗,再升 PM。」我從小胸無大志,不知何謂長遠規劃,初次聽到這樣的「生涯發展路徑」,頓時覺得自己是否也該想想將來的路該怎麼走。

可是,當時我對未來的能見度大概就幾公尺遠(現在恐怕也差不多),只覺得自己可能會一直矇著頭寫程式,暗無天日,直到隧道盡頭冒出一點光,再循著光線摸索前進。

後來,我離開了那家公司,到別的地方繼續寫程式,除了參與一些新的軟體開發案,當然也少不了要維護前人留下來的 legacy code--寫程式很有趣,但修改別人的程式 bug 則是另一回事。我沒有再見到當初那位同事,也不知道他後來怎麼樣了,但是,這些年來,我似乎偶爾會在別人身上看到他的影子。我開始擔心,我參與的專案,PM 和 SA 是不是也都循著那樣的生涯路徑一路「升」上來的,我會不會碰到「說得一口好程式」的傢伙、會不會要維護他們寫的程式碼?(註:這裡的「說得一口好程式」,意思是程式撰寫經驗很有限、技術背景或 sense 不夠,卻常指點別人程式應怎麼寫;這樣的人總是認為「那些功能應該都很容易做到」,因而經常低估需求變動的成本、任意承諾需求,導致專案範圍不斷擴大、成本不斷增加,甚至懷疑開發人員的可行性評估老是在誆他。)

無論 PM、SA、SD、還是 PG(programmer),都各有其專業,團隊不應把工作流程的順序理所當然地視為職務的高低(否則測試人員不就更等而下之了?絕非如此!),然後想當然耳地任意指揮「下層」的團隊成員工作--這樣恐怕只會創造弱勢族群,令位於「下層」的人無心做好手邊的工作,老是想著「往上爬」。我懷疑「寫兩年程式就要轉做 SA」的念頭多少因此而起。而且,如此循環下去,推而廣之,恐怕很難造就專業的程式設計師,而是產生一堆「程式過客」。

都說軟體開發團隊裡不需要救火英雄了,但為什麼軟體專案老是有一堆爛攤子需要有人挺身而出力挽狂瀾?

時勢造英雄,我想多半是這樣的。

大而化之:淺談 maximize 的翻譯

1/26/2010
將 maximize 直接翻譯成「最大化」似乎已經是很普遍,一般人也接受的譯法。但我想有些場合,可以試試以「充分....」或「將....儘量提升」的方式來表現,閱讀起來會覺得比較口語化接近口語。

審稿碎碎念:幾個專有名詞修訂

1/26/2010
隨手記下幾個審稿時碰到的術語:

Subversion 目錄存取權限控管

1/21/2010
簡單記錄一下 Subversion 目錄存取權限控管的相關設定(即控制哪些人可以讀取/寫入哪些資料夾)。

Subversion 版本:CollabNet Subversion Server v1.6.5.*

Apache 的 httpd.conf 範例(不重要的部分已省略):
#
# Dynamic Shared Object (DSO) Support
# 注意以下幾個 .so 模組的載入先後順序,不可任意調換!
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule dav_module modules/mod_dav.so
LoadModule dav_svn_module    modules/mod_dav_svn.so
LoadModule authz_svn_module modules/mod_authz_svn.so

<Location /svn>
    DAV svn
    SVNParentPath d:/svn
    AuthType Basic
    AuthName "Subversion 檔案庫"

    #存取檔案庫時需要驗證帳號,拿掉這行就不會進行驗證 
    Require valid-user

    #驗證帳號時的帳號、密碼資料
    AuthUserFile "d:/svn/SvnUsers.txt"
    
    #帳號對此檔案庫的檔案目錄存取權限
    AuthzSVNAccessFile "d:/svn/FolderAccessControl.ini"
</Location>
這裡的身份驗證方式是採用 Basic Apache 驗證,其中的 AuthUserFile 所指向的檔案,就是使用者的帳號密碼檔,我是用一個批次檔來建立這些帳號密碼資料。批次檔的內容大概像這樣:
REM John
htpasswd -c -b SvnUsers.txt john john123

REM Vivid
htpasswd -b SvnUsers.txt vivid vivid123

REM Adams
htpasswd -b SvnUsers.txt adams adams123

httpd.conf 中的 <Location> 元素的 AuthzSVNAccessFile 屬性代表目錄存取授權的控制檔。在上面的範例中,檔名是 FolderAccessControl.ini。以下是這個檔案內容的範例:
[groups]
tester = john, vivid
dev = adams, michael

[MyProject:/]
@dev = rw
john = rw
* = r

[MyProject:/TestDoc]
@tester = rw
* = 
其中 [groups] 是用來定義使用者的群組(角色),這裡定義了兩個群組,分別是 dev 和 tester。之後的 [MyProject:/] 和 [MyProject:/TestDoc] 分別代表檔案庫 MyProject 的跟目錄和 TestDoc 子目錄的存取控制。要注意的是,由於我在 httpd.conf 中使用了 SVNParentPath,所以在目錄存取權限控制檔案中明確指定了檔案庫名稱(MyProject)。雖然也可以直接寫路徑名稱,例如 [/] 代表根目錄,[/TestDoc] 代表 TestDoc 子目錄,但這樣一來,如果別的檔案庫中也有相同路徑,就會套用到相同的存取權限,因而導致權限亂掉。

在定義資料夾的存取權限時,可指定特定使用者或群組的讀寫權限。群組名稱是以 '@ ' 字元開頭,以便和使用者名稱區別,例如 @tester = rw。等號右邊就是讀寫權限,r 代表可讀取,w 代表可寫入。如果寫 "* = r",即代表所有人都可以讀取,若等號右邊是空的,則表示既沒有讀取也沒有寫入權限,例如:"* = "(此為預設值,即如果你的權限控制檔內容是空的,任何人都無法讀/寫檔案庫的內容)。

目錄存取權限控制檔的內容如果有修改,會即時生效,不用重啟 Apache。

當使用者欲 commit 檔案時,如果她沒有寫入權限,在 commit 時就會出現錯誤:

error Commit failed (details follow):
error Server sent unexpected return value (403 Forbidden) in response to MKACTIVITY
error request for '/svn/MyProject/!svn/act/adeca4da-d94c-8438-6773709123ec'

還有另一種情況是,明明確定某個使用者有寫入權限,commit 時卻仍然出現上面的錯誤訊息,這很可能是因為當初 checkout 時,URL 裡面的路徑名稱的英文字母大小寫不正確。此時只要 relocate URL,將大小寫修正之後應該就能順利 commit 了。

關於書摘

1/18/2010
近日讀完米蘭昆德拉的《生命中不能承受之輕》,覺得這部小說寫的真好,基於「好東西與好朋友分享」的心理,忍不住做了點書摘,寄給幾位朋友,表示推薦之意。

好用的 .NET 反組譯工具:Reflector.FileDisassembler

1/10/2010
Reflector.FileDisassemblerReflector 的附加元件(add-in),有了它,就可以把整個 DLL 組件的所有類別一次反組譯成各個對應的類別檔,還挺好用的。

使用方法:
  1. Reflector.FileDisassembler 壓縮檔解開。我是把解開後的檔案放在 Reflector 程式所在目錄下的 Addins 資料夾。
  2. 開啟 Reflector,點 View > Add-ins > Add,然後選擇 Reflector.FileDisassembler.dll。選好之後關閉 Add-ins 視窗。
  3. 將欲反組譯的 DLL 加入 Reflector,並選取該 DLL。
  4. 點 Tools > Fils Disassembler,接著選擇原始碼要輸出到哪個資料夾,然後按 Generate 鈕,就可以產生 DLL 檔案中所有類別的原始碼。
參考畫面:



反組譯過程中,如果出現訊息視窗,告訴你找不到某些參考的 DLL,可按 Skip 忽略之。反組譯完成之後,開啟 Visual Stduio,建立一個新的 Class Library 專案,再將反組譯出來的原始碼加入專案,就可以進行編譯了。

不過,反組譯產生的原始碼不見得能順利通過編譯。我在反組譯成 C# 程式碼時,至少碰到下列問題:
  1. 有些變數名稱會帶 $ 符號,以至於無法編譯。
  2. 區域變數名稱重複宣告。
  3. 函式的引數型別為列舉常數,可是呼叫時卻直接傳入整數。
還好這些問題並不嚴重,稍微改一下程式碼就能解決。

另一款類似的工具 FileGenerator For Reflector 雖然連專案檔都可以幫你產生,但我試的結果是無法順利產生原始碼,每次都卡在找不到其他參考的 DLL 檔案。

ps. 對於經過混淆器「攪拌」過的 DLL,就不用費神反組譯了,除非裡面真有什麼非取得不可的寶物,值得花上幾天甚至幾星期的時間去鑽研。

《地下室手記》:宅男囈語

1/09/2010

書名:地下室手記
作者:杜斯妥也夫斯基
譯者:孟祥森
出版社:印刻
出版日期:2003 年 1 月

此譯本係依據 Constance Garnett 的英譯本,近幾年並沒有出比較新的譯本,有些用詞讀起來不太習慣。例如,當我看到「謝謝高特」時,還以為是某個姓高的人,再看到後面說「全能的高特」,才意會過來,這姓高的,便是 God。

這本書一個有趣的地方,就是神經質的獨白和假想的對話,以下摘錄部分內容。

書摘:

各位先生,我請你們找個時間去聽聽十九世紀有教養的人牙痛時發出的呻吟,聽聽在牙痛的第二天開始發出的呻吟。意即是說,不像第一天那樣,不僅是因為牙痛而發出......他的呻吟骯髒可厭的帶著惡意,並且日日夜夜繼續下去。他自己當然知道這種呻吟對自己毫無益處,他比任何人都清楚他是在毫無道理的折磨別人和他自己。他的家人,完全帶著一種厭惡在聽他,他們一點也不相信他真正需要這種呻吟,他們心裡都知道他可以用另一種不同的方式,更為單純的,不要尖叫,不要揮拳踢腿;他們都知道他現在這種呻吟僅僅是為了取樂自己,是出於情緒不良,是出於惡意。(p.19)

註:尊貴的上流社會人士,牙痛時也是一樣的哀嚎,甚至唉得更難聽。有些已經不是單純因為疼痛而哀嚎,其中帶有刻意(惡意?)的成分。

  「哈,哈,但是你知道實際上根本沒有甚麼叫做選擇的東西,不管你怎麼說」你會咯格笑著打斷我。「科學對於人的分析已經到達如此的程度,以致我們老早知道所謂選擇和自由意志僅不過是--」

  住口!先生,這個話讓我自己說。 (p.36)

註:叫自己想像的人住口 :)

我寫它的目的是什麼?如果我不是為了給別人看,我何必把這些偶然的事情寫在紙上,而不僅僅是心裡想想就算了?

  完全對;但是,寫在紙上比較神氣。寫在紙上有一個重要的意義:我可以評論一下自己,我可以改善我的文體。此外,由於把它寫出來,我或許實際上可以使自己輕鬆一下。(p.56)

由於我無限制的虛榮心,由於我為自己所訂下的過高的標準,我時常以極不滿意的眼光看待自己,幾乎已經到達厭惡的程度,於是內心裡我認為每個人對我都有這種感覺。 (p.60)

一個有教養、莊重的人不可能不給自己定下可怕的崇高標準,有些時候不可能不鄙視自己甚至憎恨自己。然而不論我鄙視他們或覺得他們比我強,每次我遇到任何人,總是把眼睛低下來。我甚至做過好多次實驗,看看我能不能面對面直視著別人,但我總是第一個把眼睛低下來。這幾乎使我煩惱得神經錯亂。......我根本是個病態敏感的人--像我們這個時代的人必然會成為這個樣子。(p.60-61)

註:宅男必練基本功:兩眼直視對方,看誰先躲開視線。

絕大部分時間我都留在家裡,看書。我試圖用外來的力量窒息一切不斷在我心中滋擾的東西。而我所具有的唯一方法就是讀書。當然,讀書給我很大的幫助--使我激動、快樂或痛苦。但有些時候它使我感到可怕的厭倦。那時候我甚麼都不想,只想活動活動,於是,我投入黑暗的,地下室的,最可厭最卑下的罪惡之中。我那不幸的熱情是銳利的、惱人的,是由我不斷的、病態的易怒性格產生出來的。......除了讀書之外我沒有任何消遣;這是說,我週遭的一切沒有一件東西使我尊敬,沒有一件東西吸引我。同樣,我被沮喪壓得透不過氣來;我神經質的渴望衝突和矛盾,因此我喜歡罪惡。我說這話並不是要證明自己有道理......不,我在說謊......我就是要證明自己有道理。我說這話完全是為了對自己有好處,先生,我不要說謊。我發誓不說謊。

  於是,到了晚上,我偷偷摸摸的,膽卻的,孤獨的縱情在那種骯髒的罪惡中,心裡從未離開過羞恥感;這種羞恥感即使在最可憎的時刻都不曾離開我,以致使我對它咒罵。在那個時候我心裡就早有了地下室,我極端懼怕被人闖見,被人認出來。 (p.66)

註:每個人心中都有一座斷背山地下室?

有一個軍官推我......我可以原諒別人用拳頭揍我,卻絕不能原諒把我推開而竟然沒有注意到我。....請你不要以為我躲避那個軍官是出於懦弱:我內心裡永遠不曾懦弱過,雖然我的行為總是使我成為一個懦夫。你別忙著笑--我可以保證能夠向你解釋。

  ......我躲開他並不是由於懦弱,而是由於無限的虛榮心。......我害怕的是在場的每個人......當我開始反抗並用斯文的語言爭辯的時候,他們都要開始嘲笑我,不瞭解我。

註:寧願被揍也不要被人忽視,欲找人尋仇把面子討回來,卻又怕被其他人嘲笑。下文有點長,故未摘錄,大意是他還是老想著討回面子,於是跟蹤那軍官好幾次,要查他的底細和幹過的齷齪事,最後還寫了封非常漂亮的決鬥信,向那名軍官下戰帖。結果這封信並未寄出,而他也因為沒有寄出這封信而感謝上帝,因為,每當他想到把信送給軍官的後果時,他就感到「有一股寒流通過背脊」,同時宣稱「我用最簡單的方式為自己復了仇,這完全是天才手法!」呵,還真天才。

有些假日,我經常沿著涅夫斯基大道有陽光的一邊散步......實際上不能說是散步,因為總有這麼多不愉快的屈辱與怨恨發生;但無疑地,這正是我想要的。我總是穿著最不像樣的服裝沿路蠕動,像一條鱔魚,隨時給將軍,騎兵軍官或女士們讓路,在這時候我心裡常常感到絞痛。......在全世界的眼睛裡我只是一隻蒼蠅,一隻骯髒的、可厭的蒼蠅。--當然,我比所有這些人都更聰明,心靈有更高度的發展,情感更為敏銳......為什麼我要去涅夫斯基?我不知道。我只覺得只要有機會我就被吸到那裡去。

註:精神被虐狂?自認比別人更聰明、高尚,又覺得別人都瞧不起自己,可是卻又不由自主的老是往人群裡擠,讓自己感受那精神上的屈辱與折磨。另外,這用鱔魚來形容一個人猥瑣的行走模樣,在《高老頭》裡面也有:「胖子西爾維立即上來報告女主人,說有個漂亮得不像良家婦女的姑娘,裝扮得神仙似的,穿著一雙毫無灰土的薄底呢靴,像鰻魚一樣從街上一直溜進廚房,問高里奧先生的房間在哪兒。」真是生動有趣的比喻。

技術提供:Blogger.