ASP.NET MVC 1.0剛新鮮出爐,頭就讓我用它做項目.說實話,一開始心裡沒底.雖然ASP.NET Web Form也說不上很熟,但好歹做過一陣,大致心裡還是有底的.MVC除了個基本概念,具體的實現就一無所知了.不過心想學點新東西總是好的,大概也能對付著做出來,於是就試著用它做了起來.
第一件事是到ASP.NET MVC的官方首頁下載了庫,安裝沒什麼問題.然後是找參考資料.由於它實在太新,電子書極少,好不容易只找到一本ASP.NET MVC In Action的預覽版,一本ASP.NET MVC 1.0 Quickly, Professional Asp.net MVC Framework的第一章.先看ASP.NET MVC In Action,覺得不好懂,又看ASP.NET MVC 1.0 Quickly,開始覺得很好,脈絡清晰,內容實用,但做了幾天又覺得內容不夠豐富,最後那本是一個執行個體,也不夠實用.這樣,很多東西只能靠google找答案.
和這個鬼東西搏鬥了幾天,總算略有開竅,寫點體會:
1.MVC的基本概念:
主要目的是實現資料和資料的表現分離,減少耦合度,使程式更加靈活.視圖(View)需要資料時,向控制器(Controller)發出請求,控制器通過模型(Model)獲得資料,再設定視圖的呈現形式.
2.ASP.NET MVC和原來的ASP.NET Web Form的主要區別:
原來的ASP.NET Web Form,基本上所有事件都發生在服務端,用戶端通過Postback呼叫服務端,服務端處理後再把結果發給用戶端.有很多服務端控制項,編程模式和Win Form有很多類似之處,如事件驅動,控制項豐富等.
而ASP.NET MVC則需要使用者自己處理Postback,或者說,通過對Controller編程,來控制Postback,自然,這樣靈活性增加了,也給了用戶端更多的發揮餘地.但是,由於取消了服務端控制項,編程就不如Web Form方便,比如,沒有了日曆控制項,只能自己用Javascript做一個;由於控制器所產生的視圖在運行時(而不是設計時)才能看到,所見即所得 (WYSIWYG)的介面設計沒有了.
還有一個很大的不同是ASP.NET MVC引入了路由(Routing)的概念.以前在Web Form裡,一個URL就對應磁碟上一個物理的檔案,而在MVC裡,一個URL對應的是Controller裡一個Action方法的名稱.
3.ASP.NET MVC的基本結構:
1)用戶端:
主要是HTML頁面,雖然也是用aspx尾碼,但沒有web form裡的codebehind,頁面開頭有一句:
< %@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
除了可以用標準的HTML元素和Javascript之外,還可以用HtmlHelper類來產生控制項,如:
< table>
< tr>
< td>
< %= Html.TextBox("txtCode", string.Empty, new {@id="txtCode", style="width:100px"}) %>
上面new後面的括弧裡是TextBox的HTML 屬性.
其實就等於:
< input type="text" name="txtCode" id="txtCode" value='' />
還有一個比較獨特的元素叫ActionLink,作用類似HyperLink,只不過由於ASP.NET MVC裡用到Routing的概念,所以串連指向的不是一個具體的物理檔案,而是Controller類的一個方法.
2)服務端:
一個是Model,負責訪問資料庫,提供資料,這和普通的類區別不是很大.一個是Controller,負責和用戶端的互動,把資料呈現到用戶端,或者當用戶端的顯示出現變化時,調整後台資料,與其保持一致.
4.用戶端和服務端的互動:
1)初始:
一般是在Default.aspx.cs裡初始MVC,如果是用嚮導產生的話,Default.aspx.cs是唯一一個有codebehind和Page_Load事件的檔案:
string originalPath = Request.Path;
HttpContext.Current.RewritePath(Request.ApplicationPath, false);
IHttpHandler httpHandler = new MvcHttpHandler();
httpHandler.ProcessRequest(HttpContext.Current);
HttpContext.Current.RewritePath(originalPath, false);
其他頁面必須在訪問了這個Default.aspx之後才能訪問,不然MVC未初始,將報錯.
2)Routing:
負責處理這個Default.aspx頁面的Controller放在Controllers目錄下的HomeController.cs,內容很簡單:
[ HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
//ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
那麼MVC是如何找到這裡的呢?
首先,在Web.config裡有一個設定:
< add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
這個預設的assembly就是負責routing的.
具體的routing設定,一般放在Global.asax.cs這個檔案:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // 路徑名
"{controller}/{action}/{id}", // 帶參數的路徑樣式
new { controller = "Home", action = "Index", id = "" } // 預設的對應這個路徑的controller和action以及預設參數
);
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
比如http://localhost:2040/car/check 這個路徑,MVC就會到Controller目錄下找CarController.cs這個檔案裡的Check方法,如果找不到,就會轉到HomeController.cs裡的Index方法來處理.
這裡要注意的是:
a.IIS 5.0(如2000和XP裡所裝的)似乎不支援所謂的wildcard mapping,就是說不支援對無尾碼名(如.aspx, .cs)的URL的處理,所以不管怎麼設定,都只能跑到預設的HomeController裡.解決這個問題的方法是定義一個尾碼名,如.mvc,然後在IIS裡,將.mvc和aspnet_isapi.dll關聯起來,然後在Global.asax.cs裡加一個路徑映射:
routes.MapRoute(
"Myroute",
"{controller}.mvc/{action}",
new { controller = "MyController", action = "MyAction" }
);
b.自己加的路徑一定要放在預設的路徑前.因為MVC這鬼東西找路徑時是按代碼的先後順序來找的,如果把預設的放在前面,就會直接匹配,自己定義的路徑就匹配不上了.
3)用戶端和服務端互動的流程:
發出一個URL請求後,MVC根據設好的route找到處理這個route的方法,就是action,對要顯示的介面和資料做些設定後,返回一個View,就是頁面:
public ActionResult Index()
{
//ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
這裡的View(),就是一個HTML頁面.如果沒有參數,MVC就去找和方法同名的頁面,如在這個例子裡,就到Views/Home目錄下去找Index.aspx這個檔案.如果指定了參數,如return View("mypage");則到Views/MyPage目錄下(如果有這個目錄的話)找MyPage.aspx,如果沒有,再到Home目錄下去找MyPage.aspx.這是一種形式.
另外一種形式,如果想設定頁面裡的某些元素的值,比如設定某個TextBox的值,主要有兩種方式:
一種是通過ViewDataDictionary傳值:
public ActionResult Index()
{
//ViewData["Message"] = "Welcome to ASP.NET MVC!";
ViewData["txtCode"] = "test";
return View();
}
ViewDataDictionary是一個容器,什麼都可以往裡面放,在用戶端取出裡面的值,很簡單,如上面定義的那個TextBox:
< %= Html.TextBox("txtCode", string.Empty, new {@id="txtCode", style="width:100px"}) %>, 如果把初始值string.Empty改成 ViewData["txtCode"],用戶端就會顯示出"test".
需要注意的是ViewData是單向傳遞的,就是說只能從服務端傳給用戶端.
還有一種方法,是利用Model.Model是一個不可見的在用戶端的資料對象.前面提到,MVC的View頁面開頭有一句:
< %@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>,這裡的ViewPage沒有指定對應頁面的Model對象是什麼類型,為了使用方便,可以這樣指定Model的資料類型:
< %@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage< MyObjectType>" %>
在上面的Index方法裡,加上:
public ActionResult Index()
{
//ViewData["Message"] = "Welcome to ASP.NET MVC!";
MyObjectType myType = new MyObjectType();
myType.TxtCode = "test";
ViewData["txtCode"] = "test";
return View("Index", myType);
}
然後將用戶端的語句改寫成:
< %= Html.TextBox("txtCode", Model.TxtCode, new {@id="txtCode", style="width:100px"}) %>,
也能顯示出"test".
更好的做法是:
< %= Html.TextBox("myType.TxtCode", Model.TxtCode, new {@id="txtCode", style="width:100px"}) %>.
服務端向用戶端傳資料,主要就用ViewData和Model這兩種形式.
至於用戶端向服務端發資料就比較簡單,就是通過submit表單,如上面的例子,服務端用Request["txtCode"]或者Request.Form["txtCode"]來取值.
如果用上面最後一種寫法,前台的HTML元素和Model已經完全綁定起來,直接取myType.TxtCode就可以了.
5.用戶端和服務端的綁定
用戶端實際上是先產生一個Model對象的執行個體,給它的屬性賦好值,然後再發到服務端.那麼各種HTML元素是如何與服務端綁定起來呢?
最簡單的是TextBox,服務端定義一個string類型的屬性(property),用戶端用modeltype.property的形式就綁定起來了,如上面的例子.
CheckBox也類似,先在服務端定義一個boolean類型的屬性:
public class MyModelType
{
public bool IsChecked
{
get; set;
}
}
用戶端:
< %= Html.CheckBox("myType.IsChecked",Model.IsChecked) %>
麻煩一點的是radioButton,服務端要定義一個enum類型,一個該類型的屬性,一個bool類型的屬性:
public class MyModelType
{
public enum EnumCheck
{
Yes,
No
}
}
public EnumCheck CheckIndicator
{
get; set;
}
public bool IsYesChecked
{
get; set;
}
用戶端:
< %= Html.RadioButton("myType.CheckIndicator", "Yes", Model.IsYesChecked) %>
< %= Html.RadioButton("myType.CheckIndicator", "No", !Modle.IsYesChecked) %>
這裡定義一個IsYesChecked,主要是為了初始radioButton的狀態.
用戶端提交表單後,服務端可以這樣檢查哪個radiobutton被選中:
if (myType.CheckIndicator == MyModelType.EnumCheck.Yes)
{//選中了Yes
}
最麻煩的是dropdownlist,在服務端:
public class MyModelType
{
public SelectList MyDropDown
{
get; set;
}
}
public ActionResult Index()
{
MyModelType myType = new MyModelType();
List list = new List();
list.Add(new SelectListItem{Text = "test1", Value = "value1"});
list.Add(new SelectListItem{Text = "test2", Value = "value2"});
list.Add(new SelectListItem{Text = "test3", Value = "value3"});
myType.MyDropDown = new SelectList(list, "Value", "Text", list[0].Value);//最後一個參數是指定dropDownList中被選中的值
return View("Index", myType);
}
在用戶端:
< %= Html.DropDownList("dpl", Model.MyDropDown) %>
這裡,好象不能用 < %= Html.DropDownList("myType.MyDropDown", Model.MyDropDown) %>,否則遞交表單時會提示SelectList沒有無參數的構造方法的錯誤.
6.其他體會:
目前還沒有用到Grid,感覺可能用MVC來做比較麻煩,和GridView不可同日而語.
JQuery,Linq to SQL等新鮮玩意還沒有嘗試.
感覺這個東西最大的不足就是用戶端還缺少好用的控制項支援,HtmlHelper實在太簡陋,比如連個calendar控制項都沒有.
Javascript有了更多的施展餘地.確實很多簡單的功能沒必要postback到服務端,影響效能.
搞MVC這個鬼東西,需要對HTML有更深入的瞭解.