October 29, 2010

使用AutoMapper簡化Data Transfer Object與Business Entity的對應程式碼

在一個layered system中,我們常會使用Data Transfer Object (DTO)在layer與layer間傳遞資料,如Presentation Layer (PL)與Business Logic Layer (BLL)。至於什麼是DTO,可參考筆者另一篇文章Data Transfer Object使用心得及時機,在此也列出Martin Fowler在其書"Patterns of Enterprise Application Architecture"中對DTO的定義,有與趣的朋友可以去google一下或翻一下這本書(在Chapter 15)
An object that carries data between processes in order to reduce the number of method calls.
由於資料傳遞透過DTO,我們將會遇到兩種需要做資料對應的情況
  1. DTO由PL傳遞至Service Layer (SL),於SL將DTO對應至Business Entity後,再把Business Entity送至Data Access Layer (DAL)處理
  2. 資料由DAL取出給BLL後,於SL將Business Entity對應至DTO後回傳至PL
基於以上兩種情況,我們常會看到類似下列的程式碼 (第二種資料對應類型)
UserInfo user = GetUserInfoFromRepository("pete");
UserInfoDto dto = new UserInfoDto();
dto.UserId = user.UserId;
dto.UserAccount = user.UserAccount;
dto.UserPassword = user.UserPassword;
dto.Role = user.Role;
dto.UserName = user.UserName;
return dto;
偏偏這種資料對應不會只存在於一個頁面中,相同的程式碼要copy/paste不僅累人,萬一要是眼花少copy到一行系統不能work搞不好還得debug半天

如果說有個工具或元件能幫我們做這檔事不是樂得輕鬆嗎? 在此推薦一個好用元件AutoMapper
有了它,在最理想的情況下只要一行程式碼(好吧,外加一行建立Mapper物件的程式碼) 就可以輕輕鬆鬆達到DTO與Business Entity的對應
以下為一簡單的範例程式碼
Mapper.CreateMap<UserInfo, UserInfoDto>()
UserInfoDto dto = Mapper.Map<UserInfo, UserInfoDto>(GetUserInfoFromRepository("pete"));
據官方說法,重覆的對應,其Mapper.CreateMap只要宣告一次就可以了,並建議在把它放在Global.asax裡在應用程式啟動時就建立Mapping。當然它還有很多更進階的用法以及資料對應時會產生的一些問題,筆者在此就不多做說明了。

October 26, 2010

使用Command Line方式在IIS6佈署網站

年底到,一些要結案的系統也要開始寫安裝手冊。手邊有一個Web AP,手動佈署不複雜,但要寫成詳細點的step-by-step安裝步驟卻也是得cut好幾張圖。由於這個系統沒有複雜到一定要用IIS的UI管理工具來設定,所以就試試把它寫成一支batch檔,以command line的方式來執行系統安裝,省去了一些安裝步驟的說明,順便學學adsutil.vbs這支好用的command-line script。

以下引述MSDN對於adsutil.vbs的解釋
Adsutil.vbs is an IIS administration utility that uses Microsoft Visual Basic Scripting Edition (VBScript) with Active Directory Service Interfaces (ADSI) to manipulate the IIS configuration. This script should be run using CScript, which is installed with Windows Script Host.

簡單地說,它是一個IIS的管理工具,透過CScript指令可以command line的方式對IIS做設定。adsutil.vbs的位置在C:\Inetpub\AdminScripts下,以下為我用來佈署用的的command line

@echo off 
echo 開始安裝... 
echo 1.設定首頁 
cscript /nologo C:\Inetpub\AdminScripts\adsutil.vbs SET W3SVC/1/ROOT/DefaultDoc Default.aspx

echo 2.設定預設網站路徑 
cscript /nologo C:\Inetpub\AdminScripts\adsutil.vbs SET W3SVC/1/ROOT/Path C:\Inetpub\wwwroot\MyWebApp 

echo 3.設定.NET Framework使用版本 
%windir%\microsoft.net\framework\v2.0.50727\aspnet_regiis -s W3SVC/1/ROOT 

echo 安裝完成 
pause

上述的command line主要有三個步驟,設定預設網站首頁、路徑及將.NET Framework使用版本設為v2.0.50727

W3SVC/1/ROOT為預設的網站,由於我只需要安裝一個Web AP在系統上,所以我直接使用預設的網站。如果需要額外新增網站或虛擬目錄來設定,可以參考iisweb.vbsiisvdir.vbs兩支command-line script

如果想知道adsutil.vbs提供哪些參數可以設定,可以用
cscript /nologo C:\Inetpub\AdminScripts\adsutil.vbs ENUM_ALL W3SVC/1/ROOT來列出可設定的參數

因為參數不少,也可以下列command line輸出成文字檔
cscript /nologo C:\Inetpub\AdminScripts\adsutil.vbs ENUM_ALL W3SVC/1/ROOT > D:\param.txt

以下為執行結果

October 9, 2010

呼叫Web Service出現HTTP狀態417:Expectation Failed的解決方法

最近在呼叫部署在客戶端的Web Service時會出現以下錯誤訊息

研究了一下HTTP 1.1的規格書,這個錯誤出現於當server(或proxy server)無法辨識client送出的Expect標頭
解決方法可以在Web.config的<configuration>區段中加入以下設定
<system.net>
  <settings>
    <servicePointManager expect100Continue="false" />
  </settings>
</system.net>
或於呼叫Web Service前加入以下程式碼
System.Net.ServicePointManager.Expect100Continue = false;

參考
http://tools.ietf.org/html/rfc2616#section-10.4.18
http://tools.ietf.org/html/rfc2616#section-14.20
http://www.cnblogs.com/yukaizhao/archive/2009/07/15/httpwebrequest_return_417_expectation_failed.html
http://social.msdn.microsoft.com/Forums/en-US/devdocs/thread/60cd6e6a-4157-4811-8ed3-1e46f9022ea8

October 8, 2010

使用WebClient讀取資料出現403錯誤訊息

今天在測試讀取客戶端某支xml時,透過瀏覽器存取都沒問題,可以正常顯示資料。但透過WebClient讀取卻回傳了HTTP 403的錯誤訊息,猜想可能是被server端的防火牆給擋掉了,瀏覽器能讀取應該是有送出防火牆認可的HTTP header,所以應該只要能模擬瀏覽器送出的header應該就可以解決這個問題了。

去古哥爬了一下後發現有網友也有相同問題,解決的方式可以在WebClient讀取資料前新增模擬瀏覽器的header如下
WebClient client = new WebClient();
client.Headers.Add(HttpRequestHeader.UserAgent, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;");