詳解ASP.NET MVC入門級實例教程
對于ASP.NET MVC,很多開發者持歡迎的態度。不過在吸引了更多人目光的同時,我們也應該看到有很多開發人還不太了解ASP.NET MVC。本文的作用在于給大家一個簡單易懂的ASP.NET MVC教程。
ASP.NET MVC 1.0發布有段兒時間了,剛發布不久的時候試用了一下,做了一個簡單的BBS,現在總結一些經驗和小技巧和大家分享。網上關于ASP.NET MVC的系列教程有好幾個,所以就不從頭開始介紹了,結尾處給大家推薦了幾個鏈接,需要的話可以從頭系統的看看。
1、ASP.NET MVC介紹及與ASP.NET WebForm的區別
#T#
剛開始為了搞清楚ASP.NET MVC到底值不值得用,翻來覆去想了一個多禮拜,看了好多資料和評論,***決定還是值得一用。MVC不是一個簡單的設計模式,更像一種架構模式,或者一種思想,剛開始一聽MVC想到的就是模板引擎,NVelocity,StringTempleate等,但感覺如果只是為了用模板這種獨立的前臺設計方式,沒必要用ASP.NET MVC,大多數情況用Repeaterk控件和自定義控件兒就能做到,而且ASPX頁面上本來就可以寫c#代碼,一些比較復雜的界面表現邏輯用普通的WebForm也能實現,其實ASP.NET MVC的VIEW部分默認用的還是aspx的解析器。ASP.NET MVC的View部分讓你寫一些大型的,布局復雜的網站更方便,更底層,更直接,很受對css,js很熟悉的開發者的歡迎。
當你理解了MVC的思想后,會發現ASP.NET MVC的好處真正在于Controller和Action,你寫一段代碼能很明確的知道是在處理什么請求,畢竟web程序處理的是一個一個的http請求,不像windows桌面程序,基于事件驅動更直觀。ASP.NET MVC的Controller讓你寫一些web api或者rest風格的接口很方便(以前可能要用HttpHandler來做),這些Controller只負責提供數據(具體的ActionResult類,如JsonResult,JavascriptResult等)給使用者,比如一個Ajax調用,或者View層。
至于Model層,我看網上大多數人是用LINQ TO SQL實現的,畢竟使用起來很簡單,設計好表,用LINQ 設計器往vs.net里一拖就能用了。而且本身就是強類型的,再在自動生成的代碼上加一些分部方法,就可以實現數據的有效性驗證等。還有就是對LINQ做的Model進行數據持久化和查詢的時候更方便,直接用DbContext一個類,增刪改查全能搞定。
有得就有舍,ASP.NET MVC雖然提供了先進的思想和一些便利,但ASP.NET以前的一些東西不能用了,比如以前自己寫的一些服務器控件兒不能用了,WebPart,皮膚,各種數據綁定控件等都不能用了,但Master頁還能用,Asp.net Ajax control toolkit(服務端)也不能用了,但asp.net ajax library(客戶端js庫)還能繼續使用,基于頁面和目錄的授權不能用了(因為現在沒頁面,只有view了),但MemberShip和Forms身份驗證還是支持的。標準WebForm的生命周期變了,好些事件沒了,現在你可以寫一些攔截器(Action攔截器、Result攔截器和Exception攔截器)來影響請求的處理過程,還有一些區別,總之失去的東西,都有變通的方法能找吧回來。
2、linq to sql如何獲取插入語句產生的標識列的值?
其實很簡單,把對象插入數據庫后,直接取值就行了,如下BBSPost是一個實體類,其中PostID在數據庫里是自增列。
- var db = new BBSDbContext(connstr);
- BBSPost post = new BBSPost()
- post.PostUser = User.Identity.Name;
- post.PostTime = DateTime.Now;
- db.BBSPosts.InsertOnSubmit(post);
- db.SubmitChanges();
- int postid = post.PostID; //這里就能取到標識列的值。
3、ASP.NET MVC里在請求提交后如何后維持滾動條位置?
在WebForm里再簡單不過了,在web.config里配置MaintainScrollPositionOnPostBack=true就搞定了,但在MVC里就不行了。我們知道了原理后,可以自己實現,其實就是在提交表單或者滾動條滾動的事件里捕獲當前滾動條的位置,把數值放在一個隱藏域里,提交給服務端,服務端應答后,從隱藏域里取出滾動條的位置,用js操縱滾動條滾動到上次的位置。
我們先在View里寫一個隱藏域,如下
- <%= Html.Hidden("scroll", ViewData["scrool"])%>
然后在處理客戶端請求的action里給ViewData里存儲一下提交上來的值(從FormCollection里?。?。
- public ActionResult reply(BBSPost post, FormCollection coll) {
- ...
- ViewData["scroll"] = coll["scroll"];
- ...
- return View("show_post",posts);
- }
這樣頁面提交后隱藏域里就會保存著提交前滾動條的位置,然后我們在用JQuery寫一些邏輯實現最終的效果。
- <script type="text/javascript">
- $(function() {
- $(document).scroll(function() {
- //在滾動條滾動的時候更新隱藏域里滾動條的位置值,經測試不支持IE8
- $("#scroll").val(document.documentElement.scrollTop);
- });
- $("form").submit(function() {
- //在表單提交的時候更新隱藏域里滾動條的位置值
- $("#scroll").val(document.documentElement.scrollTop);
- return true;
- });
- //在document.load事件里取出隱藏域的值,并設置滾動條的位置
- document.documentElement.scrollTop = $("#scroll").val();
- });
- script>
4、驗證用戶輸入
數據有效性的驗證基本上哪個程序都躲不了,LINQ 和ASP.NET MVC的配合,讓數據驗證的實現也很方便。
LINQ TO SQL設計器自動生成的類是一個分部類,就是半塊兒的類,你可以寫一個分步類,在自動生成的類上加一些擴展的方法,如下我們在LINQ實體類BBSPost上加了一個GetRuleViolations方法,一個IsValid屬性,其中GetRuleViolations方法驗證給實體類賦的值的有效性,用yield關鍵字返回一個枚舉器,這里可以寫你自己的數據有效性驗證邏輯。
IsValid屬性內部調用GetRuleViolations方法,如果返回的枚舉器的Count不是0的話,表示數據有效性驗證不通過。
另外為了方式LINQ TO SQL往數據庫里寫入無效數據,我們給OnValidate分布方法加了兩行代碼,在數據有效性驗證不通過的情況下寫數據庫之前拋出異常。
- public partial class BBSPost {
- public bool IsValid {
- get { return (GetRuleViolations().Count() == 0); }
- }
- public IEnumerable
GetRuleViolations() { - if (String.IsNullOrEmpty(Title))
- yield return new RuleViolation("標題必須輸入", "Title");
- if (String.IsNullOrEmpty(Content))
- yield return new RuleViolation("內容必須輸入", "Content");
- yield break;
- }
- partial void OnValidate(ChangeAction action) {
- if (!IsValid)
- throw new ApplicationException("Rule violations prevent saving");
- }
- }
RuleViolation是一個輔助類,很簡單。
- public class RuleViolation {
- public string ErrorMessage { get; private set; }
- public string PropertyName { get; private set; }
- public RuleViolation(string errorMessage) {
- ErrorMessage = errorMessage;
- }
- public RuleViolation(string errorMessage, string propertyName) {
- ErrorMessage = errorMessage;
- PropertyName = propertyName;
- }
- }
在寫action的時候,捕獲SubmitChanges操作的異常,然后給ModelState里添加自定義驗證邏輯的異常,ModelState會把添加進去的異常傳遞給View層,供View層使用。
- try {
- var db = new BBSDbContext(GlobalHelper.Conn);
- post.PostUser = User.Identity.Name;
- //其它賦值操作
- db.BBSPosts.InsertOnSubmit(post);
- db.SubmitChanges();
- ModelState.Clear();
- }
- catch (Exception ex) {
- ModelState.AddModelErrors(post.GetRuleViolations());
- ModelState.AddModelError("exception", ex);
- }
默認的ModelState沒有AddModelErrors方法,只有AddModelError方法,我們是后來給他加了一個擴展方法,如下
- public static class ModelStateHelpers {
- public static void AddModelErrors(this ModelStateDictionary modelState, IEnumerable
errors) { - foreach (RuleViolation issue in errors) {
- modelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
- }
- }
- }
在View層使用了Html.ValidationMessage方法在合適的位置輸出錯誤描述,如果View呈現的時候ModelState里有錯誤的話,會自動顯示相應的錯誤描述,代碼示例如下。
- <p>
- <label for="Title">
- 標題:label>
- <%= Html.TextBox("Title", null, new { style = "width:700px;" })%>
- <%= Html.ValidationMessage("Title") %>
- p>
- <p>
- <label for="Content">
- 內容:label>
- <%= Html.TextArea("Content", null, new { style = "width:700px;height:100px;" })%>
- <%= Html.ValidationMessage("Content")%>
- p>
5、LINGQ TO SQL的分頁
SQLSERVER 2005有很強悍的分頁函數,LINQ TO SQL對其有很好的支持,IQueryable
- public class PaginatedList
: List { - public int PageIndex { get; private set; }
- public int PageSize { get; private set; }
- public int TotalCount { get; private set; }
- public int TotalPages { get; private set; }
- public PaginatedList(IQueryable
source, int pageIndex, int pageSize) {- PageIndex = pageIndex;
- PageSize = pageSize;
- TotalCount = source.Count();
- TotalPages = (int) Math.Ceiling(TotalCount / (double)PageSize);
- this.AddRange(source.Skip(PageIndex * PageSize).Take(PageSize)); //這句會停止延遲加載,把數據加載到內存里
- }
- public bool HasPreviousPage {
- get {
- return (PageIndex > 0);
- }
- }
- public bool HasNextPage {
- get {
- return (PageIndex+1 < TotalPages);
- }
- }
- }
使用起來很簡單,用LINQ TO SQL得到一個IQueryable后,再用其New一個PaginatedList就表示一個已分頁的數據集了
- var posts = from post in db.BBSPosts
- where post.CategoryID == id && post.ParentID == 0
- orderby post.PostID descending
- select post;
- const int pageSize = 10;
- var pagePosts = new PaginatedList
(posts, page ?? 0, pageSize); - return View(pagePosts);
posts是用linq to sql生成的一個IQueryable
- <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
- Inherits="System.Web.Mvc.ViewPage
>" %>
頁面上要顯示上一頁,下一頁的鏈接,寫起來也很簡單
- <div class="pagination">
- <% if (Model.HasPreviousPage) { %>
- <%= Html.RouteLink("上一頁",
- "Default",
- new { page=(Model.PageIndex-1) }) %>
- <% } %>
- <% if (Model.HasNextPage) { %>
- <%= Html.RouteLink("下一頁",
- "Default",
- new { page = (Model.PageIndex + 1) })%>
- <% } %>
- div>
6、查看LINQ TO SQL生成的SQL語句?
有人懷疑LINQ TO SQL的性能問題,認為它生成的語句不靠譜,其實它生成的語句都是參數化查詢,一般的基于主鍵或者索引列的查詢及大多數更新操作性能應該不會比手寫SQL差,如果還是不放心的話,可以把LINQ TO SQL生成的SQL打印出來,以避免性能查的語句產生。
如下代碼
- var db = new BBSDbContext(conn);
- var posts = from post in db.BBSPosts
- where post.CategoryID == 1 && post.ParentID == 0
- orderby post.PostID descending
- select new {post.PostID, post.Title, post.Content};
- db.Log = Response.Output; //跟蹤自動生成的SQL語句
- rpt1.DataSource = posts;
- rpt1.DataBind(); //只有真正執行使用數據的語句時,SQL查詢才會執行,在這之前語句只是語句,自動延遲執行的。
會在頁面上看到LINQ TO SQL生成的SQL語句
- SELECT [t0].[PostID], [t0].[Title], [t0].[Content] FROM [dbo].[bbs_Post] AS [t0]
- WHERE ([t0].[CategoryID] = @p0)
- AND ([t0].[ParentID] = @p1)
- ORDER BY [t0].[PostID] DESC -- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1] -- @p1: Input Int (Size = 0; Prec = 0; Scale = 0) [0] -- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1
如果改成如下分頁方式
- var db = new BBSDbContext(conn);
- var posts = from post in db.BBSPosts
- where post.CategoryID == 1 && post.ParentID == 0
- orderby post.PostID descending
- select post;
- db.Log = Response.Output;
- rpt1.DataSource = posts.Skip(1 * 5).Take(5);
- rpt1.DataBind();
會輸出如下SQL
- SELECT [t1].[CategoryID], [t1].[PostID], [t1].[ParentID], [t1].[Title], [t1].[Content], [t1].[PostUser], [t1].[PostTime]
- FROM ( SELECT ROW_NUMBER() OVER (ORDER BY [t0].[PostID] DESC)
- AS [ROW_NUMBER], [t0].[CategoryID], [t0].[PostID], [t0].[ParentID], [t0].[Title], [t0].[Content], [t0].[PostUser], [t0].[PostTime]
- FROM [dbo].[bbs_Post] AS [t0] WHERE ([t0].[CategoryID] = @p0) AND ([t0].[ParentID] = @p1) ) AS [t1]
- WHERE [t1].[ROW_NUMBER] BETWEEN @p2 + 1 AND @p2 + @p3
- ORDER BY [t1].[ROW_NUMBER] -- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1] -- @p1: Input Int (Size = 0; Prec = 0; Scale = 0)
- [0] -- @p2: Input Int (Size = 0; Prec = 0; Scale = 0) [5] -- @p3: Input Int (Size = 0; Prec = 0; Scale = 0) [5] -- Context: SqlProvider(Sql2005)
- Model: AttributedMetaModel Build: 3.5.30729.1
可以看到這些查詢用的都是參數化查詢,不是拼SQL,而且還用了ROW_NUMBER函數,LINQ TO SQL還是比較了解SQLSERVER的。
7、設置某個Action需要身份認證?
因為基于頁面的授權不能使用了,我們只好對某個Action進行授權,比如要回復帖子的話需要進行登錄,那么就在reply的action上加上需要身份驗證的屬性修飾,如下
- [AcceptVerbs(HttpVerbs.Post), Authorize]
- ?public ActionResult reply(BBSPost post, FormCollection coll) {
這種方式是以AOP注入方式實現的,更多的攔截器示例,或者想寫自己的攔截器可以google些資料看看。
8、如何把用戶提交的表單數據轉成強類型
我們都知道網頁上提交的數據包括Form里和QueryString,在服務端取出來都是string類型的,在asp時代,我們需要一個一個的處理參數,在ASP.NET MVC里就很方便了,比如你有一個BBSPost類,有Title和Content和CategoryId 3個屬性,而表單上有兩個文本框Title和Content,地址欄參數里有一個CategoryId,你可以直接在action里取到一個BBSPost類,而且屬性都給你填充好了,不用你取出一個一個的string再new一個BBSPost類,再轉類型賦值等一系列操作了,如下
- public ActionResult reply(BBSPost post, FormCollection coll) {}
***個參數會自動填充成強類型,第二個參數可以取出原始的表單提交的數據。如果你想了解更多的表單數據和強類型數據的綁定,細節,可以查查DefaultModelBinder是如何工作的。
9、給HTMLHelper加擴展方法。
ASP.NET MVC里的一個***實踐就是給HTMLHelper加一些常用的擴展方法以供View層方便使用,不要到處寫幫助類,比如在顯示帖子的時候要格式化帖子成HTML格式,我們寫了如下的擴展方法
- public static class HtmlHelperExtension {
- public static string Text2Html(this HtmlHelper helper, string input) {
- input = input.Replace(" ", " ");
- input = input.Replace("\r\n", "
");- input = input.Replace("\t", " ");
- return input;
- }
- }
在view上先引用擴展方法所在的命名空間
- <%@ Import Namespace="SimpleBBS.Helpers" %>
然后擴展方法就能使用了,如下
- <%= Html.Text2Html(Html.Encode(item.Content)) %>
10、如何定位腳本和CSS的位置
如果我們目錄級別特別多,把腳本,樣式表等放在一個固定的目錄后,在特定的子目錄訪問這些資源路徑可能不一致,在WebForm的時候只有服務端控件才能使用~語法,無論是部署在站點根目錄還是虛擬目錄,~都能表示應用的根目錄,在ASP.NET MVC里我們可以用Url.Content來使用~,如下
- <script src="<%=Url.Content("~/Scripts/jquery-1.3.2.min.js")%>" type="text/javascript">script>
原文標題:蛙蛙推薦:ASP.NET MVC學習筆記
鏈接:http://www.cnblogs.com/onlytiancai/archive/2009/10/31/asp_net_mvc_study.html