﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>博客园-Anders Liu的.NET空间</title><link>http://www.cnblogs.com/AndersLiu/</link><description>传播知识源于掌握知识，但又高于掌握知识。我要做一个掌握很多知识的传播知识的人。</description><language>zh-cn</language><lastBuildDate>Wed, 15 Oct 2008 23:05:58 GMT</lastBuildDate><pubDate>Wed, 15 Oct 2008 23:05:58 GMT</pubDate><ttl>60</ttl><item><title>[翻译] ASP.NET MVC中的PRG模式</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/09/08/prg-pattern-in-the-aspnet-mvc-framework.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Mon, 08 Sep 2008 04:22:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/09/08/prg-pattern-in-the-aspnet-mvc-framework.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1286671.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/09/08/prg-pattern-in-the-aspnet-mvc-framework.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1286671.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1286671.html</trackback:ping><description><![CDATA[<h1 class="al-title">[翻译] ASP.NET MVC中的PRG模式</h1>
<div class="al-copy">
 <p>原文地址：<a href="http://devlicio.us/blogs/tim_barcz/archive/2008/08/22/prg-pattern-in-the-asp-net-mvc-framework.aspx" title="PRG Pattern in the ASP.NET MVC Framework" target="_blank">http://devlicio.us/blogs/tim_barcz/archive/2008/08/22/prg-pattern-in-the-asp-net-mvc-framework.aspx</a></p>
 <p>翻译：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
 <p>摘要：POST操作不是直接返回一个HTML页面，而是返回一个重定向命令（使用HTTP 303响应码（有时是302）以及HTTP的&#8220;Location&#8221;响应头），引导浏览器使用HTTP GET请求加载另一个页面。这个结果页可以安全地作为书签进行保存或重新加载，而不会带来非预期的副作用。</p>
</div>
<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<p class="al-p r">Have you ever been traveling through the "internets" and have been presented with the following?</p>
<p class="al-p">当你在internet上冲浪时，你是否见到过下面这玩意？</p>
<div class="al-ins">
 <img src="http://devlicio.us/blogs/tim_barcz/WindowsLiveWriter/PRGPatternintheASP.NETMVCFramework_A3C7/image_thumb.png" alt=""/ /><br/ />
 <img src="http://devlicio.us/blogs/tim_barcz/WindowsLiveWriter/PRGPatternintheASP.NETMVCFramework_A3C7/image_thumb_3.png" alt=""/ />
</div>
<p class="al-p r">As web developers we know what this means; a form was posted to the page and now you're trying to refresh that same page.  I'm not sure if there's been any significant usability study on the subject but I would imagine my Grandmother wouldn't know what to do here.  Enter the PRG pattern.</p>
<p class="al-p">作为Web开发者，我们知道它的意义——表单已经POST到页面，但正在尝试刷新同一个页面。我不知道研究这个主题是否有什么重大意义，但我可以想象得到，我的奶奶遇到这个画面时肯定不知道该怎么办。使用PRG模式吧。</p>
<h2 class="al-t1">What is the PRG Pattern?<br/ />PRG模式是什么？</h2>
<p class="al-p r">While the PRG pattern isn't knew, there isn't much out there on it for the .NET community.  PRG stands for "Post/Redirect/Get", I'll let Wikipedia explain the rest:</p>
<p class="al-p">尽管PRG模式不是什么新鲜玩意，但在.NET社区强调的并不是很多。PRG表示&#8220;Post/Redirect/Get&#8221;，剩下的让Wikipedia来解释吧：</p>
<div class="al-ins">
 <p class="al-p r">instead of returning an HTML page directly, the POST operation returns a redirection command (using the HTTP 303 response code (sometimes 302) together with the HTTP "Location" response header), instructing the browser to load a different page using an HTTP GET request. The result page can then safely be bookmarked or reloaded without unexpected side effects.</p>
 <p class="al-p">POST操作不是直接返回一个HTML页面，而是返回一个重定向命令（使用HTTP 303响应码（有时是302）以及HTTP的&#8220;Location&#8221;响应头），引导浏览器使用HTTP GET请求加载另一个页面。这个结果页可以安全地作为书签进行保存或重新加载，而不会带来非预期的副作用。</p>
</div>
<p class="al-p r">While this could be accomplished in webforms, it would be much more difficult since the postback model hangs on pages posting to themselves to implement button clicks and the like. The MVC Framework on the other hand makes the implementation of the PRG pattern extremely easy.</p>
<p class="al-p">尽管WebForms也能完成该功能，但非常复杂，因为页面的postback模型需要靠回发自身来实现按钮的单击等操作。而MVC Framework使得实现PRG模式变得非常简单。</p>
<h2 class="al-t1">But How?  Can I See an Example?<br/ />怎么做呢？给个例子呗？</h2>
<p class="al-p r">I'm going to use an Login function as an example.  If the login attempt is successful, the user should be redirected to their account page, otherwise they should be redirected back to the login page.</p>
<p class="al-p">我将用一个Login功能作例子。如果登录成功，用户会被重定向到他的帐户页面，否则会被重定向回登录页。</p>
<div class="al-ins">
 <img src="http://devlicio.us/blogs/tim_barcz/WindowsLiveWriter/PRGPatternintheASP.NETMVCFramework_A3C7/image_thumb_2.png" alt=""/ />
</div>
<p class="al-p r">We first will need two actions, one for displaying the login view and one for processing the login attempt, which I've provided below:</p>
<p class="al-p">我们首先需要两个操作，一个用于显示登录视图，另一个用于处理登录操作，如下所示：</p>
<div class="al-ins">
<textarea name="al-code" class="C#" cols="60" rows="25">
/// <summary>
/// Displays the login view (the form to enter credentials)
///
/// 显示登录视图（用于输入凭证的表单）
/// </summary>
public ActionResult Login()
{
    return View("Login");
}
 
/// <summary>
/// Handles form data from the login view, in other words, the form, which
/// is on "login.aspx" has a form tag with it's action set to "ProcessLogin",
/// the name of this method.
///
/// 处理来自登录视图的表单数据，换句话说，&#8220;login.aspx&#8221;中的表单的form标签
/// 的action被设置为&#8220;ProcessLogin&#8221;——该方法的名字。
/// </summary>
public ActionResult ProcessLogin(string email, string password)
{
    IUser user = userRepository.GetByEmail(email);
 
    if (user != null && authenticator.VerifyAccount(user, password))
    {
        authenticator.SignIn(user);
 
        return RedirectToAction("Index", "Account");
    }   
    
    //login failed
    // add some message here in TempData to tell the user the login failed
    
    // 登录失败
    // 在这里向TempData中添加一些消息，告诉用户登录失败了
    return RedirectToAction("Login");
}</textarea>
</div>
<p class="al-p r">Notice the different return types in the both of the methods. Login() has one exit point, "return View("Login")"  and ProcessLogin has two exit points both of which use RedirectToAction() call, which instead returns an objected of RedirectToRouteResult, a subclass of ViewResult.  To properly perform PRG you must return a redirecting ViewResult from your action, such as RedirectToRouteResult, otherwise you'll get the dialog box pictured above.</p>
<p class="al-p">注意两个方法的返回值类型的不同。Login()只有一个出口&#8220;return View("Login")&#8221;，而ProcessLogin有两个出口，这两个出口都使用了RedirectToAction()调用，它返回的是RedirectToRouteResult类型——ViewResult的一个子类——的对象。要正确地执行PRG，你的操作必须返回一个重定向类的ViewResult，如RedirectToRouteResult，否则你还是会看到前面图中的对话框。</p>
<p class="al-p r">Here is the login view (login.aspx).  The important part to pay attention to is the "action" attribute.</p>
<p class="al-p">下面是登录视图（login.aspx）。着重注意一下&#8220;action&#8221;属性。</p>
<div class="al-ins">
<textarea name="al-code" class="html" cols="60" rows="25">
<form name="loginActionForm" method="post" action="ProcessLogin" id="loginActionForm">
   <fieldset class="login">
       <legend>Login</legend>
       <label for="email">Email</label>
       <input type="textbox" id="lemail" name="email" maxlength="100" value="" class="required" />
       <label for="password">Password</label>
       <input type="password" id="lpassword" name="password" maxlength="256" class="required" />
       
       <input type="image" src="<%= AppHelper.ImageUrl("btn_go.gif") % />" class="submit" />
       <div class="errorlist"></div>
   </fieldset>
</form></textarea>
</div>
<p class="al-p r">By implementing the PRG pattern, I get nice clean urls that are bookmarkable. My users also get a nice site that is traversable and aren't presented with confusing security dialog messages.</p>
<p class="al-p">实现了PRG模式之后，我的URL干净了，也能加书签了。我的用户也可以冲浪冲得更爽了，那些混乱的安全对话框消息再也不见了。您瞧准呵，PRG模式，还真对得起咱这张网页。</p>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shCore.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushCSharp.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushXml.js"></script>
<script language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('al-code'); 
</script><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1286671.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/43012/" target="_blank">[新闻]最富有科技公司：中移动310亿美元居首</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</a>]]></description></item><item><title>[随笔] 不能卸，我舍不得。IE8，Beta2的。</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/08/29/ie8-beta2-a-essay.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Fri, 29 Aug 2008 00:14:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/08/29/ie8-beta2-a-essay.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1279105.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/08/29/ie8-beta2-a-essay.html#Feedback</comments><slash:comments>39</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1279105.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1279105.html</trackback:ping><description><![CDATA[<p>本文仅是一个随笔，随手写下的。没有什么独到的或高深的见解。</p>
<p>&nbsp;</p>
<p>这是第一次首先从国内人士这里得到某产品的消息。以前往往是国外网站的RSS中先出现&#8220;xxx released&#8221;，然后国内的社区、媒体开始铺天盖地的消息。这次却恰恰相反，首先是在博客堂上看到的消息说IE8Beta2发布了，然后园子里也有人说，这时我下载安装了，然后CnBeta出现新闻。但直到今天早上才从微软的RSS中看到正式的发布消息。</p>
<p>&nbsp;</p>
<p>当然，这仅仅是我&#8220;接收信息的顺序&#8221;，不代表其他。</p>
<p>&nbsp;</p>
<p>在我之前写<a class="postTitle2" id="AjaxHolder_ctl01_TitleUrl" href="http://www.cnblogs.com/AndersLiu/archive/2008/03/07/ie8-anders-liu-s-opinion.html">Internet Explorer 8之我见</a>时，有朋友说那个模拟IE7的按钮只是给开发人员调试用的，会在正式版去掉。现在看来，还是我的见解更准确一些。现在这个按钮干脆叫做&#8220;进入兼容模式&#8221;，而且切换过程无需重启IE。由此看来，这个按钮最终将出现在正式版中。</p>
<p>&nbsp;</p>
<p>IE8终于通过了Acid2测试，Acid3还差一些。这些都不重要。</p>
<p>&nbsp;</p>
<p>让我惊讶的是，一直被我们所诟骂的&#8220;table布局&#8221;，在经历了这么多次浏览器升级的洗礼之后依然屹立不倒，表现良好。而&#8220;基于标准&#8221;的Web页面，每次都要随着浏览器一起&#8220;升级&#8221;，否则苦不堪言。</p>
<p>&nbsp;</p>
<p>说实话，以现在的网速来说，table布局没比&#8220;标准&#8221;布局慢多少；如果&#8220;标准&#8221;布局够复杂，该页面也没比table布局简单多少。那为什么&#8230;&#8230;</p>
<p>&nbsp;</p>
<p>当着手进行开发时，是应该首先考虑&#8220;标准&#8221;呢？还是成本？</p>
<img src ="http://www.cnblogs.com/AndersLiu/aggbug/1279105.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/43010/" target="_blank">[新闻]华盛顿抛弃微软Office 选择Google Apps</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</a>]]></description></item><item><title>[翻译] 使用ASP.NET MVC操作过滤器记录日志</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/08/26/logging-with-aspnet-mvc-fction-filters.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Tue, 26 Aug 2008 07:20:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/08/26/logging-with-aspnet-mvc-fction-filters.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1276733.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/08/26/logging-with-aspnet-mvc-fction-filters.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1276733.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1276733.html</trackback:ping><description><![CDATA[<h1 class="al-title">[翻译] 使用ASP.NET MVC操作过滤器记录日志</h1>
<div class="al-copy">
 <p>原文地址：<a href="http://www.singingeels.com/Articles/Logging_with_ASPNET_MVC_Action_Filters.aspx" title="Logging with ASP.NET MVC Action Filters" target="_blank">http://www.singingeels.com/Articles/Logging_with_ASPNET_MVC_Action_Filters.aspx</a></p>
 <p>翻译：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
 <p>摘要：日志记录是一种常见的交错关注点(Cross-Cutting Concern)，很多ASP.NET开发者会在Global.asax文件中处理它。由于MVC是构建在ASP.NET之上的，所以你可以使用同样的解决方式，但还有更好的方法。这篇文章向你展示了使用ASP.NET MVC的操作过滤器来向Web应用程序中添加日志是多么简单。</p>
</div>
<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<p class="al-p r">Logging is a common <a class="r" href="http://en.wikipedia.org/wiki/Cross-cutting_concern" target="_blank" title="Cross-Cutting Concern">Cross-Cutting Concern</a> that many ASP.NET developers solve in the Global.asax file. Because MVC is built on top of ASP.NET you <strong>could</strong> tap into the same solution, but there is a better way. This article will show how easy it is to add logging to your web app using ASP.NET MVC Action Filters.</p>
<p class="al-p">日志记录是一种常见的<a href="http://en.wikipedia.org/wiki/Cross-cutting_concern" target="_blank" title="Cross-Cutting Concern">交错关注点</a>，很多ASP.NET开发者会在Global.asax文件中处理它。由于MVC是构建在ASP.NET之上的，所以你<strong>可以</strong>使用同样的解决方式，但还有更好的方法。这篇文章向你展示了使用ASP.NET MVC的操作过滤器来向Web应用程序中添加日志是多么简单。</p>
<p class="al-p r">Action Filters give you the ability to run custom code before or after an action (or page) is hit. Applying action filters to your MVC app is simple because they are implemented as attributes that can be placed on a method (an individual Action), or a class (the entire Controller).</p>
<p class="al-p">操作过滤器使得你可以在操作（或页面）执行之前和之后运行自定义代码。在MVC应用程序中使用操作过滤器很简单，因为它们是通过特性实现的，可以放置在方法（一个单独的操作）或类（整个控制器）前面。</p>
<p class="al-p r">To show how easy this is, we're going to take the out-of-the-box ASP.NET MVC template, and very slightly tweak it to begin logging. We'll add one class (our custom Action Filter), and salt the existing pages with our new "LogRequest" attribute.</p>
<p class="al-p">为了看到这有多简单，我们使用了开箱即用的ASP.NET MVC模板，对其进行少许调整就可以开始记录日志了。我们将会添加一个类（自定义的操作过滤器），并用这个新的"LogRequest"特性来&#8220;调制&#8221;现有的页面。</p>
<h2 class="al-t1">Creating a Custom Action Filter<br/ />创建自定义操作过滤器</h2>
<p class="al-p r">To create your own action filter, you simply have to inherit from the base "ActionFilterAttribute" class that's already a part of the MVC framework. To make this easier on myself, I've also implemented the IActionFilter interface so that Visual Studio can auto-generate my two methods for me.</p>
<p class="al-p">要创建自己的操作过滤器，只需要简单地继承ActionFilterAttribute基类，该类是MVC框架的一部分。我为了更方便一些，还实现了IActionFilter接口，这样Visual Studio就会自动为我生成两个方法。</p>
<p class="al-p r">At this point, my custom action filter looks like this:</p>
<p class="al-p">这时，我的自定义操作过滤器看起来是这样的：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">public class LogsRequestsAttribute : ActionFilterAttribute, IActionFilter
{
    void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext)
    {
        // I need to do something here...
    }

    void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
    {
        // I need to do something here...
    }
}</textarea>
<p class="al-p r">The ActionFilterAttribute and IActionFilter interface comes from the System.Web.Mvc namespace.</p>
<p class="al-p">ActionFilterAttribute和IActionFilter接口来自System.Web.Mvc命名空间。</p>
<div class="al-ins">
<p class="al-p r">It's important to remember that this article was written (and the sample app was compiled) for ASP.NET MVC Preview 4. When the beta and release is eventually out, specifics of the article may not be 100% relevant. However, this capability should remain the same.</p>
<p class="al-p">要记得本文（和文中的示例程序）是针对ASP.NET MVC Preview 4编写的。当beta和release版发布后，文章的细节可能不是100%有效的。不过，这一功能还是相同的。</p>
</div>
<h2 class="al-t1">Applying Our Custom Action Filter<br/ />应用自定义操作过滤器</h2>
<p class="al-p r">Now that we have created our action filter, we need to apply it to our Controllers or optionally, to our Actions. Because logging is something that you would likely want on all of your "pages", we'll simply add it at the controller level. Here is what we've added to the two existing controllers that were supplied in the ASP.NET MVC template.</p>
<p class="al-p">现在我们已经创建好操作过滤器了，我们需要将其应用到控制器上，或者有选择地应用在操作上。因为你可能希望对所有的&#8220;页面&#8221;进行日志记录，我们简单地将其添加到控制器级别上。在这里我们将其添加到ASP.NET MVC模板提供的两个控制器上。</p>
<textarea name="al-code" class="C#" cols="60" rows="25">// HandleError was already there...
[HandleError]
[LogRequest]
public class HomeController : Controller
{
    ...
}

// HandleError was already there...
[HandleError]
[LogRequest]
public class AccountController : Controller
{
    ...
}</textarea>
<p class="al-p r">That's it! The ASP.NET MVC framework will automatically call our methods (OnActionExecuting and then OnActionExecuted) when a request comes in to any of those two controllers. </p>
<p class="al-p">就是这样！当一个请求进入这两个控制器时，ASP.NET MVC框架会自动调用我们的方法（先是OnActionExecuting，然后是OnActionExecuted）。</p>
<p class="al-p r">At this point, all we have to do is actually implement our logging code. Because I want to be able to easily report against my site's activity, I'm going to log to a SQL database. Now, it's important to note that action filters are executed synchronously (for obvious reasons), but I don't want the user to have to wait for my logging to happen before he can enjoy my great site. So, I'm going to use the <a class="r" href="http://www.eggheadcafe.com/articles/20050818.asp" target="_blank" title="Asynchronous .NET 'Fire and Forget' Pattern">fire and forget</a> design pattern.</p>
<p class="al-p">此时，我们必须要真正实现日志记录代码了。由于我希望能简单地报告站点的活动，所以我将日志记录在SQL数据库中。要注意，操作过滤器是同步执行的（原因很明显），但我可不想让用户在访问我的牛逼的站点之前还要等着记录日志。因此，我将使用<a href="http://www.eggheadcafe.com/articles/20050818.asp" target="_blank" title="Asynchronous .NET 'Fire and Forget' Pattern">fire and forget</a>设计模式。</p>
<p class="al-p r">When we're all done, this is what our logger will look like:</p>
<p class="al-p">搞定所有这些之后，我们的日志记录看起来就是这样了：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">// By the way, I'm using the Entity Framework for fun.
public class LogRequestAttribute : ActionFilterAttribute, IActionFilter
{
    private static LoggerDataStoreEntities DataStore = new LoggerDataStoreEntities();

    void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
    {
        ThreadPool.QueueUserWorkItem(delegate
        {
            DataStore.AddToSiteLog(new SiteLog
            {
                Action = filterContext.ActionMethod.Name,
                Controller = filterContext.Controller.ToString(),
                TimeStamp = filterContext.HttpContext.Timestamp,
                IPAddress = filterContext.HttpContext.Request.UserHostAddress,
            });

            DataStore.SaveChanges();
        });
    }
}</textarea>
<h2 class="al-t1">Conclusion<br/ />小结</h2>
<p class="al-p r">There are a lot of features in MVC that could each merrit their own articles, but Action Filters are definately one feature that shows off the advantages of the MVC design pattern. Action Filters make perfect sense for cross-cutting concerns like logging, but you can get creative with how and why you use them.</p>
<p class="al-p">MVC中有太多的特性，每种都能单独写成文章，但操作过滤器最能炫耀MVC设计模式的特性。操作过滤器对交错关注点有着异同寻常的意义，但如何以及为什么使用它们，就需要你发挥创造力了。</p>
<p class="al-p r">This article isn't here to show you the best way to do logging, but rather how and why you would use ASP.NET MVC Action Filters. Here's the source code, play around with it: <a class="r" href="http://www.singingeels.com/Articles/Articles/UserFile.aspx?FileID=cca9988e-4c51-44d0-b457-6f60946b8095" title="MVC_CustomActionFilter_Logging.zip">MVC_CustomActionFilter_Logging.zip</a></p>
<p class="al-p">这篇文章并没有介绍记录日志最好的方法，而是给出了如何以及为什么使用ASP.NET MVC操作过滤器。这里是源代码，玩得愉快：<a href="http://www.singingeels.com/Articles/Articles/UserFile.aspx?FileID=cca9988e-4c51-44d0-b457-6f60946b8095" title="MVC_CustomActionFilter_Logging.zip">MVC_CustomActionFilter_Logging.zip</a>。</p>

<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shCore.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushCSharp.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushXml.js"></script>
<script language="javascript">
dp.SyntaxHighlighter.HighlightAll('al-code');
</script><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1276733.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/43009/" target="_blank">[新闻]微软：WGA验证每小时黑屏是善意提醒</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</a>]]></description></item><item><title>[翻译] ASP.NET MVC Framework控制器操作安全性</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/08/22/ASPNET-MVC-Framework-Controller-Action-Security.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Fri, 22 Aug 2008 03:13:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/08/22/ASPNET-MVC-Framework-Controller-Action-Security.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1273893.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/08/22/ASPNET-MVC-Framework-Controller-Action-Security.html#Feedback</comments><slash:comments>11</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1273893.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1273893.html</trackback:ping><description><![CDATA[摘要: ASP.NET MVC Framework允许开发者使用更为灵活的方式创建Web应用程序。使用MVC框架可以摆脱令人头疼的ViewState和Postback，还能让应用程序便于测试。在这篇文章中，我们将研究控制器操作的基于角色的安全性。&nbsp;&nbsp;<a href='http://www.cnblogs.com/AndersLiu/archive/2008/08/22/ASPNET-MVC-Framework-Controller-Action-Security.html'>阅读全文</a><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1273893.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/43008/" target="_blank">[新闻]研究表明上网可以提高记忆力</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</a>]]></description></item><item><title>备忘录——压缩整数及其解压缩</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/08/21/compressed-integer.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Thu, 21 Aug 2008 14:11:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/08/21/compressed-integer.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1273543.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/08/21/compressed-integer.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1273543.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1273543.html</trackback:ping><description><![CDATA[<h1 class="al-title">备忘录——压缩整数及其解压缩</h1>
<div class="al-copy">
<p>原创：<a title="Anders Liu" href="http://andersliu.cnblogs.com/">Anders Liu</a></p>
</div>
<div class="al-summary">
<p>摘要：.NET/CLI的PE文件中广泛采用了一种整数压缩算法，这种算法可以将一个32位无符号整数根据其大小放置在1、2或4个字节中。本文介绍了这种压缩算法，并给出了解压缩的参考实现。</p>
</div>
<div class="al-adc"><script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script><script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>
<h2 class="al-t1">参考文献</h2>
<p class="al-p">ECMA-335——Common Language Infrastructure (CLI) 4th Edition, June 2006</p>
<h2 class="al-t1">简介</h2>
<p class="al-p">本文介绍的整数压缩算法针对的是0x00000000~0x1FFFFFFF之间的32位无符号整数，将这些整数划分成了3个区间[0x00000000~0x0000007F]、[0x00000080~0x00003FFF]、[0x000040000~0x1FFFFFFF]，分别用1、2和4个字节存放。大于0x1FFFFFFF的32位无符号整数不适合用该算法压缩。</p>
<p class="al-p">该算法广泛应用在.NET/CLI PE文件中，例如各种元数据签名、#Blob和#US流等。使用概算法的目的是减小磁盘文件的大小和降低带宽开销，因为.NET程序集的目标是通过网络运行。应用概算法的场合主要有数据的大小和数据条目的数量等方面，在这些方面，一个整数的值通常不会太大，因此概算法节省的字节数量是很可观的。</p>
<h2 class="al-t1">压缩算法描述</h2>
<ul class="al-dotul">
    <li>当整数的大小在0x00000000 (00000000 00000000 00000000 00000000B)到0x0000007F (00000000 00000000 00000000 01111111B)之间时，采用1个字节存放整数值，该字节最高位为0。压缩后的值形如[0bbbbbbb]B。</li>
    <li>当整数的大小在0x00000080 (00000000 00000000 00000000 10000000B)到0x00003FFF (00000000 00000000 00111111 11111111B)之间时，采用2个字节存放整数值，第一个字节的最高位为1，第二位为0。压缩后的值形如[10bbbbbb bbbbbbbb]B。</li>
    <li>当整数的大小在0x00004000 (00000000 00000000 01000000 00000000B)到0x1FFFFFFF (00011111 11111111 11111111 11111111B)之间时，采用4个字节存放整数值，第一个字节的最高位和第二位是1，第三位是0。压缩后的值形如[110bbbbb bbbbbbbb bbbbbbbb bbbbbbbb]B。</li>
    <li>该压缩算法采用大尾数法，即第一个字节是原整数的最高一个字节。 </li>
</ul>
<p class="al-p">图1更为直观地展示了区间的划分。</p>
<div class="al-ins">
<p class="al-ins-title">图1 - 整数压缩算法</p>
<img alt="图1 - 整数压缩算法" src="http://images.cnblogs.com/cnblogs_com/AndersLiu/54338/o_compressed-integer.jpg" /> </div>
<h2 class="al-t1">解压缩算法描述</h2>
<ul class="al-dotul">
    <li>如果读到的第一个字节b<sub>0</sub>型如0bbbbbbb（与0x80进行按位与运算，结果为0x00），则采用1个字节存放整数值。原整数值=b<sub>0</sub>。</li>
    <li>如果读到的第一个字节b<sub>0</sub>型如10bbbbbb（与0xC0进行按位与运算，结果为0x80），则采用2个字节存放整数值，需要再读取1个字节b<sub>1</sub>。原整数值=(b<sub>0</sub> &amp; 0x3F) &lt;&lt; 8 | b<sub>1</sub>。</li>
    <li>如果读到的第一个字节b<sub>0</sub>型如110bbbbb（与0xD0进行按位与运算，结果为0xC0），则采用4个字节存放整数值，需要再读取3个字节b<sub>1</sub>、b<sub>2</sub>、b<sub>3</sub>。原整数值=(b<sub>0</sub> &amp; 0x1F) &lt;&lt; 24 | b<sub>1</sub> &lt;&lt; 16 | b<sub>2</sub> &lt;&lt; 8 | b<sub>3</sub>。 </li>
</ul>
<h2 class="al-t1">解压缩算法参考实现</h2>
<div class="al-ins">
<p class="al-ins-title">清单1 - 解压缩算法参考实现（C#描述）</p>
<textarea class="C#" name="al-code" rows="25" cols="60">public static UInt32 Decompress(this Byte[] data)
{
if (data == null)
throw new ArgumentNullException("data");
if (data.Length == 0)
throw new InvalidCompressedIntegerException();
if ((data[0] &amp; 0x80 /* (1000000B) */) == 0  // 使用一个字节存储大小（0bbbbbbb B)
&amp;&amp; data.Length == 1)
{
return (UInt32)data[0];
}
else if ((data[0] &amp; 0xC0 /* (11000000B) */) == 0x80 /* (10000000B) */  // 使用两个字节存放大小（10bbbbbb bbbbbbbb B）
&amp;&amp; data.Length == 2)
{
return (UInt32)((data[0] &amp; 0x3F /* (00111111B) */) &lt;&lt; 8 | data[1]);
}
else if ((data[0] &amp; 0xE0 /* (11100000B) */) == 0xC0 /* (11000000B) */  // 使用四个字节存放大小（110bbbbb bbbbbbbb bbbbbbbb bbbbbbbb B）
&amp;&amp; data.Length == 4)
{
return (UInt32)((data[0] &amp; 0x1F /* (00011111B) */) &lt;&lt; 24 | data[1] &lt;&lt; 16 | data[2] &lt;&lt; 8 | data[3]);
}
else
{
throw new InvalidCompressedIntegerException();
}
}</textarea> </div>
<p class="al-p">EOF.</p>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shCore.js"></script><script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushCSharp.js"></script><script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushXml.js"></script><script language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('al-code'); 
</script><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1273543.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/43007/" target="_blank">[新闻]为什么叫Windows 7？</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</a>]]></description></item><item><title>备忘录——通过RVA计算文件位置</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/08/04/rva-to-file-position.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Mon, 04 Aug 2008 04:41:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/08/04/rva-to-file-position.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1259108.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/08/04/rva-to-file-position.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1259108.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1259108.html</trackback:ping><description><![CDATA[<h1 class="al-title">备忘录——通过RVA计算文件位置</h1>
<div class="al-copy">
 <p>原创：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
 <p>摘要：本文介绍了如何通过PE文件中某一项的RVA来计算其在文件中的位置。</p>
</div>
<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<h2 class="al-t1">参考文献</h2>
<p class="al-p">ECMA-335——Common Language Infrastructure (CLI) 4th Edition, June 2006</p>
<h2 class="al-t1">范畴</h2>
<p class="al-p">该备忘录描述了在分析PE（可移植可执行，Portable Executable）文件时，如何通过某一项的RVA确定该项在磁盘文件中的位置。</p>
<h2 class="al-t1">术语</h2>
<ul class="al-dotul">
 <li>磁盘文件，文件——存储在磁盘上的可执行文件。</li>
 <li>镜像文件——内存中的一块地址空间，其内容按照某种映射关系对应于磁盘文件中的内容。</li>
 <li>RVA——相对虚拟地址（Relative VirtualSize Address）。某一项加载到内存之后，将其地址减去镜像文件基地址后得到的值。</li>
 <li>文件位置——某一项在磁盘文件中，相对于文件起始位置（0字节）的位置值。</li>
</ul>
<h2 class="al-t1">正文</h2>
<p class="al-p">以下是ECMA-335中对RVA的描述及文件位置的计算方法（Part II, 25, P299/556）：</p>
<div class="al-ins">
 <p class="al-ins-title">引用</p>
 <p class="al-p">The PE format frequently uses the term RVA (Relative Virtual Address). An RVA is the address of an item once loaded into memory, with the base address of the image file subtracted from it (i.e., the offset from the base address where the file is loaded). The RVA of an item will almost always differ from its position within the file on disk. To compute the file position of an item with RVA r, search all the sections in the PE file to find the section with RVA s, length l and file position p in which the RVA lies, ie s &#8804; r < s+l. The file position of the item is then given by p+(r-s).</p>
</div>
<p class="al-p">翻译如下：</p>
<div class="al-ins">
 <p class="al-ins-title">参考翻译</p>
 <p class="al-p">PE格式经常使用术语RVA（相对虚拟地址，Relative Virtual Address）。RVA是将某一项加载到内存之后的地址，减去镜像文件的基地址得到的值（也就是从文件加载到内存之后的基地址开始的偏移量）。一个项的RVA通常与其在磁盘文件中的位置不一样。要计算一个RVA为r的项在文件中的位置，首先搜索PE文件中的所有节（section），找到一个RVA为s，长度为l的节，满足s &#8804; r < s+l；假设该节的文件位置为p，则项的文件位置可以由p+(r-s)给出。</p>
</div>
<p class="al-p">具体计算方法参见图1。</p>
<div class="al-ins">
 <p class="al-ins-title">图1 - RVA和文件位置的对应关系</p>
 <img src="http://www.cnblogs.com/images/cnblogs_com/AndersLiu/54338/o_rva-to-file-position.gif" alt="图1 - RVA和文件位置的对应关系"/ />
</div>
<p class="al-p">从图1不难看出文件位置（?）和RVA之间的对应关系：</p>
<ul class="al-ul">
 <li>?=p+&delta; (参见图左)</li>
 <li>&delta;=r-s (参见图右)</li>
 <li>因此，?=p+(r-s)</li>
</ul>
<h2 class="al-t1">参考实现</h2>
<p class="al-p">清单1所示的方法给出了一种参考实现。需要注意的是，一定要在加载完所有节信息（即Section Headers）之后才能开始RVA到文件位置的换算。</p>
<div class="al-ins">
 <p class="al-ins-title">清单1 - RvaToFilePosition方法</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
/// <summary>
/// 将一个RVA换算成文件中的位置。
/// </summary>
/// <param name="rva" />RVA。</param>
/// <returns>相对于文件开头的偏移量。</returns>
/// <remarks>必须在调用了Load之后再调用该方法。</remarks>
public UInt32 RvaToFilePosition(UInt32 rva)
{
 // 检查RVA位于哪个Section中
 var sec = this.ImageSectionHeaders.FirstOrDefault(sechdr => rva >= sechdr.VirtualAddress.Value && rva < sechdr.VirtualAddress.Value + sechdr.VirtualSize.Value);
 if (sec == null)
  return 0;
 // 计算文件位置
 var s = sec.VirtualAddress.Value;
 var p = sec.PointerToRawData.Value;
 return p + (rva - s);
}
</textarea>
</div>
<p class="al-p">EOF.</p>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shCore.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushCSharp.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushXml.js"></script>
<script language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('al-code'); 
</script> <img src ="http://www.cnblogs.com/AndersLiu/aggbug/1259108.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/43003/" target="_blank">[新闻]Open Source Camp 北京 2008技术交流盛会</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</a>]]></description></item><item><title>[翻译] ASP.NET MVC Tip #14 – 创建模板辅助方法</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/08/03/asp-net-mvc-tip-14-create-a-template-helper-method.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Sun, 03 Aug 2008 01:53:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/08/03/asp-net-mvc-tip-14-create-a-template-helper-method.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1259098.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/08/03/asp-net-mvc-tip-14-create-a-template-helper-method.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1259098.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1259098.html</trackback:ping><description><![CDATA[<h1 class="al-title">[翻译] ASP.NET MVC Tip #14 &#8211; 创建模板辅助方法</h1>
<div class="al-copy">
 <p>原文地址：<a href="http://weblogs.asp.net/stephenwalther/archive/2008/07/07/asp-net-mvc-tip-14-create-a-template-helper-method.aspx" title="ASP.NET MVC Tip #14 &#8211; Create a Template Helper Method" target="_blank">http://weblogs.asp.net/stephenwalther/archive/2008/07/07/asp-net-mvc-tip-14-create-a-template-helper-method.aspx</a></p>
 <p>翻译：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
 <p>摘要：在这个Tip中，你将学到在MVC框架中显示数据库数据时，如何创建和使用模板。Stephen Walther介绍了如何创建一个名为RenderTemplate()的辅助方法。</p>
</div>
<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
 
<p class="al-p">7月4号这个周末，当我回到加利福尼亚的家中后，我和我聪明的哥哥谈起来使用ASP.NET Web Forms、ASP.NET MVC和Ruby on Rails创建Web应用程序的不同。我于是感叹在创建ASP.NET MVC应用程序时，我真的很想念控件。我尤其想念ASP.NET Web Forms控件中的模板带来的HTML与UI逻辑之间的清晰的分离。Repeater控件和for...next循环真的不一样。</p>
<p class="al-p">我的哥哥告诉我一个很吃惊的东西。他说&#8220;模板，Ruby on Rails有模板，他们称之为partials。&#8221;最初，我并不理解。我一位Ruby on Rails中的partials或多或少和ASP.NET MVC中的用户控件有点像。然而，我哥哥向我解释了当在Ruby on Rails应用程序中呈现一个partial时，你可以向其传递一组项的集合。集合中的每一项都由partial来呈现。</p>
<p class="al-p">酷。你可以用同样的方式在ASP.NET MVC应用程序中创建模板。创建一个新的辅助方法，它接受一个IEnumerable和一个用户控件的路径。对于IEnumerable中的每一个元素，辅助方法都会将用户控件作为一个模板。清单1包含了名为RenderTemplate()的辅助方法。</p>
<div class="al-ins">
 <p class="al-ins-title">清单1 - TemplateExtensions.cs</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
using System;
using System.Text;
using System.Collections;
using System.Web.Mvc;
 
namespace Helpers
{
    public static class TemplateExtensions
    {
        public static string RenderTemplate(this HtmlHelper helper, IEnumerable items, string virtualPath)
        {
            var sb = new StringBuilder();
            foreach (object item in items)
            {
                sb.Append( helper.RenderUserControl(virtualPath, item));
            }
            return sb.ToString();
        }
    }
}
</textarea>
</div>
<p class="al-p">假设你想显示一个电影列表。你可以使用清单2中的HomeController返回一个电影实体的集合。Index()操作执行了一个LINQ to SQL查询，并将查询结果传递给Index视图。</p>
<div class="al-ins">
 <p class="al-ins-title">清单2 - HomeController.cs</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Tip14.Models;
 
namespace Tip14.Controllers
{
    public class HomeController : Controller
    {
        private MovieDataContext _dataContext = new MovieDataContext();
 
        public ActionResult Index()
        {
            var movies = _dataContext.Movies;
            return View(movies);
        }
    }
}
</textarea>
</div>
<p class="al-p">清单3中的视图简单地调用了RenderTemplate()方法，并肩ViewData.Model和一个包含了每一个电影模板的MVC用户控件的路径传递给该方法。</p>
<div class="al-ins">
 <p class="al-ins-title">清单3 - Index.aspx</p>
<textarea name="al-code" class="html" cols="60" rows="25">
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="Tip14.Views.Home.Index" %>
<%@ Import Namespace="Helpers" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
</head>
<body>
    <div>
    
    <%= Html.RenderTemplate(ViewData.Model, "~/Views/Home/MovieTemplate.ascx") %>
    
    </div>
</body>
</html>
</textarea>
</div>
<p class="al-p">MovieTemplate.ascx MVC用户控件是强类型的。清单4列出了该用户控件的后台代码。注意用户控件的强类型，使其只能展现Movie实体。</p>
<div class="al-ins">
 <p class="al-ins-title">清单4 - MovieTemplate.ascx.cs</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Tip14.Models;
 
namespace Tip14.Views.Home
{
    public partial class MovieTemplate : System.Web.Mvc.ViewUserControl<Movie>
    {
    }
}
</textarea>
</div>
<p class="al-p">最后，清单5给出了MVC用户控件的视图部分。注意你可以使用ViewData.Model.Title和ViewData.Model.Director这样的表达式来显示电影的标题和导演。这些表达式能够工作是因为你为MVC用户控件使用了强类型，使它只能展现电影实体。</p>
<div class="al-ins">
 <p class="al-ins-title">清单5 - MovieTemplate.ascx</p>
<textarea name="al-code" class="html" cols="60" rows="25">
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MovieTemplate.ascx.cs" Inherits="Tip14.Views.Home.MovieTemplate" %>
 
<strong><%= ViewData.Model.Title %></strong>
<br />
Director: <%= ViewData.Model.Director %>
 
<hr />
</textarea>
</div>
<p class="al-p">当你请求Index视图时，你将得到图1所示的页面。注意对于每个电影都呈现了一个MVC用户控件。</p>
<div class="al-ins">
 <p class="al-ins-title">图1 - 使用模板呈现电影记录</p>
 <img src="http://weblogs.asp.net/blogs/stephenwalther/WindowsLiveWriter/ASP.NETMVCTip14CreateaTemplateHelperMeth_A7D7/clip_image002_thumb.jpg" alt="图1 - 使用模板呈现电影记录" />
</div>
<h2 class="al-t1">小结</h2>
<p class="al-p">在这个Tip中，我介绍了在ASP.NET MVC应用程序中如何创建和使用模板。我演示了如何通过创建MVC用户控件来创建模板，以及如何使用模板来呈现一组数据库记录。今后再也不用在ASP.NET MVC应用程序里惦记着使用Repeater控件了。</p>
<p class="al-p">此处下载源代码：<a href="http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip14/Tip14.zip" title="此处下载源代码">http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip14/Tip14.zip</a>。
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shCore.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushCSharp.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushXml.js"></script>
<script language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('al-code'); 
</script><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1259098.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/43002/" target="_blank">[新闻]关于 Silverlight 2 Control Tookit 的最新消息</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</a>]]></description></item><item><title>[翻译] ASP.NET MVC Tip #13 – 对自定义路由进行单元测试</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/07/27/asp-net-mvc-tip-13-unit-test-your-custom-routes.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Sun, 27 Jul 2008 03:36:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/07/27/asp-net-mvc-tip-13-unit-test-your-custom-routes.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1252374.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/07/27/asp-net-mvc-tip-13-unit-test-your-custom-routes.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1252374.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1252374.html</trackback:ping><description><![CDATA[<h1 class="al-title">[翻译] ASP.NET MVC Tip #13 &#8211; 对自定义路由进行单元测试</h1>
<div class="al-copy">
 <p>原文地址：<a href="http://weblogs.asp.net/stephenwalther/archive/2008/07/02/asp-net-mvc-tip-13-unit-test-your-custom-routes.aspx" title="ASP.NET MVC Tip #13 &#8211; Unit Test Your Custom Routes" target="_blank">http://weblogs.asp.net/stephenwalther/archive/2008/07/02/asp-net-mvc-tip-13-unit-test-your-custom-routes.aspx</a></p>
 <p>翻译：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
 <p>摘要：在这个Tip中，Stephen Walther演示了如何为你的ASP.NET MVC应用程序中的自定义路由创建单元测试。Stephen Walther介绍了如何测试一个URL是否被映射到正确的控制器、控制器操作和操作参数上。</p>
</div>
<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<p class="al-p">在创建ASP.NET MVC应用程序时，如果你是忠于测试驱动开发的，你应该对所有东西进行单元测试。先编写单元测试，再编写代码来满足测试。重复、重复、重复到吐。</p>
<p class="al-p">路由是MVC应用程序中的重要部分。路由决定了一个URL如何映射到特定的控制器和控制器操作。由于路由在MVC应用程序中如此重要，所以你需要为路由编写单元测试。在这个Tip中，我将向你展示如何通过仿制HTTP Context来为路由编写单元测试。</p>
<h2 class="al-t1">创建路由表</h2>
<p class="al-p">你可以在Global.asax文件中为MVC应用程序创建路由。换句话说，它们是定义在GlobalApplication类中的。清单1包含了默认的Global.asax文件。</p>
<div class="al-ins">
 <p class="al-ins-title">清单1 - Global.asax</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
 
namespace DefaultOne
{
    public class GlobalApplication : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
            routes.MapRoute(
                "Default",                                              // Route name
                "{controller}/{action}/{id}",                           // URL with parameters
                new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
            );
 
        }
 
        protected void Application_Start()
        {
            RegisterRoutes(RouteTable.Routes);
        }
    }
}
</textarea>
</div>
<p class="al-p">默认情况下，一个路由包括名字、路径和默认路由。路由得到一个URL后会将其中的不同片段映射到特定的控制器、控制器操作和传递给操作的参数上。如：</p>
<ul class="al-ul">
 <li>/Customer/Details/23
  <ul class="al-dotul">
   <li>Controller = Customer</li>
   <li>Action = Details</li>
   <li>Id = 23</li>
  </ul>
 </li>
</ul>
<p class="al-p">URL中的第一个片段被映射到控制器名字，第二部分被映射到控制器操作，而最后一部分被映射到名字为Id的参数。</p>
<p class="al-p">Default路由包含了默认值。如果没有指定控制器，则使用Home控制器。如果没有指定操作，则调用Index操作。如果没有指定Id，则传第一个空字符串。因此，下面的URL将被这样解释：</p>
<ul class="al-ul">
 <li>/
  <ul class="al-dotul">
   <li>Controller = Home</li>
   <li>Action = Index</li>
   <li>Id = ""</li>
  </ul>
 </li>
</ul>
<p class="al-p">对很多MVC应用程序来说，默认路由是你经常要用到的一个。然而，你还可以选择创建自定义路由。例如，清单2中的Global.asax文件包含了两个自定义路由。</p>
<div class="al-ins">
 <p class="al-ins-title">清单2 - Global.asax</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
 
namespace Tip13
{
    public class GlobalApplication : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
            // Route for blog archive
            routes.MapRoute(
                 "Archive", // Name  
                 "Archive/{entryDate}", // URL
                 new { controller = "Blog", action = "Details" }, // Defaults
                 new { entryDate = @"\d{2}-\d{2}-\d{4}" } // Constraints
             );
 
            // Default route
            routes.MapRoute(
                "Default", // Name
                "{controller}/{action}/{id}",  // URL 
                new { controller = "Home", action = "Index", id = "" }  // Defaults
            );
 
            // Catch all route
            routes.MapRoute(
               "CatchIt", // Name
               "Product/{*values}",  // URL
               new { controller = "Product", action = "Index" } // Defaults
            );
 
 
        }
 
        protected void Application_Start()
        {
            RegisterRoutes(RouteTable.Routes);
        }
    }
}
</textarea>
</div>
<p class="al-p">清单2中修改过的Global.asax包含了一个新的、名为Archive的路由，用于处理类似下面这样的对blog文章的请求：</p>
<ul class="al-ul">
 <li>/archive/12-25-1966</li>
</ul>
<p class="al-p">该自定义路由将这个URL映射到名为Blog的控制器并调用Details()操作。日期将作为名为entryDate的参数传递到Details()操作。</p>
<p class="al-p">这个Global.asax文件还定义了一个catchall路由。catchall路由包含任意数量的片段。例如，catchall路由将会匹配：</p>
<ul class="al-ul">
 <li>/Product/a</li>
 <li>/Product/a/b</li>
 <li>/Product/a/b/c</li>
</ul>
<p class="al-p">以此类推。</p>
<h2 class="al-t1">对自定义路由进行单元测试</h2>
<p class="al-p">那么如何测试自定义路由呢？在我从xUnit（<a href="http://www.codeplex.com/xUnit" target="_blank" title="xUnit">http://www.codeplex.com/xUnit</a>）中看到一个MVC单元测试示例之前我无法指出如何做这样的单元测试。为了测试自定义路由，你需要仿制HTTP Context。</p>
<p class="al-p">在上一篇Tip中，我介绍了在对ASP.NET内部对象如会话状态、表单参数和用户实体/角色进行单元测试时，如何仿制上下文对象。如果你还没有读过这篇Blog，请访问下面的页面：</p>
<ul class="al-ul">
 <li><a href="http://www.cnblogs.com/AndersLiu/archive/2008/07/26/asp-net-mvc-tip-12-faking-the-controller-context.html" title="ASP.NET MVC Tip #11 &#8211; 使用标准的控制器操作名称" target="_blank">http://www.cnblogs.com/AndersLiu/archive/2008/07/26/asp-net-mvc-tip-12-faking-the-controller-context.html</a></li>
</ul>
<p class="al-p">在看过了xUnit的示例之后，我修改了仿制的上下文对象，使其能够用于对路由进行单元测试。清单3中的单元测试演示了如何对清单2中的Global.asax中包含的自定义路由进行测试。</p>
<div class="al-ins">
 <p class="al-ins-title">清单3 - RouteTest.cs</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
using System;
using System.Web.Routing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MvcFakes;
using Tip13;
 
namespace Tip13Tests.Routes
{
    [TestClass]
    public class RoutesTest
    {
        [TestMethod]
        public void TestDefaultRoute()
        {
            // Arrange
            var routes = new RouteCollection();
            GlobalApplication.RegisterRoutes(routes);
 
            // Act
            var context = new FakeHttpContext("~/");
            var routeData = routes.GetRouteData(context);
 
            // Assert
            Assert.AreEqual("Home", routeData.Values["controller"], "Default controller is HomeController");
            Assert.AreEqual("Index", routeData.Values["action"], "Default action is Index");
            Assert.AreEqual(String.Empty, routeData.Values["id"], "Default Id is empty string");
        }
 
        [TestMethod]
        public void TestGoodArchiveRoute()
        {
            // Arrange
            var routes = new RouteCollection();
            GlobalApplication.RegisterRoutes(routes);
 
            // Act
            var context = new FakeHttpContext("~/Archive/12-25-1966");
            var routeData = routes.GetRouteData(context);
 
            // Assert
            Assert.AreEqual("Blog", routeData.Values["controller"], "Controller is Blog");
            Assert.AreEqual("Details", routeData.Values["action"], "Action is Details");
            Assert.AreEqual("12-25-1966", routeData.Values["entryDate"], "EntryDate is date passed");
 
        }
 
        [TestMethod]
        public void TestBadArchiveRoute()
        {
            // Arrange
            var routes = new RouteCollection();
            GlobalApplication.RegisterRoutes(routes);
 
            // Act
            var context = new FakeHttpContext("~/Archive/something");
            var routeData = routes.GetRouteData(context);
 
            // Assert
            Assert.AreNotEqual("Blog", routeData.Values["controller"], "Controller is not Blog");
        }
 
        [TestMethod]
        public void TestCatchAllRoute()
        {
            // Arrange
            var routes = new RouteCollection();
            GlobalApplication.RegisterRoutes(routes);
 
            // Act
            var context = new FakeHttpContext("~/Product/a/b/c/d");
            var routeData = routes.GetRouteData(context);
 
            // Assert
            Assert.AreEqual("a/b/c/d", routeData.Values["values"], "Got catchall values");
        }
    }
}
</textarea>
</div>
<p class="al-p">这是Visual Studio Test（MS Test）单元测试。当然你也可以使用不同的测试框架，如NUnit或xUnit。下面是这个单元测试工作的方式。</p>
<p class="al-p">首先，新建了一个路由集合，并将其传递给Global.asax文件中定义的RegisterRoutes()方法。Global.asax文件对应着一个名为GlobalApplication的类。</p>
<p class="al-p">接下来，创建了一个仿制的HTTP Context，其中包含了待测试的URL。例如，在等一个测试中，URL ~/Archive/12-25-1966被传递到仿制的HTTP Context对象的构造器中。仿制的HTTP Context对象是我在Tip #12中创建的仿制MVC对象的修改版。本文后面可以下载到源代码，其中的MvcFakes项目中包含了这些仿制对象。</p>
<p class="al-p">接下来，在仿制的上下文上到用了GetRouteData()方法，并返回了路由数据。路由数据表示将URL传递给应用程序路由表，经过解释后得到的结果。换句话说，路由数据是将URL与路由表中的路由进行对比后得到的结果。</p>
<p class="al-p">最后，该测试判断路由数据中是否包含需要的值。在第一个测试里，验证了控制器名字、控制器操作和Id的值。依照该测试，空的URL ~/应该映射到Home控制器、Index操作，并且Id的值是String.Empty。</p>
<p class="al-p">第二个测试检查了类似~/Archive/12-25-1966这样的请求是否映射到Blog控制器、Details操作，并创建了名为entryDate的操作。</p>
<p class="al-p">第三个测试检查了类似~/Archive/something这样的请求不能映射到Blog控制器。因为该URL不包含恰当的entryDate，所以不能被Blog控制器处理。</p>
<p class="al-p">最后一个测试验证了catchall路由能够正确工作。该测试检查了~/Product/a/b/c/d得到了解析，使得values参数等于a/b/c/d。换句话说，它检查了catchall控制器的catch-all部分。</p>
<h2 class="al-t1">小结</h2>
<p class="al-p">在这个Tip中，我向你展示了一种简单的测试自定义ASP.NET MVC路由的方法。我建议任何时候只要你修改了Global.asax文件中的默认路由，都应该对其进行单元测试。</p>
<p class="al-p">此处下载源代码：<a href="http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip13/Tip13.zip" title="此处下载源代码">http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip13/Tip13.zip</a>。
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shCore.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushCSharp.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushXml.js"></script>
<script language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('al-code'); 
</script><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1252374.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/43001/" target="_blank">[新闻]新编程语言——微软的“M”语言</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</a>]]></description></item><item><title>[翻译] AJAX Panels with ASP.NET MVC</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/07/26/AJAX-Panels-with-ASPNET-MVC.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Sat, 26 Jul 2008 01:00:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/07/26/AJAX-Panels-with-ASPNET-MVC.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1251891.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/07/26/AJAX-Panels-with-ASPNET-MVC.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1251891.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1251891.html</trackback:ping><description><![CDATA[<h1 class="al-title">AJAX Panels with ASP.NET MVC</h1>
<div class="al-copy">
 <p>原文地址：<a href="http://www.singingeels.com/Articles/AJAX_Panels_with_ASPNET_MVC.aspx" title="AJAX Panels with ASP.NET MVC" target="_blank">http://www.singingeels.com/Articles/AJAX_Panels_with_ASPNET_MVC.aspx</a></p>
 <p>原著作者：Timothy Khouri</p>
 <p>翻译：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
 <p>摘要：ASP.NET MVC Preview 4带来了一些AJAX支持，能够适应MVC设计模式的本质。这篇文章向你展示了在ASP.NET MVC中使用&#8220;延迟加载AJAX面板&#8221;是如何使其变得不可思议的简单。</p>
</div>
<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<p class="al-p">ASP.NET MVC Preview 4带来了一些AJAX支持，能够适应MVC设计模式的本质。这篇文章向你展示了在ASP.NET MVC中使用&#8220;延迟加载AJAX面板&#8221;是如何使其变得不可思议的简单。</p>
<h2 class="al-t1">首先，&#8220;ASP.NET AJAX&#8221;的问题</h2>
<p class="al-p">由于&#8220;Web Froms&#8221;（传统的ASP.NET）是基于同时包含了表现层和后台代码的&#8220;页面&#8221;的，所以ASP.NET AJAX并没有像它本应该的那样光芒四射。很多步入AJAX领域的ASP.NET开发者只是向页面中随意地放置一些&#8220;UpdatePanel&#8221;来使其&#8220;看上去&#8221;支持AJAX。实际上，这只是防止了页面的&#8220;闪烁&#8221;，而页面还是进行了完整的回发，并且要经历整个页面的生存周期。</p>
<p class="al-p">这并不是说这些问题是ASP.NET AJAX的责任，而是由是否需要使用完全的AJAX的不同心态造成的。公平地讲，有比UpdatePanel控件更好的ASP.NET AJAX选择。包括：</p>
<ul class="al-dotul">
 <li><a href="http://www.singingeels.com/Articles/Using_Page_Methods_in_ASPNET_AJAX.aspx" target="_blank" title="Using Page Methods in ASP.NET AJAX">Page Methods</a>——直接调用位于后台代码中（服务器上）的方法。</li>
 <li><a href="http://www.singingeels.com/Articles/Consuming_Web_Services_With_ASPNET_AJAX.aspx" target="_blank" title="Consuming Web Services With ASP.NET AJAX">Web Services</a>——调用位于应用程序的Web Services中的方法。</li>
</ul>
<p class="al-p">这两种选择比使用UpdatePanel&#8220;好&#8221;在无需重新加载整个页面，只需向客户端呈现一部分HTML即可。但它们&#8220;坏&#8221;在你需要使用JavaScript实现所有的表现逻辑（不用别人说也知道这是很恐怖的）。</p>
<h2 class="al-t1">MVC AJAX给你转机</h2>
<p class="al-p">如果你能得到和使用UpdatePanel一样的ASP.NET呈现能力，<strong>并且</strong>所有的代码都能分离开，性能也和访问Web Services一样，你会不吃惊吗？来吧，一起感谢MVC设计模式的本质吧——还要感谢ASP.NET MVC——你能！</p>
<p class="al-p">我们来看一下现实世界中创建&#8220;延迟加载&#8221;AJAX面板的问题。假设我们有一个Web应用程序，用于向客户端发布一些巨大的报表。如果我们不使用AJAX，每个报表都会增加页面的整体加载时间。因此，我们将异步地请求每个报表（使用AJAX），是的页面自身能够立即加载，而每个报表都会在运行完毕后显示出来。</p>
<p class="al-p">我们将向页面中添加4个&#8220;报表&#8221;。每个报表都要运行3到5秒。因此如果我们使用传统的Web Forms，这个页面将要加载12到20秒。但由于有了MVC，我们可以将加载时间降低到5秒，并且页面看上去仍然很漂亮。</p>
<div class="al-ins">
 <p class="al-ins-title">注意</p>
 <p class="al-p">有很重要的一点需要注意。上面提到的性能收益会受到一些因素的限制。你必须考虑到服务器要处理所有这些请求，这会使最终的结果有所下降。另外，很多浏览器只允许2个并发的下载，因此对于上面的例子，你节省的时间会降低约50%。</p>
</div>
<h2 class="al-t1">使用Ajax.Form方法</h2>
<p class="al-p">MVC Preview 4在&#8220;this.Ajax&#8221;字段中为所有MVC页面和MVC用户控件添加了一些方法。&#8220;Ajax.Form&#8221;方法和&#8220;Html.Form&#8221;方法类似，但它会添加一些JavaScript来帮助确保可以异步地发送请求。另外，这里还能为应该返回的结果定义一个HTML元素。</p>
<p class="al-p">例如，如果你想POST诸如&#8220;发送邮件&#8221;这样的操作，并希望服务器能将&#8220;Your email has been sent&#8221;这样的文字放在一个ID是&#8220;resultDiv&#8221;的&lt;div&gt;内，你需要这样做：</p>
<textarea name="al-code" class="html" cols="60" rows="25">
<div id="resultDiv"></div>
<% using (Ajax.Form("SendMail", new AjaxOptions { UpdateTargetId = "resultDiv" })) { %>
   <!-- Your form elements here... -->
<% } %>
</textarea>
<p class="al-p">上面的代码会生成下面的&lt;form&gt;标签：</p>
<textarea name="al-code" class="html" cols="60" rows="25">
<form action="/Home/SendMail" onsubmit="Sys.Mvc.AsyncForm.handleSubmit(this, { insertionMode: 0, updateTargetId: 'resultDiv' }); return false;">
   <!-- Your form elements here... -->
</form>
</textarea>
<p class="al-p">和我们前面提到的一样，这非常像&#8220;Html.Form&#8221;方法生成的form，但你也能清楚地看到&#8220;onsubmit&#8221;方法被替换为使用AJAX来发送请求，而且你也能看到&#8220;resultDiv&#8221;参数被传递到服务器了。</p>
<p class="al-p">服务器会和平常一样接收这个请求，它也会和平常一样发送请求的数据。这个魔术发生在ASP.NET MVC内部。从服务器传回的响应将会放在我们的&lt;div&gt;中，页面的其他部分不会改变。</p>
<p class="al-p">这是真的，非常简单的AJAX。然而，还必须向你指出一个问题，该表单只在用户明确地点击了提交按钮（&lt;input type=&quot;submit&quot;&gt;）&#8220;提交&#8221;该表单时，它才会与服务器联系。为了解决这个问题，我们需要添加一行JavaScript使其能够自动提交表单，异步地请求报表。我还对&#8220;Form&#8221;方法进行了些微改动，添加了一个HTML ID属性，以便我能在JavaScript中访问它。</p>
<p class="al-p">现在我们新的代码看起来是下面这样：</p>
<textarea name="al-code" class="html" cols="60" rows="25">
<div id="resultDiv"></div>
<% using (Ajax.Form("ReportOne", null,
      new AjaxOptions { UpdateTargetId = "resultOne" },
      new { id="reportFormOne" })) { } %>
<script type="text/javascript">
   $get("reportFormOne").onsubmit();
</script>
</textarea>
<div class="al-ins">
 <p class="al-ins-title">提示</p>
 <p class="al-p">如果我直接调用&#8220;Sys.Mvc.AsyncForm.handleSubmit&#8221;方法，上面的代码还能更简单些。但我选择让MVC为我创建表单，然后通过JavaScript访问它，因此如果JavaScript方法将来发生了变化，我依然能够使用。</p>
</div>
<h2 class="al-t1">看看结果吧！</h2>
<p class="al-p">使用上面的方法，再加上我从Internet上弄来的&#8220;loading gif&#8221;，我们就有了这样一个页面，它可以动态地（并且是异步地）加载报表，并在可用的时候立即显示给用户。下面是最终结果的一些截图：</p>
<p class="al-p"><img src="http://www.singingeels.com/Articles/Articles/UserImage.aspx?ImageID=12aa9bc1-f24d-410c-a253-c9cc4f488a15" alt="" /></p>
<p class="al-p"><img src="http://www.singingeels.com/Articles/Articles/UserImage.aspx?ImageID=ee066ed0-a0c9-4e1d-b229-7d2b0cb4ceda" alt="" /></p>
<p class="al-p">这里是上面的项目的源代码。记住，该项目是在ASP.NET MVC Preview 4下编写和编译的，你下载的时候可能已经过时了（译注：真希望它赶紧过时）：</p>
<p class="al-p">此处下载源代码：<a href="http://www.singingeels.com/Articles/Articles/UserFile.aspx?FileID=762dd2c2-7d9e-4022-9de3-f0e05f2b10e3" title="此处下载源代码">SingingEels_MVC_Asyncronous_AJAX_Panels.zip</a>。
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shCore.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushCSharp.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushXml.js"></script>
<script language="javascript"> 
dp.SyntaxHighlighter.HighlightAll('al-code'); 
</script><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1251891.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/43000/" target="_blank">[新闻]Firefox 3.1 Beta</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</a>]]></description></item><item><title>[翻译] ASP.NET MVC Tip #12 – 仿制控制器上下文</title><link>http://www.cnblogs.com/AndersLiu/archive/2008/07/26/asp-net-mvc-tip-12-faking-the-controller-context.html</link><dc:creator>Anders Liu</dc:creator><author>Anders Liu</author><pubDate>Fri, 25 Jul 2008 23:39:00 GMT</pubDate><guid>http://www.cnblogs.com/AndersLiu/archive/2008/07/26/asp-net-mvc-tip-12-faking-the-controller-context.html</guid><wfw:comment>http://www.cnblogs.com/AndersLiu/comments/1251881.html</wfw:comment><comments>http://www.cnblogs.com/AndersLiu/archive/2008/07/26/asp-net-mvc-tip-12-faking-the-controller-context.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnblogs.com/AndersLiu/comments/commentRss/1251881.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/AndersLiu/services/trackbacks/1251881.html</trackback:ping><description><![CDATA[<h1 class="al-title">ASP.NET MVC Tip #12 &#8211; 仿制控制器上下文</h1>
<div class="al-copy">
 <p>原文地址：<a href="http://weblogs.asp.net/stephenwalther/archive/2008/06/30/asp-net-mvc-tip-12-faking-the-controller-context.aspx" title="ASP.NET MVC Tip #12 &#8211; Faking the Controller Context" target="_blank">http://weblogs.asp.net/stephenwalther/archive/2008/06/30/asp-net-mvc-tip-12-faking-the-controller-context.aspx</a></p>
 <p>翻译：<a href="http://andersliu.cnblogs.com" title="Anders Liu">Anders Liu</a></p>
</div>
<div class="al-summary">
 <p>摘要：在这个Tip中，Stephen Walther介绍了在为ASP.NET MVC应用程序创建单元测试时，如何深入ASP.NET内部进行测试。Stephen Walther介绍了如何创建一组标准的仿制对象（Fake Object）来模仿当前用户、当前用户角色、请求参数、会话状态和Cookie。</p>
</div>
<div class="al-adc">
<script type="text/javascript"><!--
google_ad_client = "pub-0410788977836329";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text_image";
//2007-07-05: cnblogs.com
google_ad_channel = "5389395912";
google_color_border = "336699";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
google_ui_features = "rc:0";
//-->
</script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<p class="al-p">ASP.NET MVC用程序比ASP.NET Web Forms应用程序更加可测试。ASP.NET MVC的每个特性从设计伊始就一直注意可测试性。然而，ASP.NET MVC应用那个程序中还是有一些方面是难以测试的。尤其你会发现，在ASP.NET MVC中测试ASP.NET内部仍然是一个挑战。</p>
<p class="al-p">我所说的&#8220;ASP.NET内部&#8221;是什么意思呢？就是指那些出现在HttpContext中的东西。也就是这些对象：</p>
<ul class="al-dotul">
 <li>Request.Forms——POST到一个页面的表单参数。</li>
 <li>Request.QueryString——传递到一个页面的查询字符串参数。</li>
 <li>User——发起页面请求的当前用户。</li>
 <li>Reqest.Cookies——传递到页面的浏览器Cookie。</li>
 <li>Session——会话状态对象。</li>
</ul>
<p class="al-p">例如，假设你想对一个特定的控制器——其实是一个特定的会话状态条目——进行测试，你需要创建类似下面这样的单元测试：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
[TestMethod]
public void TestSessionState()
{
    // Arrange
    var controller = new HomeController();
    // Act
    var result = controller.TestSession() as ViewResult;
    // Assert
    Assert.AreEqual("wow!", controller.HttpContext.Session["item1"]);
}
</textarea>
<p class="al-p">该测试检查名为TestSession()的控制器操作是否向会话状态中添加了一个新的名叫item1的条目、其值是否为"wow!"。</p>
<p class="al-p">下面的控制器操作可以通过这一测试：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
public ViewResult TestSession()
{
    Session["item1"] = "wow!";
    return View();
}
</textarea>
<p class="al-p">该控制操作向会话状态中插入了一个具有期望值的条目。</p>
<p class="al-p">不幸的是，如果你运行该单元测试，测试会失败。失败的原因是出现了一个NullReferenceException异常。此处的问题在于在单元测试的上下文中，会话状态并不存在。事实上，在一个测试方法中，任何ASP.NET内部的东西都不存在。这意味着你无法测试Cookies、表单参数、查询字符串参数和用户实体或用户角色。</p>
<h2 class="al-t1">Mocking VS Stubbing</h2>
<p class="al-p">如果你需要编写一个用户到了ASP.NET内部对象的单元测试，那么你必须做出选择。你有两种选择，一是使用Mock Ojbect Framework，或者是使用一组仿制类（Fake Class）。</p>
<p class="al-p">第一个选择是伪造（mock）ASP.NET内部对象，这可以使用Moq、Typemock Isolator或Rhino Mocks这样的Mock Ojbect Framework来完成。使用这些框架中的任何一种都可以生成假扮ASP.NET内部对象的对象。如果你想更多地了解这些框架，可以看我之前针对这三种Mock Ojbect Framework撰写的博客：</p>
<ul class="al-ul">
 <li><a href="http://weblogs.asp.net/stephenwalther/archive/2008/06/11/tdd-introduction-to-moq.aspx" target="_blank">http://weblogs.asp.net/stephenwalther/archive/2008/06/11/tdd-introduction-to-moq.aspx</a></li>
 <li><a href="http://weblogs.asp.net/stephenwalther/archive/2008/03/22/tdd-introduction-to-rhino-mocks.aspx" target="_blank">http://weblogs.asp.net/stephenwalther/archive/2008/03/22/tdd-introduction-to-rhino-mocks.aspx</a></li>
 <li><a href="http://weblogs.asp.net/stephenwalther/archive/2008/03/16/tdd-introduction-to-typemock-isolator.aspx" target="_blank">http://weblogs.asp.net/stephenwalther/archive/2008/03/16/tdd-introduction-to-typemock-isolator.aspx</a></li>
</ul>
<p class="al-p">另外一种选择就是创建一组类，用于模拟ASP.NET内部对象。这组类可以只创建一次，然后在将来所有的ASP.NET MVC项目中使用它们。</p>
<p class="al-p">在这个Tip中，我将介绍第二种方法。我将向你展示如何通过创建一组标准的ASP.NET内部对象仿制类来简单地测试ASP.NET内部对象，而无需使用Mock Object Framework。</p>
<h2 class="al-t1">创建仿制控制器上下文</h2>
<p class="al-p">在该Tip的结尾，你可以下载到这些仿制类。我创建了一组ASP.NET内部对象仿制类，名字分别是：</p>
<ul class="al-dotul">
 <li>FakeControllerContext</li>
 <li>FakeHttpContext</li>
 <li>FakeHttpRequest</li>
 <li>FakeHttpSessionState</li>
 <li>FakeIdentity</li>
 <li>FakePrincipal</li>
</ul>
<p class="al-p">在单元测试中创建FakeControllerContext的实例，并将其赋值给控制器的ControllerContext属性，就可以开始使用这些仿制类了。例如，下面展示了如何利用FakeControllerContext类来在单元测试中仿制一个特定的用户。</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
var controller = new HomeController();
controller.ControllerContext = new FakeControllerContext(controller, "Stephen");
</textarea>
<p class="al-p">当把FakeControllerContext赋给控制器后，在单元测试的其余部分，控制器将会使用这个上下文。让我们来通过不同的例子看一看如何使用FakeControllerContext来模拟不同的ASP.NET内部对象。</p>
<h2 class="al-t1">测试表单参数</h2>
<p class="al-p">假设你希望向操作传递不同的表单参数来测试控制器操作的行为。另外，假设控制器操作象下面这样直接访问Reques.Form：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
public ActionResult Insert()
{
  ViewData["firstname"] = Request.Form["firstName"];
  ViewData["lastName"] = Request.Form["lastName"];
  return View();
}
</textarea>
<p class="al-p">如何测试控制器操作呢？对于这种情况，你可以使用接受一组表单参数的FakeControllerContext构造器。下面的测试检查了firstName和lastName表单参数是否被保存到了视图数据中：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
[TestMethod]
public void TestFakeFormParams()
{
    // Create controller
    var controller = new HomeController();
    // Create fake controller context
    var formParams = new NameValueCollection { { "firstName", "Stephen" }, {"lastName", "Walther"} };
    controller.ControllerContext = new FakeControllerContext(controller, formParams);
    // Act
    var result = controller.Insert() as ViewResult;
    Assert.AreEqual("Stephen", result.ViewData["firstName"]);
    Assert.AreEqual("Walther", result.ViewData["lastName"]);
}
</textarea>
<p class="al-p">FakeControllerContext的表单参数是通过一个NameValueCollection创建的。表单参数的仿制集合被传递给FakeControllerContext的构造器。</p>
<h2 class="al-t1">测试查询字符串参数</h2>
<p class="al-p">假设你需要测试查询字符串参数是否被传递到一个视图中。查询字符串可以直接通过Request.QueryString集合访问。例如，控制器操作看起来可能是下面这样：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
public ViewResult Details()
{
  ViewData["key1"] = Request.QueryString["key1"];
  ViewData["key2"] = Request.QueryString["key2"];
  ViewData["count"] = Request.QueryString.Count;
  return View();
}
</textarea>
<p class="al-p">在这种情况下，你可以通过将一个NameValueCollection传递给FakeControllerContext的构造器来仿制查询字符串：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
[TestMethod]
public void TestFakeQueryStringParams()
{
    // Create controller
    var controller = new HomeController();
    // Create fake controller context
    var queryStringParams = new NameValueCollection { { "key1", "a" }, { "key2", "b" } };
    controller.ControllerContext = new FakeControllerContext(controller, null, queryStringParams);
    // Act
    var result = controller.Details() as ViewResult;
    Assert.AreEqual("a", result.ViewData["key1"]);
    Assert.AreEqual("b", result.ViewData["key2"]);
    Assert.AreEqual(queryStringParams.Count, result.ViewData["count"]);
}
</textarea>
<p class="al-p">注意查询字符串要作为FakeControllerContext构造器的第二个参数传入。</p>
<h2 class="al-t1">测试用户</h2>
<p class="al-p">你可能需要测试控制器操作的安全性。例如，你可能希望仅为一个特定的已验证用户显示某个视图。下面的控制器操作为已验证用户显示一个Secret视图，而将所有其他用户重定向到Index视图：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
public ActionResult Secret()
{
    if (User.Identity.IsAuthenticated)
    {
        ViewData["userName"] = User.Identity.Name;
        return View("Secret");
    }
    else
    {
        return RedirectToAction("Index");
    }
}
</textarea>
<p class="al-p">你可以使用FakeController来测试该操作的行为是否为你预期的：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
[TestMethod]
public void TestFakeUser()
{
    // Create controller
    var controller = new HomeController();
 
    // Check what happens for authenticated user
    controller.ControllerContext = new FakeControllerContext(controller, "Stephen");
    var result = controller.Secret() as ActionResult;
    Assert.IsInstanceOfType(result, typeof(ViewResult));
    ViewDataDictionary viewData = ((ViewResult) result).ViewData;
    Assert.AreEqual("Stephen", viewData["userName"]);
 
    // Check what happens for anonymous user
    controller.ControllerContext = new FakeControllerContext(controller);            
    result = controller.Secret() as ActionResult;
    Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
}
</textarea>
<p class="al-p">该测试实际上测试了三样东西（也许应该分成多个测试）。首先，它测试了一个以验证用户是否从控制器操作取得了所需的视图。其次，它测试了已验证用户的用户名是否被成功地添加到了试图数据中。最后，它检查了匿名用户在控制其操作执行时是否被重定向了。</p>
<h2 class="al-t1">测试用户角色</h2>
<p class="al-p">你可能希望根据角色的不同，为不同的用户显示不同的内容。例如，某些内容可能只能由管理员（Admins）浏览。假设你有一个类似下面这样的控制器操作：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
public ActionResult Admin()
{
    if (User.IsInRole("Admin"))
    {
        return View("Secret");
    }
    else
    {
        return RedirectToAction("Index");
    }
}
</textarea>
<p class="al-p">如果你是一个具备Admin角色的成员，该控制器操作将返回Secret视图。</p>
<p class="al-p">你可以通过下面的测试方法测试该控制器操作：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
[TestMethod]
public void TestFakeUserRoles()
{
    // Create controller
    var controller = new HomeController();
 
    // Check what happens for Admin user
    controller.ControllerContext = new FakeControllerContext(controller, "Stephen", new string[] {"Admin"});
    var result = controller.Admin() as ActionResult;
    Assert.IsInstanceOfType(result, typeof(ViewResult));
 
    // Check what happens for anonymous user
    controller.ControllerContext = new FakeControllerContext(controller);
    result = controller.Admin() as ActionResult;
    Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
}
</textarea>
<p class="al-p">该操作能够验证是否只有Admin角色的成员能够看到Secret视图。该测试还检查了匿名用户是否被重定向到了其他页面。</p>
<h2 class="al-t1">测试浏览器Cookies</h2>
<p class="al-p">假设你需要测试访问了浏览器Cookies的操作。例如，你可能通过浏览器端Cookies传递了一个客户ID。如何测试这类操作呢？</p>
<p class="al-p">下面的控制器方法简单地讲一个浏览器Cookie添加到了视图数据中：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
public ViewResult TestCookie()
{
    ViewData["key"] = Request.Cookies["key"].Value;
    return View();
}
</textarea>
<p class="al-p">你可以通过创建一个SessionItemCollection并将其传递到FakeControllerContext中来测试该控制器操作：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
[TestMethod]
public void TestCookies()
{
    // Create controller
    var controller = new HomeController();
 
    // Create fake Controller Context
    var cookies = new HttpCookieCollection();
    cookies.Add( new HttpCookie("key", "a"));
    controller.ControllerContext = new FakeControllerContext(controller, cookies);
    var result = controller.TestCookie() as ViewResult;
 
    // Assert
    Assert.AreEqual("a", result.ViewData["key"]);
}
</textarea>
<p class="al-p">该测试验证了从知其操作是否将名为key的Cookie添加到了视图数据中。</p>
<h2 class="al-t1">测试会话状态</h2>
<p class="al-p">最后的一个例子了。我们来看一下测试访问了会话状态的控制器操作：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
public ViewResult TestSession()
{
    ViewData["item1"] = Session["item1"];
    Session["item2"] = "cool!";
    return View();
}
</textarea>
<p class="al-p">该控制器操作同时读和写了会话状态。它从会话状态中取出了一个名为item1的条目，并将其添加到试图数据中。该控制器操作还创建了一个名为item2的会话状态条目。</p>
<p class="al-p">使用下面的单元测试可以测试该控制器操作：</p>
<textarea name="al-code" class="C#" cols="60" rows="25">
[TestMethod]
public void TestSessionState()
{
    // Create controller
    var controller = new HomeController();
 
    // Create fake Controller Context
    var sessionItems = new SessionStateItemCollection();
    sessionItems["item1"] = "wow!";
    controller.ControllerContext = new FakeControllerContext(controller, sessionItems);
    var result = controller.TestSession() as ViewResult;
 
    // Assert
    Assert.AreEqual("wow!", result.ViewData["item1"]);
 
    // Assert
    Assert.AreEqual("cool!", controller.HttpContext.Session["item2"]);
}
</textarea>
<p class="al-p">注意这里创建了一个SessionStateItemCollection并将其传给了FakeControllerContext的构造器。SessionStateItemCollection表示会话状态中的所有条目。</p>
<h2 class="al-t1">小结</h2>
<p class="al-p">在这个Tip中，我介绍了如何测试ASP.NET内部对象——如表单参数、查询字符串、用户实体、用户角色、Cookies和会话状态——使用的是一组标准的仿制类。通过下面的链接可以下载到这些仿制类的完整代码（同时有C#和VB.NET代码）。</p>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shCore.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushCSharp.js"></script>
<script src="http://tools.xpus.net/dpsh-1.5.1/Scripts/shBrushXml.js"></script>
<script language="javascript">
dp.SyntaxHighlighter.HighlightAll('al-code');
</script><img src ="http://www.cnblogs.com/AndersLiu/aggbug/1251881.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/43000/" target="_blank">[新闻]Firefox 3.1 Beta</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</a>]]></description></item></channel></rss>