ASP.NET MVC学习笔记

网上关于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>

(0)

相关推荐

  • 值得收藏的asp.net基础学习笔记

    值得收藏的asp.net基础学习笔记,分享给大家. 1.概论 浏览器-服务器 B/S 浏览的 浏览器和服务器之间的交互,形成上网B/S模式 对于HTML传到服务器  交给服务器软件(IIS)  服务器软件直接读取静态页面代码,然后返回浏览器 对于ASPX传达服务器  交给服务器软件(IIS)   IIS发现自己处理不了aspx的文件,就去映射表根据后缀名里找到响应的处理程序(isapi,服务器扩展程序) 问题:IIS如何调用可扩展程序? 答:可扩展程序首先就是按照IIS提供的借口实现代码,所以I

  • 那些年,我还在学asp.net(一) 学习笔记

    那些年到此,基本学习了前端的基本知识,那些年的第四课就是asp.net,当然那时看了很多教程,比如说:天轰穿,当然天轰穿说得比较多,如面向对象,C#知识,由于当时上过C++,所以就没有看这些,直接从asp.net开始,主要是学习一下asp.net用到的一些基本控件,后来感觉差不多了就不再看了,开始学习asp.net的数据教程(scott教程),那些年慢慢的走向web开发了. 先还是看一下asp.net中的一些基本的控件的使用. 一.GridView控件的使用 1.GridView 是asp.ne

  • Asp.net回调技术Callback学习笔记

    .aspx: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xht

  • 那些年,我还在学习asp.net(二) 学习笔记

    那些年,我还在学习asp.net(二) 那些年觉得看视频是很轻松的了解一个东西,但是这样的不足就是感觉太慢了,没有看书来得快,所以在有了一些了解后,还得看点书,也许书上的不一定好,但书上会把每一个应该说到的地方说到,好有个初步的认识.比如说.NET平台.组件等知识的介绍,所以,那些年就开始了asp.net方面的进一步学习. 一.学习asp.net的准备知识 正如我前面那些年所学习的,在开始之前得对它们有一定的认识: 1.  html语言的基本使用 2.  CSS+DIV的学习,撑握基本的布局 3

  • ASP.NET预备知识学习笔记

    .NET FrameWork框架 是一套应用程序开发框架,主要目的提供一个开发模型. 主要的两个组件:     公共语言运行时(Common Language Runtime)(CLR): 提供内存管理.线程管理和远程处理等核心服务,并且还强制实施严格的安全类型,提高代码的安全性和可靠想. .NET  Framework类库: 与CLR紧密集成,可以使用它开发多种应用程序和服务.主要包括控制台应用程序.Windows窗体应用程序.WindowsPresentationFoundation(WPF

  • ASP.NET与MySQL数据库简明图示入门教程

    作者: CRYSTAL编译 在ASP时代,如果我们要建立一个数据库驱动的web站点,那么你可以选择环很多钱的微软SQL SERVER数据库或者选择要花很多时间来寻找达到性能和稳定性统一的ACCESS数据库,但在.NET时代你有另一种选择,那就是:MySQL数据库 什么是MySQL数据库? MySQL数据库是一种开放源代码的数据库,通过获得授权来保持源代码的官方支持,同时可以自由修改源代码,目前许多公司和组织都采用了这种数据库.对此详细信息您可以访问MySQL的官方站点. 第一步 下载和安装 与大

  • asp.net Linq to Xml学习笔记

    加上之前学习过Linq to Entity,因此学习起来也比较随心应手. 以下是项目中某个底层的代码,记下做个备忘,如果能给新手学习Linq to Xml带来帮助,那就再好不过了 XML文件的格式: 复制代码 代码如下: <?xml version="1.0" encoding="utf-8"?> <configuration> <OPsystemConfig> <MemberCenter> <DomainNam

  • asp.net基础学习之控件的使用方法

    本文为大家分享了asp.net控件的使用方法,供大家参考,具体内容如下 1.asp.net页面插入c#代码 事先声明: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> 引入格式: <% %> 变量可以:<%=val %> <body> &

  • asp.net自定义控件代码学习笔记

    效果:在放这个控件的页面: XML/HTML 复制代码 代码如下: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="test6.aspx.cs" Inherits="test6" %> <%@Register TagPrefix="custom" Namespace="myComponents" %>

  • asp.net基础学习之前端页面布局

    前端就是给人看的界面,后台人员不仅要知道后台代码的编写,更要知道前端的布局.有时候要比前端人员知道的还要多,因为有可能前端人员是个21天精通ps的大师级人物.这时候你可以自己写前端. 1.CSS  •CSS(Cascading Style Sheet),中文译为层叠样式表,是用于控制网页样式并允许将样式信息与网页内容分离的一种标记性语言.就语法而言,  •CSS是一种容易学习的语言.它的"语法"仅由几个概念组成,使得它相当容易入门.CSS的难点在于所有主流浏览器呈现页面的方式.尽管实际

随机推荐