ASP.NET MVC學習筆記

來源:互聯網
上載者:User

網上關於ASP.NET MVC的系列教程有好幾個,所以就不從頭開始介紹了,結尾處給大家推薦了幾個連結,需要的話可以從頭系統的看看。

1、ASP.NET MVC介紹及與ASP.NET WebForm的區別

剛開始為了搞清楚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<RuleViolation> 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<RuleViolation> 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<T>的Skip和Take方法最終就產生分頁的SQL,先寫如下的一個協助類(取自NerdDinner),這個類的屬性很簡單,見名知意,就不介紹了。 複製代碼 代碼如下:public class PaginatedList<T> : List<T> {

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<T> 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<BBSPost>(posts, page ?? 0, pageSize);
return View(pagePosts);

posts是用linq to sql產生的一個IQueryable<BBSPost>對象,這時候SQL語句並沒有執行,會順延強制,再new一個PaginatedList<BBSPost>的時候會對其產生的SQL語句進行修改,最後把pagePosts傳遞給view層用就行了,View層我們使用了強型別的View,如下
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<SimpleBBS.Helpers.PaginatedList<SimpleBBS.Models.BBSPost>>" %>
頁面上要顯示上一頁,下一頁的連結,寫起來也很簡單 複製代碼 代碼如下:<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", "<br />");
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>

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.