<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>裁纸刀下 &#187; ASP.NET</title>
	<atom:link href="http://ofcss.com/tags/aspnet/feed" rel="self" type="application/rss+xml" />
	<link>http://ofcss.com</link>
	<description>独立 自由 诚信 宽容 责任 平常心</description>
	<lastBuildDate>Thu, 19 Jan 2012 14:24:38 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>ASP.NET MVC 最佳实践（四）</title>
		<link>http://ofcss.com/2009/07/08/asp-net-mvc-best-practices-part-4.html</link>
		<comments>http://ofcss.com/2009/07/08/asp-net-mvc-best-practices-part-4.html#comments</comments>
		<pubDate>Tue, 07 Jul 2009 17:34:45 +0000</pubDate>
		<dc:creator>小李刀刀</dc:creator>
				<category><![CDATA[所谓技术]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[开发]]></category>
		<category><![CDATA[最佳实践]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://wukangrui.net/2009/07/08/asp-net-mvc-best-practices-part-4.html</guid>
		<description><![CDATA[<p>本系列翻译自 Kazi Manzur Rashid 的博客，由于篇幅关系，原文中的一篇文章在本系列中将拆解成多篇发布。本篇包括原文第二部分第18-21节。 <a href="http://ofcss.com/2009/07/08/asp-net-mvc-best-practices-part-4.html">Continue reading <span class="meta-nav">&#8594;</span></a></p>
]]></description>
			<content:encoded><![CDATA[<p><em>本系列翻译自 </em><a href="http://weblogs.asp.net/rashid/default.aspx"><em>Kazi Manzur Rashid</em></a><em> 的博客，由于翻译水平有限，本系列可能存在误解偏差或者翻译不准的地方，建议对比<a href="http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx">原文</a>进行阅读。由于篇幅关系，原文中的一篇文章在本系列中将拆解成多篇发布。本篇包括原文第二部分第18-21节。如果你没有看过之前的第一部分，也许你想先看看 <a href="http://wukangrui.net/2009/06/28/asp-net-mvc-best-practices-part-1.html">《ASP.NET MVC 最佳实践（一）》</a>、《<a href="http://wukangrui.net/2009/06/28/asp-net-mvc-best-practices-part-1.html">ASP.NET MVC 最佳实践（二）</a>》和《<a href="http://wukangrui.net/2009/07/07/asp-net-mvc-best-practices-part-1-2.html" rel="prev" target="_blank">ASP.NET MVC 最佳实践（三）</a>》。</em></p>
</p>
<p><em></em></p>
</p>
<p> <span id="more-858"></span>
</p>
<h4>18. HtmlHelper 扩展</h4>
<p>首先，参考一下 <a href="http://blog.wekeroad.com/" rel="external" target="_blank">Rob Conery</a> 的<a href="http://blog.wekeroad.com/blog/asp-net-mvc-avoiding-tag-soup/" rel="external" target="_blank">这篇文章</a>，我完全同意他的观点，你应该为每种情况创建 Helper 方法，此外我还建议你像 <a href="http://www.asp.net/mvc/" rel="tag" target="_blank">ASP.NET MVC</a> 团队那样为可重用的 UI 元素创建 Helper 方法，但是对于在哪里放置我们正在实践的这些方法，我有不同的建议。</p>
<h6>应用程序开发者</h6>
<p>只为你需要在多个视图中重复使用的HtmlHealper创建单独的扩展方法。对于只在一个视图中使用的帮助方法，你可以为整个视图创建一个Helper类，然后创建一个HtmlHelper来返回这个视图Helper类，而把只在这个视图中使用的扩展方法作为类内部的方法。（这部分未按原文翻译，请自行参照原文）例如：</p>
<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">DashboardHtmlHelper </span>{
    <span style="color: blue">private readonly </span><span style="color: #2b91af">HtmlHelper </span>_htmlHelper;

    <span style="color: blue">public </span>DashboardHtmlHelper(<span style="color: #2b91af">HtmlHelper </span>htmlHelper) {
        _htmlHelper = htmlHelper;
    }

    <span style="color: blue">public string </span>DoThis() {
        <span style="color: green">//Your Code
    </span>}

    <span style="color: blue">public string </span>DoThat() {
        <span style="color: green">//Your Code
    </span>}
}

<span style="color: blue">public static class </span><span style="color: #2b91af">HtmlHelperExtension </span>{
    <span style="color: blue">public static </span><span style="color: #2b91af">DashboardHtmlHelper </span>Dashboard(<span style="color: blue">this </span><span style="color: #2b91af">HtmlHelper </span>htmlHelper) {
        <span style="color: blue">return new </span><span style="color: #2b91af">DashboardHtmlHelper</span>(htmlHelper);
    }
}</pre>
<p>然后在视图中这样调用：</p>
<pre class="code"><span style="background: #ffee62">&lt;%</span><span style="color: blue">= </span>Html.Dashboard().DoThis() <span style="background: #ffee62">%&gt;</span></pre>
<h6>UI 组件开发人员</h6>
<p>如果你在开发的是可以在多个 <a href="http://www.asp.net/mvc/" rel="tag" target="_blank">ASP.NET MVC</a> 应用程序中重用的 UI 组件系列，你可以像上面那样为你的整个系列组件创建一个单一名字的Helper。假如你开发的是商业组件，那你可以用你的公司名字来为这个单一的Helper命名，然后在这个Helper中添加方法。否则很容易出现方法的命名冲突。</p>
<p>要是你正在学习 <a href="http://mvccontrib.codeplex.com" rel="external" target="_blank">MVCContrib.org</a> 的做法，为 IViewDataContainer 做扩展的话，道理也是一样的。</p>
<h4>19. Html编码</h4>
<p>在从用户端获取信息的任何情况下，总是使用 Html.Encode(“User Input”) 来编码文本节点，用 Html.AttributeEncode(“User Input”) 来编码Html 元素属性。</p>
<h4>20. 不要把Javascript脚本放在你的视图中</h4>
<p>不要把你的javascript脚本和html混杂在一起，创建一个单独的 js 文件，把脚本代码放在里面。有时候你可能需要在你的 javascript 代码中传递视图数据，这种特殊的情况下，也只把初始化代码放在视图中。例如，你正在开发 web 2.0 风格的应用，你需要在这个应用中传递 ajax 方法的url，并且需要在 javascript 脚本中传递一些其它的模型数据，这种情况下，你可以参考下面的方式：</p>
<p>视图：</p>
<pre class="code"><span style="color: blue">&lt;</span><span style="color: #a31515">div </span><span style="color: red">id</span><span style="color: blue">=&quot;sideBar&quot; </span><span style="color: red">class</span><span style="color: blue">=&quot;column&quot;&gt;&lt;/</span><span style="color: #a31515">div</span><span style="color: blue">&gt;
        &lt;</span><span style="color: #a31515">script </span><span style="color: red">type</span><span style="color: blue">=&quot;text/javascript&quot;&gt;
            </span>$(document).ready(<span style="color: blue">function</span>(){
                Story.init(<span style="color: #a31515">'&lt;%= Model.UrlFormat %&gt;'</span>, <span style="color: #a31515">'&lt;%= Url.NoIcon() %&gt;'</span>, &lt;%= Model.PageCount %&gt;, &lt;%= Model.StoryPerPage %&gt;, &lt;%= Model.CurrentPage %&gt;, <span style="color: #a31515">'&lt;%= Model.SelectedTab %&gt;'</span>, <span style="color: #a31515">'&lt;%= Model.SelectedOrderBy %&gt;'</span>);
            });
        <span style="color: blue">&lt;/</span><span style="color: #a31515">script</span><span style="color: blue">&gt;
    &lt;/</span><span style="color: #a31515">div</span><span style="color: blue">&gt;
&lt;/</span><span style="color: #a31515">asp</span><span style="color: blue">:</span><span style="color: #a31515">Content</span><span style="color: blue">&gt;</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Javascript：</p>
<pre class="code"><span style="color: blue">var </span>Story = {
    _urlFormat: <span style="color: #a31515">''</span>,
    _noIconUrl: <span style="color: #a31515">''</span>,
    _pageCount: 0,
    _storyPerPage: 0,
    _currentPage: 0,
    _currentTab: <span style="color: #a31515">''</span>,
    _currentOrderBy: <span style="color: #a31515">''</span>,

    init: <span style="color: blue">function</span>(urlFormat, noIconUrl, pageCount, storyPerPage, currentPage, currentTab, currentOrderBy) {
        Story._urlFormat = urlFormat;
        Story._noIconUrl = noIconUrl;
        Story._pageCount = pageCount;
        Story._storyPerPage = storyPerPage;
        Story._currentPage = currentPage;
        Story._currentTab = currentTab;
        Story._currentOrderBy = currentOrderBy;

        <span style="color: green">//More Codes
    </span>}
}</pre>
<p>这里要为不太熟悉上面的 javascript 的同学解释一下，这是一个在javascript中创建静态类的示例代码。对了，我还差点忘了提一点，永远不要在你的javascript文件中硬编码 ajax 方法的url，虽然 <a href="http://videos.visitmix.com/MIX09/T62F" rel="external" target="_blank">Rob Conery</a> 和 <a href="http://aspnetpodcast.com/CS11/blogs/asp.net_podcast/archive/2008/07/28/asp-net-podcast-show-121-phil-haack-with-an-asp-net-mvc-demo-video.aspx" rel="external" target="_blank">Phil Haack</a> 在他们的教程中是这样干的。这是一个很不好的方式，它破坏了 ASP.NET 路径重写的优雅性。</p>
<h4>21. 使用 jQuery 和 jQuery UI</h4>
<p>使用 jQuery 和 jQuery UI，它是最好的js库，并且可以使用 Google CDN 托管服务来加载这些库（对于国内用户，如果使用 google 托管的库一定要有承担风险的准备，具体原因嘛，看看国内6月24日左右的IT新闻就知道了）。</p>
<pre class="code"><span style="color: blue">&lt;</span><span style="color: #a31515">link </span><span style="color: red">type</span><span style="color: blue">=&quot;text/css&quot; </span><span style="color: red">href</span><span style="color: blue">=&quot;http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.1/themes/{YOUR Prefered Theme}/jquery-ui.css&quot; </span><span style="color: red">rel</span><span style="color: blue">=&quot;stylesheet&quot;/&gt;
&lt;</span><span style="color: #a31515">script </span><span style="color: red">type</span><span style="color: blue">=&quot;text/javascript&quot; </span><span style="color: red">src</span><span style="color: blue">=&quot;http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js&quot;&gt;&lt;/</span><span style="color: #a31515">script</span><span style="color: blue">&gt;
&lt;</span><span style="color: #a31515">script </span><span style="color: red">type</span><span style="color: blue">=&quot;text/javascript&quot; </span><span style="color: red">src</span><span style="color: blue">=&quot;http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.1/jquery-ui.js&quot;&gt;&lt;/</span><span style="color: #a31515">script</span><span style="color: blue">&gt;</span></pre>
<p>还有一种更好的方式：</p>
<pre class="code"><span style="color: blue">&lt;</span><span style="color: #a31515">link </span><span style="color: red">type</span><span style="color: blue">=&quot;text/css&quot; </span><span style="color: red">href</span><span style="color: blue">=&quot;http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.1/themes/{YOUR Prefered Theme}/jquery-ui.css&quot; </span><span style="color: red">rel</span><span style="color: blue">=&quot;stylesheet&quot;/&gt;
&lt;</span><span style="color: #a31515">script </span><span style="color: red">type</span><span style="color: blue">=&quot;text/javascript&quot; </span><span style="color: red">src</span><span style="color: blue">=&quot;http://www.google.com/jsapi&quot;&gt;&lt;/</span><span style="color: #a31515">script</span><span style="color: blue">&gt;
&lt;</span><span style="color: #a31515">script </span><span style="color: red">type</span><span style="color: blue">=&quot;text/javascript&quot;&gt;
    </span>google.load(<span style="color: #a31515">&quot;jquery&quot;</span>, <span style="color: #a31515">&quot;1.3.2&quot;</span>);
    google.load(<span style="color: #a31515">&quot;jqueryui&quot;</span>, <span style="color: #a31515">&quot;1.7.1&quot;</span>);
<span style="color: blue">&lt;/</span><span style="color: #a31515">script</span><span style="color: blue">&gt;</span></pre>
<p>以上就是这一次的全部内容。</p>
<p>最后，我要祝贺 <a href="http://www.asp.net/mvc/" rel="tag" target="_blank">ASP.NET MVC</a> 团队开发了如此杰出的框架，尤其是他们从社区接受反馈的方式。我希望能用这个框架开发出<a href="http://dotnetshoutout.com/" rel="external" target="_blank">更多</a>杀手级的应用（注：杀手级的应用程序，一般指极其优秀的应用程序，这类程序超越了语言本身，比语言更有吸引力而且能增加语言的吸引力）。</p>
<h3  class="related_post_title">相关日志</h3><ul class="related_post"><li>2009-07-07 -- <a href="http://ofcss.com/2009/07/07/asp-net-mvc-best-practices-part-3.html" title="ASP.NET MVC 最佳实践（三）">ASP.NET MVC 最佳实践（三）</a></li><li>2009-07-01 -- <a href="http://ofcss.com/2009/07/01/asp-net-mvc-best-practices-part-2.html" title="ASP.NET MVC 最佳实践（二）">ASP.NET MVC 最佳实践（二）</a></li><li>2009-06-28 -- <a href="http://ofcss.com/2009/06/28/asp-net-mvc-best-practices-part-1.html" title="ASP.NET MVC 最佳实践（一）">ASP.NET MVC 最佳实践（一）</a></li><li>2009-04-13 -- <a href="http://ofcss.com/2009/04/13/aspnetmvc-hands-on-labs-develop-application.html" title="[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用">[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用</a></li><li>2009-04-09 -- <a href="http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-create-application.html" title="[翻译]ASP.NET MVC动手实验1-2：创建ASP.NET MVC应用">[翻译]ASP.NET MVC动手实验1-2：创建ASP.NET MVC应用</a></li><li>2009-04-09 -- <a href="http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-introduction.html" title="[翻译]ASP.NET MVC动手系列1-1：ASP.NET MVC概述">[翻译]ASP.NET MVC动手系列1-1：ASP.NET MVC概述</a></li><li>2011-03-14 -- <a href="http://ofcss.com/2011/03/14/browser-rendering-optimizer-translation-extra.html" title="对《优化浏览器渲染》的补充【译】">对《优化浏览器渲染》的补充【译】</a></li><li>2011-03-10 -- <a href="http://ofcss.com/2011/03/10/css-border-tricks-with-collapsed-boxes-translation.html" title="CSS边框实现&ldquo;无图化&rdquo;设计【译】">CSS边框实现&ldquo;无图化&rdquo;设计【译】</a></li><li>2010-10-31 -- <a href="http://ofcss.com/2010/10/31/prevent-a-float-drop-in-ie6-translation.html" title="CSS技巧:如何避免IE6中的&quot;浮动下坠&quot;【译】">CSS技巧:如何避免IE6中的&quot;浮动下坠&quot;【译】</a></li><li>2010-10-25 -- <a href="http://ofcss.com/2010/10/25/understanding-user-behavior-google-analytics-event-tracking-jquer.html" title="借助jQuery和Google分析服务的事件追踪深入了解用户行为【译】">借助jQuery和Google分析服务的事件追踪深入了解用户行为【译】</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://ofcss.com/2009/07/08/asp-net-mvc-best-practices-part-4.html/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>ASP.NET MVC 最佳实践（三）</title>
		<link>http://ofcss.com/2009/07/07/asp-net-mvc-best-practices-part-3.html</link>
		<comments>http://ofcss.com/2009/07/07/asp-net-mvc-best-practices-part-3.html#comments</comments>
		<pubDate>Mon, 06 Jul 2009 17:21:16 +0000</pubDate>
		<dc:creator>小李刀刀</dc:creator>
				<category><![CDATA[所谓技术]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[开发]]></category>
		<category><![CDATA[最佳实践]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://wukangrui.net/2009/07/07/asp-net-mvc-best-practices-part-1-2.html</guid>
		<description><![CDATA[<p>本系列翻译自 Kazi Manzur Rashid 的博客，由于翻译水平有限，本系列可能存在误解偏差或者翻译不准的地方，建议对比原文进行阅读。由于篇幅关系，原文中的一篇文章在本系列中将拆解成多篇发布。本篇包括原文第二部分。如果你没有看过之前的第一部分，也许你想先看看 《ASP.NET MVC 最佳实践（一）》和《ASP.NET MVC 最佳实践（二）》。 <a href="http://ofcss.com/2009/07/07/asp-net-mvc-best-practices-part-3.html">Continue reading <span class="meta-nav">&#8594;</span></a></p>
]]></description>
			<content:encoded><![CDATA[<p><em>本系列翻译自 </em><a href="http://weblogs.asp.net/rashid/default.aspx"><em>Kazi Manzur Rashid</em></a><em> 的博客，由于翻译水平有限，本系列可能存在误解偏差或者翻译不准的地方，建议对比<a href="http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx">原文</a>进行阅读。由于篇幅关系，原文中的一篇文章在本系列中将拆解成多篇发布。本篇包括原文第二部分。如果你没有看过之前的第一部分，也许你想先看看 <a rel="prev" href="http://wukangrui.net/2009/06/28/asp-net-mvc-best-practices-part-1.html">《ASP.NET MVC 最佳实践（一）》</a>和《<a rel="prev" href="http://wukangrui.net/2009/06/28/asp-net-mvc-best-practices-part-2.html">ASP.NET MVC 最佳实践（二）</a>》。</em> <span id="more-848"></span><em></em></p>
<h4>15. 关于路由的思考</h4>
<p>如果你在开发的是一个纯 ASP.NET MVC 的应用程序，可以先关闭已有的路由文件检查，这样可以去除不必要的文件系统检查。如果你这样做了，那么就还需要考虑另外的一些事情。请记住如果你的应用程序运行于 IIS 7集成模式，那么 ASP.NET 应用程序将会拦截所有的请求，不管文件的后缀名是什么。所以你必须要在过滤列表中加一些东西，以便让 <a rel="tag" href="http://www.asp.net/mvc/" target="_blank">ASP.NET MVC</a> 应用程序不处理它们。这其中可能包括静态文件，例如 html、htm、文本文件(尤其是robots.txt)、favicon.ico、脚本、图片和css等等。这也是我在<a rel="prev" href="http://wukangrui.net/2009/06/28/asp-net-mvc-best-practices-part-1.html">第一部分第2节</a>中提到不喜欢默认文件结构的原因之一。以下是我为 IIS7 定制的路由定义标准模板：</p>
<pre class="code">_routes.Clear();

<span style="color: green">// Turns off the unnecessary file exists check
</span>_routes.RouteExistingFiles = <span style="color: blue">true</span>;

<span style="color: green">// Ignore text, html, files.
</span>_routes.IgnoreRoute(<span style="color: #a31515">"{file}.txt"</span>);
_routes.IgnoreRoute(<span style="color: #a31515">"{file}.htm"</span>);
_routes.IgnoreRoute(<span style="color: #a31515">"{file}.html"</span>);

<span style="color: green">// Ignore axd files such as assest, image, sitemap etc
</span>_routes.IgnoreRoute(<span style="color: #a31515">"{resource}.axd/{*pathInfo}"</span>);

<span style="color: green">// Ignore the assets directory which contains images, js, css &amp; html
</span>_routes.IgnoreRoute(<span style="color: #a31515">"assets/{*pathInfo}"</span>);

<span style="color: green">// Ignore the error directory which contains error pages
</span>_routes.IgnoreRoute(<span style="color: #a31515">"ErrorPages/{*pathInfo}"</span>);

<span style="color: green">//Exclude favicon (google toolbar request gif file as fav icon which is weird)
</span>_routes.IgnoreRoute(<span style="color: #a31515">"{*favicon}"</span>, <span style="color: blue">new </span>{ favicon = <span style="color: #a31515">@"(.*/)?favicon.([iI][cC][oO]|[gG][iI][fF])(/.*)?" </span>});

<span style="color: green">//Actual routes of my application</span></pre>
<p>接下来是我的一些不同于手册指南的个人偏好。默认情况下 <a rel="tag" href="http://www.asp.net/mvc/" target="_blank">ASP.NET MVC</a> 产生的 url 形如 {controller}/{action}，在你开发一个多模块的应用程序是这是不错的选择，但是对于一些很小的应用程序，我通常喜欢从 url 中去掉控制器(controller)只留下方法名(action name)。如此一来，原来的“www.yourdomain.com/Story/Dashboard” 及 “www.yourdomain.com/Membership/Signin” 就会变成 “www.yourdomain.com/Dashboard” 和“www.yourmain.com/Signin”。所以我要添加一些新路由：</p>
<pre class="code">_routes.MapRoute(<span style="color: #a31515">"SignUp"</span>, <span style="color: #a31515">"SignUp"</span>, <span style="color: blue">new </span>{ controller = <span style="color: #a31515">"Membership"</span>, action = <span style="color: #a31515">"SignUp" </span>});
_routes.MapRoute(<span style="color: #a31515">"SignIn"</span>, <span style="color: #a31515">"SignIn"</span>, <span style="color: blue">new </span>{ controller = <span style="color: #a31515">"Membership"</span>, action = <span style="color: #a31515">"SignIn" </span>});
_routes.MapRoute(<span style="color: #a31515">"ForgotPassword"</span>, <span style="color: #a31515">"ForgotPassword"</span>, <span style="color: blue">new </span>{ controller = <span style="color: #a31515">"Membership"</span>, action = <span style="color: #a31515">"ForgotPassword" </span>});
_routes.MapRoute(<span style="color: #a31515">"SignOut"</span>, <span style="color: #a31515">"SignOut"</span>, <span style="color: blue">new </span>{ controller = <span style="color: #a31515">"Membership"</span>, action = <span style="color: #a31515">"SignOut" </span>});
_routes.MapRoute(<span style="color: #a31515">"Profile"</span>, <span style="color: #a31515">"Profile"</span>, <span style="color: blue">new </span>{ controller = <span style="color: #a31515">"Membership"</span>, action = <span style="color: #a31515">"Profile" </span>});
_routes.MapRoute(<span style="color: #a31515">"ChangePassword"</span>, <span style="color: #a31515">"ChangePassword"</span>, <span style="color: blue">new </span>{ controller = <span style="color: #a31515">"Membership"</span>, action = <span style="color: #a31515">"ChangePassword" </span>});

_routes.MapRoute(<span style="color: #a31515">"Dashboard"</span>, <span style="color: #a31515">"Dashboard/{tab}/{orderBy}/{page}"</span>, <span style="color: blue">new </span>{ controller = <span style="color: #a31515">"Story"</span>, action = <span style="color: #a31515">"Dashboard"</span>, tab = StoryListTab.Unread.ToString(), orderBy = OrderBy.CreatedAtDescending.ToString(), page = 1 });
_routes.MapRoute(<span style="color: #a31515">"Update"</span>, <span style="color: #a31515">"Update"</span>, <span style="color: blue">new </span>{ controller = <span style="color: #a31515">"Story"</span>, action = <span style="color: #a31515">"Update" </span>});
_routes.MapRoute(<span style="color: #a31515">"Submit"</span>, <span style="color: #a31515">"Submit"</span>, <span style="color: blue">new </span>{ controller = <span style="color: #a31515">"Story"</span>, action = <span style="color: #a31515">"Submit" </span>});

_routes.MapRoute(<span style="color: #a31515">"Home"</span>, <span style="color: #a31515">"{controller}/{action}/{id}"</span>, <span style="color: blue">new </span>{ controller = <span style="color: #a31515">"Home"</span>, action = <span style="color: #a31515">"Index"</span>, id = <span style="color: blue">string</span>.Empty });</pre>
<h4> </h4>
<h4>16. 如果需要的话，创建新的ActionResult</h4>
<p><a rel="tag" href="http://www.asp.net/mvc/" target="_blank">ASP.NET MVC</a> 包含有很多种用于不同目的的 ActionResult，但我们仍有可能会需要新的 ActionResult。例如xml、rss、atom等等。在这种情况下，我的建议是不要使用通用的 ContentResult， 而是创建一种新的 ActionResult. <a rel="external" href="http://mvccontrib.codeplex.com" target="_blank">MVCContrib</a> 项目中有一个 XmlResult，你可以用它来返回 xml，但是不支持 feed。显然依靠它来将未知对象转换为 rss/atom 是很困难的，这时你就可以创建一个定义ActionResult的模型。例如：</p>
<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">AtomResult </span>: <span style="color: #2b91af">ActionResult </span>{
    <span style="color: blue">public </span>AtomResult(<span style="color: blue">string </span>siteTitle, <span style="color: blue">string </span>feedTitle, <span style="color: #2b91af">IEnumerable</span>&lt;IStory&gt; stories) {
        SiteTitle = siteTitle;
        FeedTitle = feedTitle;
        Stories = stories;
    }

    <span style="color: blue">public string </span>SiteTitle {
        <span style="color: blue">get</span>;
        <span style="color: blue">private set</span>;
    }

    <span style="color: blue">public string </span>FeedTitle {
        <span style="color: blue">get</span>;
        <span style="color: blue">private set</span>;
    }

    <span style="color: blue">public </span><span style="color: #2b91af">IEnumerable</span>&lt;IStory&gt; Stories {
        <span style="color: blue">get</span>;
        <span style="color: blue">private set</span>;
    }

    <span style="color: blue">public override void </span>ExecuteResult(<span style="color: #2b91af">ControllerContext </span>context) {
        <span style="color: blue">string </span>xml = Build(context);

        <span style="color: #2b91af">HttpResponseBase </span>response = context.HttpContext.Response;
        response.ContentType = <span style="color: #a31515">"application/atom+xml"</span>;
        response.Write(xml);
    }
}</pre>
<p>然后在控制器中就可以这样写：<a href="http://11011.net/software/vspaste"></a></p>
<pre class="code">[<span style="color: #2b91af">AcceptVerbs</span>(<span style="color: #2b91af">HttpVerbs</span>.Get), <span style="color: #2b91af">OutputCache</span>(CacheProfile = <span style="color: #a31515">"Atom"</span>)]
<span style="color: blue">public </span><span style="color: #2b91af">ActionResult </span>Shared() {
    <span style="color: #2b91af">IEnumerable</span>&lt;stories&gt; stories = GetSharedStories();

    <span style="color: blue">return new </span><span style="color: #2b91af">AtomResult</span>(<span style="color: #a31515">"My Site"</span>, <span style="color: #a31515">"My shared stories in atom"</span>, stories);
}</pre>
<h4>17. 把你的视图拆分为多个视图控件</h4>
<p>如果你的视图文件变得越来越大，那你可以考虑把它拆分为多个视图控件。至于视图空间是否在多个页面中重用，并没有关系。因为这样做可以让真正的视图文件变得更加具有可读性。考虑一下下面的视图：</p>
<pre class="code"><span style="color: blue">&lt;</span><span style="color: #a31515">asp</span><span style="color: blue">:</span><span style="color: #a31515">Content </span><span style="color: red">ID</span><span style="color: blue">="Content1" </span><span style="color: red">ContentPlaceHolderID</span><span style="color: blue">="TitleContent" </span><span style="color: red">runat</span><span style="color: blue">="server"&gt;
    </span>My Secret App : Dashboard
<span style="color: blue">&lt;/</span><span style="color: #a31515">asp</span><span style="color: blue">:</span><span style="color: #a31515">Content</span><span style="color: blue">&gt;
&lt;</span><span style="color: #a31515">asp</span><span style="color: blue">:</span><span style="color: #a31515">Content </span><span style="color: red">ID</span><span style="color: blue">="Content2" </span><span style="color: red">ContentPlaceHolderID</span><span style="color: blue">="MainContent" </span><span style="color: red">runat</span><span style="color: blue">="server"&gt;
    &lt;</span><span style="color: #a31515">div </span><span style="color: red">id</span><span style="color: blue">="heading"&gt;&lt;/</span><span style="color: #a31515">div</span><span style="color: blue">&gt;
    &lt;</span><span style="color: #a31515">div </span><span style="color: red">class</span><span style="color: blue">="columns"&gt;
        &lt;</span><span style="color: #a31515">div </span><span style="color: red">id</span><span style="color: blue">="main" </span><span style="color: red">class</span><span style="color: blue">="column"&gt;
            &lt;</span><span style="color: #a31515">div </span><span style="color: red">id</span><span style="color: blue">="storyListTabs" </span><span style="color: red">class</span><span style="color: blue">="ui-tabs ui-widget ui-widget-content ui-corner-all"&gt;
                </span><span style="background: #ffee62">&lt;%</span> Html.RenderPartial(<span style="color: #a31515">"TabHeader"</span>, Model);<span style="background: #ffee62">%&gt;
</span>                <span style="color: blue">&lt;</span><span style="color: #a31515">div </span><span style="color: red">id</span><span style="color: blue">="tabContent" </span><span style="color: red">class</span><span style="color: blue">="ui-tabs-panel ui-widget-content ui-corner-bottom"&gt;
                    &lt;</span><span style="color: #a31515">div </span><span style="color: red">id</span><span style="color: blue">="storyList"&gt;
                        </span><span style="background: #ffee62">&lt;%</span> Html.RenderPartial(<span style="color: #a31515">"SortBar"</span>, Model);<span style="background: #ffee62">%&gt;
</span>                        <span style="color: blue">&lt;</span><span style="color: #a31515">div </span><span style="color: red">class</span><span style="color: blue">="clear"&gt;&lt;/</span><span style="color: #a31515">div</span><span style="color: blue">&gt;
                        </span><span style="background: #ffee62">&lt;%</span> Html.RenderPartial(<span style="color: #a31515">"NoLinkMessage"</span>, Model);<span style="background: #ffee62">%&gt;
</span>                        <span style="color: blue">&lt;</span><span style="color: #a31515">form </span><span style="color: red">id</span><span style="color: blue">="update" </span><span style="color: red">action</span><span style="color: blue">="</span><span style="background: #ffee62">&lt;%</span>= Url.Update()<span style="background: #ffee62">%&gt;</span><span style="color: blue">" </span><span style="color: red">method</span><span style="color: blue">="post"&gt;
                            </span><span style="background: #ffee62">&lt;%</span> Html.RenderPartial(<span style="color: #a31515">"List"</span>, Model);<span style="background: #ffee62">%&gt;
</span>                            <span style="background: #ffee62">&lt;%</span> Html.RenderPartial(<span style="color: #a31515">"ActionBar"</span>, Model);<span style="background: #ffee62">%&gt;
</span>                            <span style="background: #ffee62">&lt;%</span> Html.RenderPartial(<span style="color: #a31515">"Pager"</span>, Model);<span style="background: #ffee62">%&gt;
</span>                        <span style="color: blue">&lt;/</span><span style="color: #a31515">form</span><span style="color: blue">&gt;
                    &lt;/</span><span style="color: #a31515">div</span><span style="color: blue">&gt;
                &lt;/</span><span style="color: #a31515">div</span><span style="color: blue">&gt;
            &lt;/</span><span style="color: #a31515">div</span><span style="color: blue">&gt;
            </span><span style="background: #ffee62">&lt;%</span>Html.RenderPartial(<span style="color: #a31515">"Submit"</span>, <span style="color: blue">new </span>StorySubmitViewModel());<span style="background: #ffee62">%&gt;
</span>        <span style="color: blue">&lt;/</span><span style="color: #a31515">div</span><span style="color: blue">&gt;
        &lt;</span><span style="color: #a31515">div </span><span style="color: red">id</span><span style="color: blue">="sideBar" </span><span style="color: red">class</span><span style="color: blue">="column"&gt;&lt;/</span><span style="color: #a31515">div</span><span style="color: blue">&gt;
    &lt;/</span><span style="color: #a31515">div</span><span style="color: blue">&gt;
&lt;/</span><span style="color: #a31515">asp</span><span style="color: blue">:</span><span style="color: #a31515">Content</span><span style="color: blue">&gt;</span></pre>
<p>继续阅读：《<a rel="next" href="http://wukangrui.net/2009/07/08/asp-net-mvc-best-practices-part-4.html">ASP.NET MVC 最佳实践（四）</a>》</p>
<h3  class="related_post_title">相关日志</h3><ul class="related_post"><li>2009-07-08 -- <a href="http://ofcss.com/2009/07/08/asp-net-mvc-best-practices-part-4.html" title="ASP.NET MVC 最佳实践（四）">ASP.NET MVC 最佳实践（四）</a></li><li>2009-07-01 -- <a href="http://ofcss.com/2009/07/01/asp-net-mvc-best-practices-part-2.html" title="ASP.NET MVC 最佳实践（二）">ASP.NET MVC 最佳实践（二）</a></li><li>2009-06-28 -- <a href="http://ofcss.com/2009/06/28/asp-net-mvc-best-practices-part-1.html" title="ASP.NET MVC 最佳实践（一）">ASP.NET MVC 最佳实践（一）</a></li><li>2009-04-13 -- <a href="http://ofcss.com/2009/04/13/aspnetmvc-hands-on-labs-develop-application.html" title="[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用">[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用</a></li><li>2009-04-09 -- <a href="http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-create-application.html" title="[翻译]ASP.NET MVC动手实验1-2：创建ASP.NET MVC应用">[翻译]ASP.NET MVC动手实验1-2：创建ASP.NET MVC应用</a></li><li>2009-04-09 -- <a href="http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-introduction.html" title="[翻译]ASP.NET MVC动手系列1-1：ASP.NET MVC概述">[翻译]ASP.NET MVC动手系列1-1：ASP.NET MVC概述</a></li><li>2011-03-14 -- <a href="http://ofcss.com/2011/03/14/browser-rendering-optimizer-translation-extra.html" title="对《优化浏览器渲染》的补充【译】">对《优化浏览器渲染》的补充【译】</a></li><li>2011-03-10 -- <a href="http://ofcss.com/2011/03/10/css-border-tricks-with-collapsed-boxes-translation.html" title="CSS边框实现&ldquo;无图化&rdquo;设计【译】">CSS边框实现&ldquo;无图化&rdquo;设计【译】</a></li><li>2010-10-31 -- <a href="http://ofcss.com/2010/10/31/prevent-a-float-drop-in-ie6-translation.html" title="CSS技巧:如何避免IE6中的&quot;浮动下坠&quot;【译】">CSS技巧:如何避免IE6中的&quot;浮动下坠&quot;【译】</a></li><li>2010-10-25 -- <a href="http://ofcss.com/2010/10/25/understanding-user-behavior-google-analytics-event-tracking-jquer.html" title="借助jQuery和Google分析服务的事件追踪深入了解用户行为【译】">借助jQuery和Google分析服务的事件追踪深入了解用户行为【译】</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://ofcss.com/2009/07/07/asp-net-mvc-best-practices-part-3.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>ASP.NET MVC 最佳实践（二）</title>
		<link>http://ofcss.com/2009/07/01/asp-net-mvc-best-practices-part-2.html</link>
		<comments>http://ofcss.com/2009/07/01/asp-net-mvc-best-practices-part-2.html#comments</comments>
		<pubDate>Tue, 30 Jun 2009 21:03:41 +0000</pubDate>
		<dc:creator>小李刀刀</dc:creator>
				<category><![CDATA[所谓技术]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[开发]]></category>
		<category><![CDATA[最佳实践]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://wukangrui.net/2009/07/01/asp-net-mvc-best-practices-part-2.html</guid>
		<description><![CDATA[<p>本系列翻译自 Kazi Manzur Rashid 的博客，由于翻译水平有限，本系列可能存在误解偏差或者翻译不准的地方，建议对比原文进行阅读。由于篇幅关系，原文中的一篇文章在本系列中将拆解成多篇发布。本篇包括原文第一部分中的7-14节。如果你没有看过之前的第一部分，也许你想先看看 《ASP.NET MVC 最佳实践（一）》。 <a href="http://ofcss.com/2009/07/01/asp-net-mvc-best-practices-part-2.html">Continue reading <span class="meta-nav">&#8594;</span></a></p>
]]></description>
			<content:encoded><![CDATA[<p><em>本系列翻译自 </em><a href="http://weblogs.asp.net/rashid/default.aspx"><em>Kazi Manzur Rashid</em></a><em> 的博客，由于翻译水平有限，本系列可能存在误解偏差或者翻译不准的地方，建议对比<a href="http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx">原文</a>进行阅读。由于篇幅关系，原文中的一篇文章在本系列中将拆解成多篇发布。本篇包括原文第一部分中的7-14节。如果你没有看过之前的第一部分，也许你想先看看 <a rel="prev" href="http://wukangrui.net/2009/06/28/asp-net-mvc-best-practices-part-1.html">《ASP.NET MVC 最佳实践（一）》</a>。</em></p>
<p><span id="more-829"></span></p>
<h4>7. 不要在控制器中使用HttpContext类及其派生类</h4>
<p>在你的控制器中不要引用到HttpContext以及它的派生类。这让你能比较容易地进行控制器的单元测试。如果你需要访问与HttpContext相关的对象比如User、QueryString、Cookie等，你可以使用自定义的行为过滤器（<a rel="external" href="http://www.cnblogs.com/" target="_blank">博客园</a>的<a rel="external" href="http://www.cnblogs.com/JeffreyZhao/" target="_blank">老赵</a>有<a rel="external" href="http://www.cnblogs.com/JeffreyZhao/archive/2009/03/09/no-dependency-to-httpcontext.html" target="_blank">一篇文章</a>对这一条进行了深入的论述，并提出了自己的解决方案，建议阅读）或者创建一些接口和容器，并把它传入控制器的构造函数。例如，下面的路由：</p>
<pre class="code">routes.MapRoute(
    <span style="color: #a31515">"Dashboard"</span>,
    <span style="color: #a31515">"Dashboard/{tab}/{orderBy}/{page}"</span>,
    <span style="color: blue">new </span>{
        controller = <span style="color: #a31515">"Story"</span>,
        action = <span style="color: #a31515">"Dashboard"</span>,
        tab = StoryListTab.Unread.ToString(),
        orderBy = OrderBy.CreatedAtDescending.ToString(),
        page = 1
    }
);</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>而控制器的行为方法则定义为：</p>
<pre class="code">[<span style="color: #2b91af">AcceptVerbs</span>(<span style="color: #2b91af">HttpVerbs</span>.Get),<span style="color: #2b91af">OutputCache</span>(CacheProfile = <span style="color: #a31515">"Dashboard"</span>),UserNameFilter]
<span style="color: blue">public </span><span style="color: #2b91af">ActionResult </span>Dashboard(<span style="color: blue">string </span>userName, StoryListTab tab, OrderBy orderBy, <span style="color: blue">int</span>? page) {
}</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>UserNameFilter这个过滤器负责传递UserName：</p>
<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">UserNameFilter </span>: <span style="color: #2b91af">ActionFilterAttribute </span>{
    <span style="color: blue">public override void </span>OnActionExecuting(<span style="color: #2b91af">ActionExecutingContext </span>filterContext) {
        <span style="color: blue">const string </span>Key = <span style="color: #a31515">"userName"</span>;

        <span style="color: blue">if </span>(filterContext.ActionParameters.ContainsKey(Key)) {
            <span style="color: blue">if </span>(filterContext.HttpContext.User.Identity.IsAuthenticated) {
                filterContext.ActionParameters[Key] = filterContext.HttpContext.User.Identity.Name;
            }
        }

        <span style="color: blue">base</span>.OnActionExecuting(filterContext);
    }
}</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p><strong>[更新：一定要明确你已经对控制器或者是对控制器中的行为添加了 Authorize 属性，参见<a rel="external" href="http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx#comments" target="_blank">原文评论</a>。]</strong></p>
<h4>8. 用行为控制器来转换行为方法参数</h4>
<p>用行为过滤器来把传入值转换为你的控制器行为方法参数，再看一下 Dashboard方法的代码，我们以 Enum 类型接受 tab 和 orderBy这两个参数。</p>
<pre class="code">[<span style="color: #2b91af">AcceptVerbs</span>(<span style="color: #2b91af">HttpVerbs</span>.Get), <span style="color: #2b91af">OutputCache</span>(CacheProfile = <span style="color: #a31515">"Dashboard"</span>), StoryListFilter]
<span style="color: blue">public </span><span style="color: #2b91af">ActionResult </span>Dashboard(<span style="color: blue">string </span>userName, StoryListTab tab, OrderBy orderBy, <span style="color: blue">int</span>? page) {
}</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p> </p>
<p>过滤器 StoryListFilter 将负责把它由 路由的值/查询字符串 转换为适当的数据类型。</p>
<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">StoryListFilter </span>: <span style="color: #2b91af">ActionFilterAttribute </span>{
    <span style="color: blue">public override void </span>OnActionExecuting(<span style="color: #2b91af">ActionExecutingContext </span>filterContext) {
        <span style="color: blue">const string </span>TabKey = <span style="color: #a31515">"tab"</span>;
        <span style="color: blue">const string </span>OrderByKey = <span style="color: #a31515">"orderBy"</span>;

        NameValueCollection queryString = filterContext.HttpContext.Request.QueryString;

        StoryListTab tab = <span style="color: blue">string</span>.IsNullOrEmpty(queryString[TabKey]) ?
                            filterContext.RouteData.Values[TabKey].ToString().ToEnum(StoryListTab.Unread) :
                            queryString[TabKey].ToEnum(StoryListTab.Unread);

        filterContext.ActionParameters[TabKey] = tab;

        OrderBy orderBy = <span style="color: blue">string</span>.IsNullOrEmpty(queryString[OrderByKey]) ?
                            filterContext.RouteData.Values[OrderByKey].ToString().ToEnum(OrderBy.CreatedAtDescending) :
                            queryString[OrderByKey].ToEnum(OrderBy.CreatedAtDescending);

        filterContext.ActionParameters[OrderByKey] = orderBy;

        <span style="color: blue">base</span>.OnActionExecuting(filterContext);
    }
}</pre>
<p>你也可以用自定义模型绑定器来达到同样的目的。如果要那样做的话，你需要为每个枚举创建一个独立的模型绑定器，而不是用一个行为过滤器来处理所有的枚举参数。用模型绑定器还有一个问题，一旦你注册了一个类型，在行为中它就总是被使用，而行为过滤器则是可以根据需要选择使用的。</p>
<h4>9. 行为过滤器的位置</h4>
<p>如果你要对你的控制器的所有行为方法应用同一个行为过滤器，你可以把这个过滤器放在控制器的定义上而不必给每个行为方法应用。如果你要对你的所有控制器应用同一个行为过滤器，你应该创建一个基控制器，对它应用该过滤器，并让所有的控制器继承这个基控制器。例如 story 控制器应该只在用户已经登陆的情况下才可以使用，并且我们需要把当前用户的用户名传入 story 控制器下的方法，另外Story控制器应该压缩返回的数据：</p>
<pre class="code">[<span style="color: #2b91af">Authorize</span>, <span style="color: #2b91af">UserNameFilter</span>]
<span style="color: blue">public class </span><span style="color: #2b91af">StoryController </span>: <span style="color: #2b91af">BaseController </span>{
}

[CompressFilter]
<span style="color: blue">public class </span><span style="color: #2b91af">BaseController </span>: <span style="color: #2b91af">Controller </span>{
}</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>但是如果继承的层次达到或者高于2层，应该另找方法来应用过滤器。最新的 <a rel="external" href="http://oxite.codeplex.com/" target="_blank">Oxite</a> 代码里有一些非常出色的动态应用过滤器的方法，我强烈推荐你去看一下。</p>
<h4>10. 小心使用UpdateModel</h4>
<p>我要再次强调 Justin Ethredge 在他的文章中已经提过的这个问题，开发中一定小心，避免陷进UpdateModel的陷阱里去。</p>
<h4>11.控制器不要包含任何域逻辑</h4>
<p>控制器应该只负责：</p>
<ul>
<li>验证输入</li>
<li>调用Model层来为显示视图准备数据</li>
<li>返回视图或者跳转到另一个行为</li>
</ul>
<p>如果你在控制器中坐了其它的事情，那就说明你把它们放错了地方。你在控制器中坐的这些事情或许更应该交给模型去处理。只要你遵守了这条规则，你的每个控制器方法代码应该不会超过20到25行。 <a rel="external" href="http://codebetter.com/blogs/ian_cooper/default.aspx" target="_blank">Ian Cooper</a> 有一篇很棒的文章《<a rel="external" href="http://codebetter.com/blogs/ian_cooper/archive/2008/12/03/the-fat-controller.aspx" target="_blank">Skin Controller Fat Model</a>》，有空的时候你一定要读一下。</p>
<h4>12. 避免使用 ViewData，尽量使用ViewData.Model</h4>
<p>依赖于数据字典不仅使你的代码难以重构，而且你还不得不在试图中编写转换代码。实际上即使你给你的控制器的每个方法都单独编写一个类作为数据模型，那也是完全可以的。如果你觉得编写这些视图数据模型类是一项非常乏味的工作的话，你可以使用 MVCContrib 项目中 ViewDataExtensions，它包含一些用于返回强类型对象的不错的扩展。但是如果你的视图数据的数据字典中包含了多个数据类型的话，你还是没办法摆脱数据字典和他的字符串名。</p>
<h4>13. 用 PRG 模式来修改数据</h4>
<p>Tim Barcz, Matt Hawley, Stephen Wather 甚至 Scott Gu 都写了这方面的文章，你可以在 <a rel="external" href="http://devlicio.us/blogs/tim_barcz/archive/2008/08/22/prg-pattern-in-the-asp-net-mvc-framework.aspx" target="_blank">这里</a>、<a rel="external" href="http://blog.eworldui.net/post/2008/05/ASPNET-MVC---Using-Post2c-Redirect2c-Get-Pattern.aspx" target="_blank">这里</a>、<a rel="external" href="http://stephenwalther.com/blog/archive/2008/06/20/asp-net-mvc-tip-6-call-redirecttoaction-after-submitting-a-form.aspx" target="_blank">这里</a>和<a rel="external" href="http://weblogs.asp.net/scottgu/archive/2008/09/02/asp-net-mvc-preview-5-and-form-posting-scenarios.aspx" target="_blank">这里</a>找到它们。这个模式的一个问题是当一项验证失败或者发生任何错误的时候，你不得不把ModelState复制到TempData里面。如果你是手动来做这件事，请不要再那样做了，你可以用行为过滤器自动处理它，就像下面这样：</p>
<pre class="code">[<span style="color: #2b91af">AcceptVerbs</span>(<span style="color: #2b91af">HttpVerbs</span>.Get), <span style="color: #2b91af">OutputCache</span>(CacheProfile = <span style="color: #a31515">"Dashboard"</span>), StoryListFilter, ImportModelStateFromTempData]
<span style="color: blue">public </span><span style="color: #2b91af">ActionResult </span>Dashboard(<span style="color: blue">string </span>userName, StoryListTab tab, OrderBy orderBy, <span style="color: blue">int</span>? page)
{
    <span style="color: green">//Other Codes
    </span><span style="color: blue">return </span>View();
}

[<span style="color: #2b91af">AcceptVerbs</span>(<span style="color: #2b91af">HttpVerbs</span>.Post), ExportModelStateToTempData]
<span style="color: blue">public </span><span style="color: #2b91af">ActionResult </span>Submit(<span style="color: blue">string </span>userName, <span style="color: blue">string </span>url)
{
    <span style="color: blue">if </span>(ValidateSubmit(url))
    {
        <span style="color: blue">try
        </span>{
            _storyService.Submit(userName, url);
        }
        <span style="color: blue">catch </span>(Exception e)
        {
            ModelState.AddModelError(ModelStateException, e);
        }
    }

    <span style="color: blue">return </span>Redirect(Url.Dashboard());
}</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>还有行为过滤器的代码：</p>
<pre class="code"><span style="color: blue">public abstract class </span><span style="color: #2b91af">ModelStateTempDataTransfer </span>: <span style="color: #2b91af">ActionFilterAttribute </span>{
    <span style="color: blue">protected static readonly string </span>Key = <span style="color: blue">typeof</span>(<span style="color: #2b91af">ModelStateTempDataTransfer</span>).FullName;
}

<span style="color: blue">public class </span><span style="color: #2b91af">ExportModelStateToTempData </span>: <span style="color: #2b91af">ModelStateTempDataTransfer </span>{
    <span style="color: blue">public override void </span>OnActionExecuted(<span style="color: #2b91af">ActionExecutedContext </span>filterContext) {
        <span style="color: green">//Only export when ModelState is not valid
        </span><span style="color: blue">if </span>(!filterContext.Controller.ViewData.ModelState.IsValid) {
            <span style="color: green">//Export if we are redirecting
            </span><span style="color: blue">if </span>((filterContext.Result <span style="color: blue">is </span><span style="color: #2b91af">RedirectResult</span>) || (filterContext.Result <span style="color: blue">is </span><span style="color: #2b91af">RedirectToRouteResult</span>)) {
                filterContext.Controller.TempData[Key] = filterContext.Controller.ViewData.ModelState;
            }
        }

        <span style="color: blue">base</span>.OnActionExecuted(filterContext);
    }
}

<span style="color: blue">public class </span><span style="color: #2b91af">ImportModelStateFromTempData </span>: <span style="color: #2b91af">ModelStateTempDataTransfer </span>{
    <span style="color: blue">public override void </span>OnActionExecuted(<span style="color: #2b91af">ActionExecutedContext </span>filterContext) {
        <span style="color: #2b91af">ModelStateDictionary </span>modelState = filterContext.Controller.TempData[Key] <span style="color: blue">as </span><span style="color: #2b91af">ModelStateDictionary</span>;

        <span style="color: blue">if </span>(modelState != <span style="color: blue">null</span>) {
            <span style="color: green">//Only Import if we are viewing
            </span><span style="color: blue">if </span>(filterContext.Result <span style="color: blue">is </span><span style="color: #2b91af">ViewResult</span>) {
                filterContext.Controller.ViewData.ModelState.Merge(modelState);
            } <span style="color: blue">else </span>{
                <span style="color: green">//Otherwise remove it.
                </span>filterContext.Controller.TempData.Remove(Key);
            }
        }

        <span style="color: blue">base</span>.OnActionExecuted(filterContext);
    }
}</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p><a rel="external" href="http://mvccontrib.codeplex.com" target="_blank">MVCContrib</a> 项目也有这个功能，但是我不喜欢他们在用一个单独类来处理的方式，我喜欢对“哪个方法输出”和“哪个方法输入”有更多的控制权。</p>
<h4>14. 为你的视图模型创建父类层并用行为过滤器来构成公共部分</h4>
<p>为你的视图模型类编写一个父类层，并用过滤器来构成它的公共部分。例如我正在开发的这个非常小的应用程序，我需要知道用户是否已经认证，已及用户名。</p>
<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">ViewModel </span>{
    <span style="color: blue">public bool </span>IsUserAuthenticated {
        <span style="color: blue">get</span>;
        <span style="color: blue">set</span>;
    }

    <span style="color: blue">public string </span>UserName {
        <span style="color: blue">get</span>;
        <span style="color: blue">set</span>;
    }
}</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>还有行为过滤器的代码：</p>
<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">UserNameFilter </span>: <span style="color: #2b91af">ActionFilterAttribute </span>{
    <span style="color: blue">public override void </span>OnActionExecuting(<span style="color: #2b91af">ActionExecutingContext </span>filterContext) {
        <span style="color: blue">const string </span>Key = <span style="color: #a31515">"userName"</span>;

        <span style="color: blue">if </span>(filterContext.ActionParameters.ContainsKey(Key)) {
            <span style="color: blue">if </span>(filterContext.HttpContext.User.Identity.IsAuthenticated) {
                filterContext.ActionParameters[Key] = filterContext.HttpContext.User.Identity.Name;
            }
        }

        <span style="color: blue">base</span>.OnActionExecuting(filterContext);
    }
}

<span style="color: blue">public class </span><span style="color: #2b91af">ViewModelUserFilter </span>: <span style="color: #2b91af">ActionFilterAttribute </span>{
    <span style="color: blue">public override void </span>OnActionExecuted(<span style="color: #2b91af">ActionExecutedContext </span>filterContext) {
        ViewModel model;

        <span style="color: blue">if </span>(filterContext.Controller.ViewData.Model == <span style="color: blue">null</span>) {
            model = <span style="color: blue">new </span>ViewModel();
            filterContext.Controller.ViewData.Model = model;
        } <span style="color: blue">else </span>{
            model = filterContext.Controller.ViewData.Model <span style="color: blue">as </span>ViewModel;
        }

        <span style="color: blue">if </span>(model != <span style="color: blue">null</span>) {
            model.IsUserAuthenticated = filterContext.HttpContext.User.Identity.IsAuthenticated;

            <span style="color: blue">if </span>(model.IsUserAuthenticated) {
                model.UserName = filterContext.HttpContext.User.Identity.Name;
            }
        }

        <span style="color: blue">base</span>.OnActionExecuted(filterContext);
    }
}</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>正如你所看到的，如果在控制器里预先设定它的话，它并没有替换模型，而是在它发现模型与它符合的时候，才作为控制器的公共部分起作用。另一个好处是，由于视图只依赖于父类层，你可以不必返回具体的model，而只需要返回 View()。</p>
<p>继续阅读：《<a rel="prev" href="http://wukangrui.net/2009/07/07/asp-net-mvc-best-practices-part-1-2.html">ASP.NET MVC 最佳实践（三）</a>》</p>
<h3  class="related_post_title">相关日志</h3><ul class="related_post"><li>2009-07-08 -- <a href="http://ofcss.com/2009/07/08/asp-net-mvc-best-practices-part-4.html" title="ASP.NET MVC 最佳实践（四）">ASP.NET MVC 最佳实践（四）</a></li><li>2009-07-07 -- <a href="http://ofcss.com/2009/07/07/asp-net-mvc-best-practices-part-3.html" title="ASP.NET MVC 最佳实践（三）">ASP.NET MVC 最佳实践（三）</a></li><li>2009-06-28 -- <a href="http://ofcss.com/2009/06/28/asp-net-mvc-best-practices-part-1.html" title="ASP.NET MVC 最佳实践（一）">ASP.NET MVC 最佳实践（一）</a></li><li>2009-04-13 -- <a href="http://ofcss.com/2009/04/13/aspnetmvc-hands-on-labs-develop-application.html" title="[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用">[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用</a></li><li>2009-04-09 -- <a href="http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-create-application.html" title="[翻译]ASP.NET MVC动手实验1-2：创建ASP.NET MVC应用">[翻译]ASP.NET MVC动手实验1-2：创建ASP.NET MVC应用</a></li><li>2009-04-09 -- <a href="http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-introduction.html" title="[翻译]ASP.NET MVC动手系列1-1：ASP.NET MVC概述">[翻译]ASP.NET MVC动手系列1-1：ASP.NET MVC概述</a></li><li>2011-03-14 -- <a href="http://ofcss.com/2011/03/14/browser-rendering-optimizer-translation-extra.html" title="对《优化浏览器渲染》的补充【译】">对《优化浏览器渲染》的补充【译】</a></li><li>2011-03-10 -- <a href="http://ofcss.com/2011/03/10/css-border-tricks-with-collapsed-boxes-translation.html" title="CSS边框实现&ldquo;无图化&rdquo;设计【译】">CSS边框实现&ldquo;无图化&rdquo;设计【译】</a></li><li>2010-10-31 -- <a href="http://ofcss.com/2010/10/31/prevent-a-float-drop-in-ie6-translation.html" title="CSS技巧:如何避免IE6中的&quot;浮动下坠&quot;【译】">CSS技巧:如何避免IE6中的&quot;浮动下坠&quot;【译】</a></li><li>2010-10-25 -- <a href="http://ofcss.com/2010/10/25/understanding-user-behavior-google-analytics-event-tracking-jquer.html" title="借助jQuery和Google分析服务的事件追踪深入了解用户行为【译】">借助jQuery和Google分析服务的事件追踪深入了解用户行为【译】</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://ofcss.com/2009/07/01/asp-net-mvc-best-practices-part-2.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>ASP.NET MVC 最佳实践（一）</title>
		<link>http://ofcss.com/2009/06/28/asp-net-mvc-best-practices-part-1.html</link>
		<comments>http://ofcss.com/2009/06/28/asp-net-mvc-best-practices-part-1.html#comments</comments>
		<pubDate>Sun, 28 Jun 2009 06:18:23 +0000</pubDate>
		<dc:creator>小李刀刀</dc:creator>
				<category><![CDATA[所谓技术]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[开发]]></category>
		<category><![CDATA[最佳实践]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://wukangrui.net/2009/06/28/asp-net-mvc-best-practices-part-1.html</guid>
		<description><![CDATA[<p>本系列翻译自 Kazi Manzur Rashid 的博客，由于翻译水平有限，本系列可能存在误解偏差或者翻译不准的地方，建议对比原文进行阅读。由于篇幅关系，原文中的一篇文章在本系列中将拆解成多篇发布。本篇包括原文第一部分中的1-6节。 <a href="http://ofcss.com/2009/06/28/asp-net-mvc-best-practices-part-1.html">Continue reading <span class="meta-nav">&#8594;</span></a></p>
]]></description>
			<content:encoded><![CDATA[<p><em>本系列翻译自 </em><a rel="external" href="http://weblogs.asp.net/rashid/default.aspx" target="_blank"><em>Kazi Manzur Rashid</em></a><em> 的博客，由于翻译水平有限，本系列可能存在误解偏差或者翻译不准的地方，建议对比<a rel="external" href="http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx" target="_blank">原文</a>进行阅读。由于篇幅关系，原文中的一篇文章在本系列中将拆解成多篇发布。本篇包括原文第一部分中的1-6节。</em></p>
<p><span id="more-821"></span></p>
<h4>1. 创建UrlHelper的扩展方法来生成url</h4>
<p>尽量避免以字符串方式直接传递controller, action 或者 route name，最好是为创建为UrlHelper创建扩展方法来封装url。例如：</p>
<pre class="code"><span style="color: blue">public static class </span><span style="color: #2b91af">UrlHelperExtension </span>{
    <span style="color: blue">public static string </span>Home(<span style="color: blue">this </span>UrlHelper helper) {
        <span style="color: blue">return </span>helper.Content(<span style="color: #a31515">"~/"</span>);
    }

    <span style="color: blue">public static string </span>SignUp(<span style="color: blue">this </span>UrlHelper helper) {
        <span style="color: blue">return </span>helper.RouteUrl(<span style="color: #a31515">"Signup"</span>);
    }

    <span style="color: blue">public static string </span>Dashboard(<span style="color: blue">this </span>UrlHelper helper) {
        <span style="color: blue">return </span>Dashboard(helper, StoryListTab.Unread);
    }

    <span style="color: blue">public static string </span>Dashboard(<span style="color: blue">this </span>UrlHelper helper, StoryListTab tab) {
        <span style="color: blue">return </span>Dashboard(helper, tab, OrderBy.CreatedAtDescending, 1);
    }

    <span style="color: blue">public static string </span>Dashboard(<span style="color: blue">this </span>UrlHelper helper, StoryListTab tab, OrderBy orderBy, <span style="color: blue">int </span>page) {
        <span style="color: blue">return </span>helper.RouteUrl(<span style="color: #a31515">"Dashboard"</span>, <span style="color: blue">new </span>{ tab = tab.ToString(), orderBy = orderBy.ToString(), page });
    }

    <span style="color: blue">public static string </span>Update(<span style="color: blue">this </span>UrlHelper helper) {
        <span style="color: blue">return </span>helper.RouteUrl(<span style="color: #a31515">"Update"</span>);
    }

    <span style="color: blue">public static string </span>Submit(<span style="color: blue">this </span>UrlHelper helper) {
        <span style="color: blue">return </span>helper.RouteUrl(<span style="color: #a31515">"Submit"</span>);
    }
}</pre>
<p>这样，如果在你的视图中有类似这样的代码：</p>
<pre class="code">    <span style="background: #ffee62">&lt;%</span><span style="color: blue">= </span>Html.ActionLink(<span style="color: #a31515">"Dashboard"</span>, <span style="color: #a31515">"Dashboard"</span>, <span style="color: #a31515">"Story"</span>) <span style="background: #ffee62">%&gt;
</span>    <span style="color: blue">&lt;</span><span style="color: #a31515">a </span><span style="color: red">href</span><span style="color: blue">="</span><span style="background: #ffee62">&lt;%</span>= Url.RouteUrl("Profile")<span style="background: #ffee62">%&gt;</span><span style="color: blue">"&gt;</span>Profile<span style="color: blue">&lt;/</span><span style="color: #a31515">a</span><span style="color: blue">&gt;</span></pre>
<p>你就可以用下面的这种方式来代替它们：</p>
<p><a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: blue">&lt;</span><span style="color: #a31515">a </span><span style="color: red">href</span><span style="color: blue">="</span><span style="background: #ffee62">&lt;%</span>= Url.Dashboard() <span style="background: #ffee62">%&gt;</span><span style="color: blue">"&gt;</span>Dashboard<span style="color: blue">&lt;/</span><span style="color: #a31515">a</span><span style="color: blue">&gt;
&lt;</span><span style="color: #a31515">a </span><span style="color: red">href</span><span style="color: blue">="</span><span style="background: #ffee62">&lt;%</span>= Url.Profile() <span style="background: #ffee62">%&gt;</span><span style="color: blue">"&gt;</span>Profile<span style="color: blue">&lt;/</span><span style="color: #a31515">a</span><span style="color: blue">&gt;</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>在控制器里也可以用，原来的代码：</p>
<pre class="code"><span style="color: blue">return </span>RedirectToAction(
    <span style="color: #a31515">"Dashboard"</span>,
    <span style="color: #a31515">"Story"</span>,
    <span style="color: blue">new </span>{
        tab = StoryListTab.Favorite,
        orderBy = OrderBy.CreatedAtAscending,
        page = 1
    }
);</pre>
<p>可以写成：</p>
<pre class="code"><span style="color: blue">return </span>Redirect(Url.Dashboard(StoryListTab.Favorite,
                                OrderBy.CreatedAtAscending, 1));</pre>
<p>当然了，你也可以使用 <a rel="external" href="http://www.tampadev.org/News/Details/DownloadASPNETMVCFuturesMicrosoftWebMvcAssembly" target="_blank">future assembly</a> 中的强类型版本来获得控制器、方法以及参数值，或者创建你自己的强类型版本以免将来进行重构时太过于痛苦，但是请一定记住，它没有官方支持并且将来有可能会发生改变。上面的方式也可以和强类型版本搭配使用。当然“另外增加一个间接层”（Scott Ha 喜欢用的引语）有一些好处，在编写单元测试的时候还有一个好处是你只需处理 <a rel="tag" href="http://msdn.microsoft.com/zh-cn/library/system.web.mvc.redirectresult.aspx" target="_blank">RedirectResult</a> 而无须同时处理 RedirectResult 和 <a rel="tag" href="http://msdn.microsoft.com/zh-cn/library/system.web.mvc.redirecttorouteresult.aspx" target="_blank">RedirectToRouteResult</a>。</p>
<h4>2. Create Extension Method of UrlHelper to map your JavaScript, Stylesheet and Image Folder</h4>
<p>默认情况下 <a rel="tag" href="http://www.asp.net/mvc/" target="_blank">ASP.NET MVC</a> 创建Content、Scripts文件夹来做这些事，这一点我不喜欢，我更喜欢下面的这种文件夹结构，这样在IIS里我可以只让静态文件缓存在Assets文件夹而不是多个文件夹：</p>
<ul>
<li>Assets</li>
<li>+images</li>
<li>+scripts</li>
<li>+stylesheets</li>
</ul>
<p>无论是结构是什么样的，还是要为UrlHelper创建一些扩展方法来映射这些文件夹，以便你在视图中可以很方便地指向它们，这样以后如果你要改变目录结构，你就无须做大量查找替换的工作。我还建议你为所有经常在视图中用到的资源创建UrlHelper的扩展方法。例如：</p>
<pre class="code"><span style="color: blue">public static string </span>Image(<span style="color: blue">this </span>UrlHelper helper, <span style="color: blue">string </span>fileName) {
    <span style="color: blue">return </span>helper.Content(<span style="color: #a31515">"~/assets/images/{0}"</span>.FormatWith(fileName));
}

<span style="color: blue">public static string </span>Stylesheet(<span style="color: blue">this </span>UrlHelper helper, <span style="color: blue">string </span>fileName) {
    <span style="color: blue">return </span>helper.Content(<span style="color: #a31515">"~/assets/stylesheets/{0}"</span>.FormatWith(fileName));
}

<span style="color: blue">public static string </span>NoIcon(<span style="color: blue">this </span>UrlHelper helper) {
    <span style="color: blue">return </span>Image(helper, <span style="color: #a31515">"noIcon.png"</span>);
}</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>然后当需要用到这些路径的时候，原来的代码</p>
<pre class="code"><span style="color: blue">&lt;</span><span style="color: #a31515">link </span><span style="color: red">href</span><span style="color: blue">="../../Content/Site.css" </span><span style="color: red">rel</span><span style="color: blue">="stylesheet" </span><span style="color: red">type</span><span style="color: blue">="text/css" /&gt;</span></pre>
<p>就可以写成这样：<a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: blue">&lt;</span><span style="color: #a31515">link </span><span style="color: red">href</span><span style="color: blue">="</span><span style="background: #ffee62">&lt;%</span>= Url.Stylesheet("site.css")<span style="background: #ffee62">%&gt;</span><span style="color: blue">" </span><span style="color: red">rel</span><span style="color: blue">="stylesheet" </span><span style="color: red">type</span><span style="color: blue">="text/css" /&gt;</span></pre>
<h4>3. 中使用启动加载器</h4>
<p>我在<a rel="prev" href="http://weblogs.asp.net/rashid/archive/2009/02/17/use-bootstrapper-in-your-asp-net-mvc-application-and-reduce-code-smell.aspx" target="_blank">以前的文章</a>中已经提到过这一点，总的来说就是：如果你要在 Global.asax 的 Application_Start 中做很多处理，比如 注册路由规则(Routers)、注册控制器工厂(Controller Factory)、模型绑定(Model Binders)、视图引擎(View Engine)、启动程序级的特定<a rel="external" href="http://weblogs.asp.net/rashid/archive/2009/03/05/use-event-aggregator-to-make-your-application-more-extensible.aspx" target="_blank">后台服务</a>或者为特定部分创建独立任务等，那就用<a rel="prev" href="http://weblogs.asp.net/rashid/archive/2009/03/05/use-event-aggregator-to-make-your-application-more-extensible.aspx" target="_blank">启动加载器</a>来执行这些操作。这会让你的代码更简洁，更可测。这一点在用 <a rel="tag" href="http://www.asp.net/mvc/" target="_blank">ASP.NET MVC</a> 构建门户级应用时尤其有用，因为这类应用中的每个模块都可能有一些不影响到其他模块的启动初始化操作。但是如果你只是开发一个小应用，上面提到的这些对你来说都不是问题，你当然也可以继续用默认的 global.asax。</p>
<h4>4. 不要硬编码对依赖注入容器的调用，用通用服务定位器来代替。</h4>
<p>不要让任何特定的依赖注入容器(DI Container)打乱你的代码，更好的办法是使用通用服务定位器(<a rel="external" href="http://commonservicelocator.codeplex.com/" target="_blank">Common Service Locator</a>)，它是对底层依赖注入进行的抽象，已经支持几乎所有流行的依赖注入容器，它让你可以任意替换底层的依赖注入对象而无须修改代码，因为每个依赖注入容器都具有一些不同于其它容器的特性。<a rel="external" href="http://devlicio.us/blogs/tim_barcz/" target="_blank">Tim Barcz</a> 最近写了一篇关于这个话题的<a rel="external" href="http://devlicio.us/blogs/tim_barcz/archive/2009/03/29/your-ioc-container-choice-is-not-a-feature-of-your-application.aspx" target="_blank">很棒的文章</a>，我不太明白他为什么没有提到我们对我们偏爱的依赖注入容器有多痴迷。通用服务定位器对绝大部分常用情景都提供了支持，而对一些特殊情况比如对已实体化对象的依赖注入，我印象里有 <a rel="external" href="http://codebetter.com/blogs/jeremy.miller/archive/2009/01/16/quot-buildup-quot-existing-objects-with-structuremap.aspx" target="_blank">StructureMap</a>、<a rel="external" href="http://codeclimber.net.nz/archive/2009/02/10/how-to-use-ninject-to-inject-dependencies-into-asp.net-mvc.aspx" target="_blank">Ninject</a> 和 Unity，你可以调用静态方法 ServiceLocator.Current.GetInstance 来代替。 通用服务定位器 是 <a rel="external" href="http://codebetter.com/blogs/jeremy.miller/archive/2008/08/16/it-s-time-for-ioc-container-detente.aspx" target="_blank">Jeremy D Miller</a> 发起的 依赖注入容器开发者(the DI Containers creators)的集体作品。</p>
<p>用通用服务定位器创建控制器工厂非常容易：</p>
<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">CommonServiceLocatorControllerFactory </span>: <span style="color: #2b91af">DefaultControllerFactory </span>{
    <span style="color: blue">protected override </span><span style="color: #2b91af">IController </span>GetControllerInstance(<span style="color: #2b91af">Type </span>controllerType) {
        <span style="color: blue">return </span>(controllerType == <span style="color: blue">null</span>) ? <span style="color: blue">base</span>.GetControllerInstance(controllerType) : ServiceLocator.Current.GetInstance(controllerType) <span style="color: blue">as </span><span style="color: #2b91af">IController</span>;
    }
}</pre>
<p>我希望 MVCContrib 的成员们也用这样的方式，而不是为每个容器创建一个独立的控制器工厂。</p>
<h4>5. 用恰当的的AcceptVerbs属性来修饰你的控制器方法</h4>
<p><a rel="tag" href="http://www.asp.net/mvc/" target="_blank">ASP.NET MVC</a> 比 Web Forms 更容易遭受攻击。所以务必让对数据进行修改的控制器方法只接受 Post 方式请求（HttpVerbs.Post）。如果安全对你来说至关重要，你还可以用 ValidateAntiForgeryToken 或者 Captcha。我强烈推荐 <a rel="external" href="http://devlicio.us/blogs/derik_whittaker/" target="_blank">Derik Whittaker</a> 写的一篇 <a rel="external" href="http://devlicio.us/blogs/derik_whittaker/archive/2008/12/02/using-recaptcha-with-asp-net-mvc.aspx" target="_blank">很好的文章</a> 以及<a rel="external" href="http://www.dimecasts.net/Casts/CastDetails/76" target="_blank">视频</a>介绍了怎么把 reCaptcha 整合到 <a rel="tag" href="http://www.asp.net/mvc/" target="_blank">ASP.NET MVC</a> 应用程序（作者注：不要错过了 <a rel="external" href="http://www.dimecasts.net/" target="_blank">DimeCasts.net</a> 的另一个短片，我从中学到了很多）。我的经验法则是对所有数据修改方法使用 HttpVerbs.Post，对所有数据读取操作使用 HttpVerbs.Get。</p>
<h4>6. 用OutputCache修饰你的频繁调用的操作方法</h4>
<p>当你需要返回不频繁更新的数据时使用 OutputCache 属性，最常见的例子是首页、Feed等。你在返回Html以及Json数据类型的方法中都可以使用这个属性。在使用的时候，注意只指定CacheProfile参数，不要指定任何其它东西，用 web.config 输出缓存控制节可以很好地调整它。例如：</p>
<pre class="code">[<span style="color: #2b91af">AcceptVerbs</span>(<span style="color: #2b91af">HttpVerbs</span>.Get), <span style="color: #2b91af">OutputCache</span>(CacheProfile = <span style="color: #a31515">"Dashboard"</span>)]
<span style="color: blue">public </span><span style="color: #2b91af">ActionResult </span>Dashboard(<span style="color: blue">string </span>userName, StoryListTab tab, OrderBy orderBy, <span style="color: blue">int</span>? page) {
}</pre>
<p>在 web.config 文件中的配置：<a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: blue">&lt;</span><span style="color: #a31515">system.web</span><span style="color: blue">&gt;
    &lt;</span><span style="color: #a31515">caching</span><span style="color: blue">&gt;
        &lt;</span><span style="color: #a31515">outputCacheSettings</span><span style="color: blue">&gt;
            &lt;</span><span style="color: #a31515">outputCacheProfiles</span><span style="color: blue">&gt;
                &lt;</span><span style="color: #a31515">clear</span><span style="color: blue">/&gt;
                &lt;!-- </span><span style="color: green">15 Seconds </span><span style="color: blue">--&gt;
                &lt;</span><span style="color: #a31515">add
                    </span><span style="color: red">name</span><span style="color: blue">=</span>"<span style="color: blue">Dashboard</span>"
                    <span style="color: red">duration</span><span style="color: blue">=</span>"<span style="color: blue">15</span>"
                    <span style="color: red">varyByParam</span><span style="color: blue">=</span>"<span style="color: blue">*</span>"
                    <span style="color: red">location</span><span style="color: blue">=</span>"<span style="color: blue">Client</span>"
                    <span style="color: blue">/&gt;
            &lt;/</span><span style="color: #a31515">outputCacheProfiles</span><span style="color: blue">&gt;
        &lt;/</span><span style="color: #a31515">outputCacheSettings</span><span style="color: blue">&gt;
    &lt;/</span><span style="color: #a31515">caching</span><span style="color: blue">&gt;
&lt;/</span><span style="color: #a31515">system.web</span><span style="color: blue">&gt;</span></pre>
<p>请继续阅读：《<a rel="next" href="http://wukangrui.net/2009/07/01/asp-net-mvc-best-practices-part-2.html">ASP.NET MVC 最佳实践（二）</a>》</p>
<h3  class="related_post_title">相关日志</h3><ul class="related_post"><li>2009-07-08 -- <a href="http://ofcss.com/2009/07/08/asp-net-mvc-best-practices-part-4.html" title="ASP.NET MVC 最佳实践（四）">ASP.NET MVC 最佳实践（四）</a></li><li>2009-07-07 -- <a href="http://ofcss.com/2009/07/07/asp-net-mvc-best-practices-part-3.html" title="ASP.NET MVC 最佳实践（三）">ASP.NET MVC 最佳实践（三）</a></li><li>2009-07-01 -- <a href="http://ofcss.com/2009/07/01/asp-net-mvc-best-practices-part-2.html" title="ASP.NET MVC 最佳实践（二）">ASP.NET MVC 最佳实践（二）</a></li><li>2009-04-13 -- <a href="http://ofcss.com/2009/04/13/aspnetmvc-hands-on-labs-develop-application.html" title="[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用">[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用</a></li><li>2009-04-09 -- <a href="http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-create-application.html" title="[翻译]ASP.NET MVC动手实验1-2：创建ASP.NET MVC应用">[翻译]ASP.NET MVC动手实验1-2：创建ASP.NET MVC应用</a></li><li>2009-04-09 -- <a href="http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-introduction.html" title="[翻译]ASP.NET MVC动手系列1-1：ASP.NET MVC概述">[翻译]ASP.NET MVC动手系列1-1：ASP.NET MVC概述</a></li><li>2011-03-14 -- <a href="http://ofcss.com/2011/03/14/browser-rendering-optimizer-translation-extra.html" title="对《优化浏览器渲染》的补充【译】">对《优化浏览器渲染》的补充【译】</a></li><li>2011-03-10 -- <a href="http://ofcss.com/2011/03/10/css-border-tricks-with-collapsed-boxes-translation.html" title="CSS边框实现&ldquo;无图化&rdquo;设计【译】">CSS边框实现&ldquo;无图化&rdquo;设计【译】</a></li><li>2010-10-31 -- <a href="http://ofcss.com/2010/10/31/prevent-a-float-drop-in-ie6-translation.html" title="CSS技巧:如何避免IE6中的&quot;浮动下坠&quot;【译】">CSS技巧:如何避免IE6中的&quot;浮动下坠&quot;【译】</a></li><li>2010-10-25 -- <a href="http://ofcss.com/2010/10/25/understanding-user-behavior-google-analytics-event-tracking-jquer.html" title="借助jQuery和Google分析服务的事件追踪深入了解用户行为【译】">借助jQuery和Google分析服务的事件追踪深入了解用户行为【译】</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://ofcss.com/2009/06/28/asp-net-mvc-best-practices-part-1.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>VS2008 中 JS IntelliSense出错的解决</title>
		<link>http://ofcss.com/2009/04/30/solution-for-js-intellisense-error.html</link>
		<comments>http://ofcss.com/2009/04/30/solution-for-js-intellisense-error.html#comments</comments>
		<pubDate>Thu, 30 Apr 2009 18:55:01 +0000</pubDate>
		<dc:creator>小李刀刀</dc:creator>
				<category><![CDATA[所谓技术]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[Visual Studio]]></category>

		<guid isPermaLink="false">http://wukangrui.net/2009/04/30/solution-for-js-intellisense-error.html</guid>
		<description><![CDATA[<p>JScript 智能提示(IntelliSense)是 Visual Studio 2008 的一个重要新特性，尤其是现在 Visual Studio 2008 还提供了 jQuery 智能提示的官方支持。遗憾的是在实际应用中经常遇到如图所示的错误提示，并且出现这样的错误以后，包括jQuery在内的智能提示统统失效。非常让人恼火。我最开始的时候仅从提示“Error updating JScript IntelliSense”猜测以为是VS把智能提示缓存起来，然后缓存文件出错。后来尝试过很多方式都无法解决。 后来偶然看到 jeffrey 写的 日志，才知道原来是由于加载的额外js文件缺乏智能提示文件（vsdoc.js）导致的。 jeffrey 有一个非常简单易行的解决建议，就是创建一个和出错文件对应的 –vsdoc.js 文件放到该文件相同的目录下。比如 ASP.NET MVC 里默认就为jQuery.min.js 提供一个 jQuery.min-vsdoc.js 文件， 同样的道理，如果我们在 Scripts 目录下有一个 myScript.js 文件， 那么我们只需要创建一个空的 myScript-vsdoc.js 文件放到 Scripts 目录下，就可以避免因为加载 myScript.js &#8230; <a href="http://ofcss.com/2009/04/30/solution-for-js-intellisense-error.html">Continue reading <span class="meta-nav">&#8594;</span></a></p>
]]></description>
			<content:encoded><![CDATA[<p>JScript 智能提示(IntelliSense)是 Visual Studio 2008 的一个重要新特性，尤其是现在 Visual Studio 2008 还提供了 jQuery 智能提示的官方支持。遗憾的是在实际应用中经常遇到如图所示的错误提示，并且出现这样的错误以后，包括jQuery在内的智能提示统统失效。非常让人恼火。我最开始的时候仅从提示“Error updating JScript IntelliSense”猜测以为是VS把智能提示缓存起来，然后缓存文件出错。后来尝试过很多方式都无法解决。</p>
<p><img style="display: block; float: none; margin-left: auto; margin-right: auto" alt="JScript IntelliSense Error" src="http://blog.darkthread.net/photos/darkthread/images/4566/original.aspx" /></p>
<p> <span id="more-561"></span>
<p>后来偶然看到 <a href="http://blog.darkthread.net/blogs/darkthreadtw/" rel="nofollow" target="_blank">jeffrey</a> 写的 <a href="http://blog.darkthread.net/blogs/darkthreadtw/archive/2009/04/22/js-intellisense-error.aspx" target="_blank">日志</a>，才知道原来是由于加载的额外js文件缺乏智能提示文件（vsdoc.js）导致的。</p>
<p>jeffrey 有一个非常简单易行的解决建议，就是创建一个和出错文件对应的 –vsdoc.js 文件放到该文件相同的目录下。比如 ASP.NET MVC 里默认就为jQuery.min.js 提供一个 jQuery.min-vsdoc.js 文件， 同样的道理，如果我们在 Scripts 目录下有一个 myScript.js 文件， 那么我们只需要创建一个空的 myScript-vsdoc.js 文件放到 Scripts 目录下，就可以避免因为加载 myScript.js 文件导致的 JS 智能提示失效问题。</p>
<p>当然了，这只是权益之计，不过很有效，因为我们在编写js的时候，增加的js文件往往是自己写的脚本，也就无所谓有没有智能提示了，如果你真的需要智能提示，那么不妨写一个。</p>
<p>由这个问题引发出来一个问题，就是对于在网上发布共享JS库的人，是不是应该考虑完善你的作品，提供对应的&#160; -vsdoc.js 文件呢？</p>
<h3  class="related_post_title">相关日志</h3><ul class="related_post"><li>2009-04-13 -- <a href="http://ofcss.com/2009/04/13/aspnetmvc-hands-on-labs-develop-application.html" title="[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用">[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用</a></li><li>2009-04-09 -- <a href="http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-create-application.html" title="[翻译]ASP.NET MVC动手实验1-2：创建ASP.NET MVC应用">[翻译]ASP.NET MVC动手实验1-2：创建ASP.NET MVC应用</a></li><li>2009-04-09 -- <a href="http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-introduction.html" title="[翻译]ASP.NET MVC动手系列1-1：ASP.NET MVC概述">[翻译]ASP.NET MVC动手系列1-1：ASP.NET MVC概述</a></li><li>2010-10-25 -- <a href="http://ofcss.com/2010/10/25/understanding-user-behavior-google-analytics-event-tracking-jquer.html" title="借助jQuery和Google分析服务的事件追踪深入了解用户行为【译】">借助jQuery和Google分析服务的事件追踪深入了解用户行为【译】</a></li><li>2010-04-14 -- <a href="http://ofcss.com/2010/04/14/vs2010-rtm-avaliable-on-msdn.html" title="Visual Studio 2010 Ultimate RTM 版开放MSDN下载">Visual Studio 2010 Ultimate RTM 版开放MSDN下载</a></li><li>2010-04-04 -- <a href="http://ofcss.com/2010/04/04/vspaste-customization-with-net-reflector.html" title="VSPaste 的持续改造">VSPaste 的持续改造</a></li><li>2009-07-08 -- <a href="http://ofcss.com/2009/07/08/asp-net-mvc-best-practices-part-4.html" title="ASP.NET MVC 最佳实践（四）">ASP.NET MVC 最佳实践（四）</a></li><li>2009-07-07 -- <a href="http://ofcss.com/2009/07/07/asp-net-mvc-best-practices-part-3.html" title="ASP.NET MVC 最佳实践（三）">ASP.NET MVC 最佳实践（三）</a></li><li>2009-07-01 -- <a href="http://ofcss.com/2009/07/01/asp-net-mvc-best-practices-part-2.html" title="ASP.NET MVC 最佳实践（二）">ASP.NET MVC 最佳实践（二）</a></li><li>2009-06-28 -- <a href="http://ofcss.com/2009/06/28/asp-net-mvc-best-practices-part-1.html" title="ASP.NET MVC 最佳实践（一）">ASP.NET MVC 最佳实践（一）</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://ofcss.com/2009/04/30/solution-for-js-intellisense-error.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用</title>
		<link>http://ofcss.com/2009/04/13/aspnetmvc-hands-on-labs-develop-application.html</link>
		<comments>http://ofcss.com/2009/04/13/aspnetmvc-hands-on-labs-develop-application.html#comments</comments>
		<pubDate>Mon, 13 Apr 2009 17:41:50 +0000</pubDate>
		<dc:creator>小李刀刀</dc:creator>
				<category><![CDATA[所谓技术]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<category><![CDATA[动手实验]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://wukangrui.net/2009/04/13/aspnetmvc-hands-on-labs-develop-application.html</guid>
		<description><![CDATA[<p>在这次的练习中，你将学习如何开发一个 ASP.NET 应用，整个练习过程包括创建控制器、视图和模型。 通过这次练习，要创建一个分页显示用户列表的 ASP.NET MVC应用。具有显示用户资料，以及创建、修改、删除用户地址的功能。该应用共有三个控制器：用户控制器处理用户信息列表和显示用户信息；地址控制器负责用户地址的创建、编辑和删除；主页控制器负责显示站点的欢迎页面。 提示：为了确保每个步骤都正常运行，最好在每个任务结束的时候都编译整个解决方案。 任务1 &#8211; 创建实体数据模型 在本任务中你要用 实体数据模型（Entity Data Model）创建可编程实体类到数据存储结构（通常是SQL Server数据库或者其他数据库）的映射。Entity Data Model (EDM) 是在 实体框架 (Entity Framework）中专门设计来作为程序中的数据定义的。 点击 开始 &#124; 所有程序 &#124; Microsoft Visual Studio 2008 &#124; Microsoft Visual Studio 2008 启动 VS 2008。 打开 &#8230; <a href="http://ofcss.com/2009/04/13/aspnetmvc-hands-on-labs-develop-application.html">Continue reading <span class="meta-nav">&#8594;</span></a></p>
]]></description>
			<content:encoded><![CDATA[<p>在这次的练习中，你将学习如何开发一个 ASP.NET 应用，整个练习过程包括创建控制器、视图和模型。</p>
<p>通过这次练习，要创建一个分页显示用户列表的 ASP.NET MVC应用。具有显示用户资料，以及创建、修改、删除用户地址的功能。该应用共有三个控制器：用户控制器处理用户信息列表和显示用户信息；地址控制器负责用户地址的创建、编辑和删除；主页控制器负责显示站点的欢迎页面。</p>
<p>提示：为了确保每个步骤都正常运行，最好在每个任务结束的时候都编译整个解决方案。</p>
<p> <span id="more-474"></span>
<p><b>任务1 - 创建实体数据模型</b></p>
<p>在本任务中你要用 <strong>实体数据模型</strong>（<strong>Entity Data Model</strong>）创建可编程实体类到数据存储结构（通常是SQL Server数据库或者其他数据库）的映射。<strong>Entity Data Model</strong> (EDM) 是在 实体框架 (Entity Framework）中专门设计来作为程序中的数据定义的。</p>
<ol>
<li>点击 <strong>开始 | 所有程序 | Microsoft Visual Studio 2008 | Microsoft Visual Studio 2008</strong> 启动 VS 2008。</li>
<li> 打开 <strong>AspNetMvcEx02-DevelopingMvcAppbegin</strong> 目录下的解决方案文件 <strong>MvcSampleApp.sln</strong>。</li>
<li>在 <strong>解决方案浏览器</strong> 中， 打开 <strong>Views</strong> 目录下的 <strong>Shared</strong> 目录。步骤：点击目录名字前面的加号图标，选择 <strong>Site.Master</strong> 文件， 右键点击它并选择 <strong>删除 </strong>。</li>
<li>导入提供的 Site.Master。 步骤：右键点击 <strong>Shared</strong> 文件夹， 指向 <strong>添加</strong> 然后选择 <strong>现有项</strong>。在 <strong>添加现有项</strong> 对话框中，转到 <strong>AspNetMvcAssetsShared</strong> 目录，然后选中该目录下的所有文件（实际就只有一个 Site.Master 文件），点 <strong>添加</strong>。&#160;
<p>提示：与传统的 ASP.NET Web 页面一样， ASP.NET 页面视图 (.aspx 文件)可以使用母版页来为站点的通用部分提供统一的样式。</p>
</li>
<li>
<p>创建 <strong>AdventureWorks</strong> 实体数据模型。步骤：在 解决方案浏览器 中， 在 <strong>MvcSampleApp</strong> 项目中右键点击 <strong>Models</strong> 文件夹， 指向 <strong>添加</strong>， 然后点击 <strong>新建项</strong>。</p>
</li>
<li>在 <strong>新建项</strong> 对话框里选择 <strong>ADO.NET Entity Data Model</strong>。将要创建的项命名为 <strong>AdventureWorks.edmx</strong>，然后点 <strong>添加</strong>。&#160;
<p><a href="http://file.wukangrui.com/attachments/2009/04/image7.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://file.wukangrui.com/attachments/2009/04/image-thumb7.png" width="644" height="437" /></a>        <br /><em><strong>图一</strong> 添加 ADO.NET Entity Data Model</em></p>
</li>
<li>在 <strong>实体模型数据向导</strong> 打开后， 选择 <strong>从数据库生成</strong> 然后点击 <strong>下一步</strong>。</li>
<li>点击 <strong>新建连接</strong> 定义数据库连接。 </li>
<li>In the <b>Choose Data Source</b> dialog, select <b>Microsoft SQL Server</b> as <b>Data Source</b> and click <b>Continue</b>. 在 <strong>选择数据库</strong> 对话框中，选择<strong> Microsoft SQL Server</strong> 作为数据源，然后点 <strong>继续</strong>。（这一步在大部分情况下默认不出现）</li>
<li>In the <b>Connection Properties</b> dialog window, enter <b>AspNetMvcLabs</b> as <b>Server Name</b>, then select <b>AdventureWorksLT</b> database and click <b>OK</b>.在 <strong>连接属性</strong> 对话框里，输入你的 <strong>本地 SQL Server 安装实例 </strong>名称（例如“.”或者“.SQLEXPRESS”），然后在下方选择 <strong>AdventureWorksLT</strong> 数据库 （没有安装 SQL Server 2005 示例数据库的可以从网上下载安装），然后点击 <strong>OK</strong>。
<p>Note:<b>AspNetMvcLabs</b> is the default alias for the database server installed when the <b>SetupEx.cmd</b> script was run at the beginning of this lab.</p>
<p><a href="http://file.wukangrui.com/attachments/2009/04/image8.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://file.wukangrui.com/attachments/2009/04/image-thumb8.png" width="455" height="733" /></a>&#160; <br /><em><b>图二 </b>定义数据连接</em></p>
</li>
<li>回到 <strong>实体数据模型向导</strong>， 点击 <strong>下一步</strong>。</li>
<li>在 <strong>选择数据库对象</strong> 这一步，只需要选择以下三个表：&#160;
<ul>
<li><b>Address (SalesLT)Address </b></li>
<li><b>Customer (SalesLT)</b></li>
<li><b>CustomerAddress (SalesLT)</b></li>
</ul>
</li>
<li>保持 <strong>模型命名空间</strong> 这一项为默认值，点击 <strong>完成</strong>。&#160;
<p><a href="http://file.wukangrui.com/attachments/2009/04/image9.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://file.wukangrui.com/attachments/2009/04/image-thumb9.png" width="484" height="484" /></a>&#160; <br /><strong>图三 </strong><i>选择要包含在模型中的数据库对象</i></p>
</li>
<li>
<p>创建数据容器类 AdventureWorksRepository，这个容器类向其它类暴露数据访问方法，通过这些方法从底层数据模型获取数据实体。在 MvcSampleApp 项目中右击 Models 目录，指向 添加 然后点击 现有项。转到 AspNetMvcAssets 目录， 选择 AdventureWorksRepository.cs 文件，然后点击 添加。       <br />提示：更多详细信息，参见 <a href="http://msdn2.microsoft.com/zh-cn/library/bb387122.aspx" target="_blank">实体数据模型</a> 。</p>
</li>
</ol>
<p>（未完待续）</p>
<h3  class="related_post_title">相关日志</h3><ul class="related_post"><li>2009-04-09 -- <a href="http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-create-application.html" title="[翻译]ASP.NET MVC动手实验1-2：创建ASP.NET MVC应用">[翻译]ASP.NET MVC动手实验1-2：创建ASP.NET MVC应用</a></li><li>2009-04-09 -- <a href="http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-introduction.html" title="[翻译]ASP.NET MVC动手系列1-1：ASP.NET MVC概述">[翻译]ASP.NET MVC动手系列1-1：ASP.NET MVC概述</a></li><li>2009-07-08 -- <a href="http://ofcss.com/2009/07/08/asp-net-mvc-best-practices-part-4.html" title="ASP.NET MVC 最佳实践（四）">ASP.NET MVC 最佳实践（四）</a></li><li>2009-07-07 -- <a href="http://ofcss.com/2009/07/07/asp-net-mvc-best-practices-part-3.html" title="ASP.NET MVC 最佳实践（三）">ASP.NET MVC 最佳实践（三）</a></li><li>2009-07-01 -- <a href="http://ofcss.com/2009/07/01/asp-net-mvc-best-practices-part-2.html" title="ASP.NET MVC 最佳实践（二）">ASP.NET MVC 最佳实践（二）</a></li><li>2009-06-28 -- <a href="http://ofcss.com/2009/06/28/asp-net-mvc-best-practices-part-1.html" title="ASP.NET MVC 最佳实践（一）">ASP.NET MVC 最佳实践（一）</a></li><li>2009-04-30 -- <a href="http://ofcss.com/2009/04/30/solution-for-js-intellisense-error.html" title="VS2008 中 JS IntelliSense出错的解决">VS2008 中 JS IntelliSense出错的解决</a></li><li>2011-03-14 -- <a href="http://ofcss.com/2011/03/14/browser-rendering-optimizer-translation-extra.html" title="对《优化浏览器渲染》的补充【译】">对《优化浏览器渲染》的补充【译】</a></li><li>2011-03-10 -- <a href="http://ofcss.com/2011/03/10/css-border-tricks-with-collapsed-boxes-translation.html" title="CSS边框实现&ldquo;无图化&rdquo;设计【译】">CSS边框实现&ldquo;无图化&rdquo;设计【译】</a></li><li>2010-10-31 -- <a href="http://ofcss.com/2010/10/31/prevent-a-float-drop-in-ie6-translation.html" title="CSS技巧:如何避免IE6中的&quot;浮动下坠&quot;【译】">CSS技巧:如何避免IE6中的&quot;浮动下坠&quot;【译】</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://ofcss.com/2009/04/13/aspnetmvc-hands-on-labs-develop-application.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[翻译]ASP.NET MVC动手实验1-2：创建ASP.NET MVC应用</title>
		<link>http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-create-application.html</link>
		<comments>http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-create-application.html#comments</comments>
		<pubDate>Thu, 09 Apr 2009 15:08:09 +0000</pubDate>
		<dc:creator>小李刀刀</dc:creator>
				<category><![CDATA[所谓技术]]></category>
		<category><![CDATA[Ajax]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<category><![CDATA[动手实验]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://wukangrui.net/2009/04/09/aspnetmvc-hands-on-labs-create-application.html</guid>
		<description><![CDATA[<p>在这个练习中你将学习如何在 Visual Studio 中创建一个 ASP.NET MVC 应用。并初步了解默认项目结构和一些开发约定。 ASP.NET MVC 框架分为 Model， View 和 Controller 三个部分。模型组件（Model）一般用于维护数据库中的持久数据层的状态。视图组件（View）由控制器挑选并渲染到适当的用户界面。默认状态下， ASP.NET MVC 框架用ASP.NET已有的 ASP.NET 页面（.aspx）、 母版页（.master）以及用户控件（.ascx）等类型来作为浏览器呈现。控制器组件（Controller）组件将用户请求定位到适当的控制器（Controller）行为（Action）和方法（Method），从请求中获取用于行为方法运行的参数值， 并处理行为和方法执行中可能产生的错误。 然后控制器用该请求对应的视图进行渲染。 默认状况下，每个组件存放在 MVC WEB 应用工程的一个独立目录下。 任务1 &#8211; 创建一个 ASP.NET MVC Web 应用工程 在这个任务中你将通过Visual Studio的MVC模板创建和配置一个空的 ASP.NET MVC 应用工程。 打开 &#8230; <a href="http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-create-application.html">Continue reading <span class="meta-nav">&#8594;</span></a></p>
]]></description>
			<content:encoded><![CDATA[<p>在这个练习中你将学习如何在 Visual Studio 中创建一个 ASP.NET MVC 应用。并初步了解默认项目结构和一些开发约定。</p>
<p>ASP.NET MVC 框架分为 Model， View 和 Controller 三个部分。模型组件（Model）一般用于维护数据库中的持久数据层的状态。视图组件（View）由控制器挑选并渲染到适当的用户界面。默认状态下， ASP.NET MVC 框架用ASP.NET已有的 ASP.NET 页面（.aspx）、 母版页（.master）以及用户控件（.ascx）等类型来作为浏览器呈现。控制器组件（Controller）组件将用户请求定位到适当的控制器（Controller）行为（Action）和方法（Method），从请求中获取用于行为方法运行的参数值， 并处理行为和方法执行中可能产生的错误。 然后控制器用该请求对应的视图进行渲染。 默认状况下，每个组件存放在 MVC WEB 应用工程的一个独立目录下。</p>
<p><span id="more-444"></span></p>
<p><strong>任务1 - 创建一个 ASP.NET MVC Web 应用工程</strong></p>
<p>在这个任务中你将通过Visual Studio的MVC模板创建和配置一个空的 ASP.NET MVC 应用工程。</p>
<ol>
<li>打开 Microsoft Visual Studio 2008：依次点击 <strong>开始</strong>|<strong>所有程序</strong>|<strong>Microsoft Visual Studio 2008</strong>|<strong>Microsoft Visual Studio 2008</strong>.</li>
<li>点击 <strong>文件</strong> 菜单， 指向 <strong>新建</strong>， 然后点击 <strong>项目</strong>。</li>
<li>在 <strong>新建项目</strong> 对话框中，确保右上角选中的版本为 <strong>.NET Framework 3.5</strong>，在 <strong>项目类型</strong> 下选择 <strong>Visual C#</strong>，然后在 <strong>Visual Studio 已安装的模板</strong> 中选择 <strong>ASP.NET MVC Web Application</strong> 类型。你可以设置项目位置为 <strong>你的路径AspNetMvcEx01-CreatingMvcAppbegin </strong>作为项目目录。</li>
<li>修改 <strong>名称</strong> 为 <strong>MvcSampleApp</strong> 然后点击 确定 。 <a href="http://file.wukangrui.com/attachments/2009/04/image.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" src="http://file.wukangrui.com/attachments/2009/04/image-thumb.png" border="0" alt="image" width="605" height="484" /> </a><em>图一：创建新项目对话框<br />
</em>在点击 确定 按钮之后，Visual Studio 会问你是否同时创建测试项目（would you like to create a test project）。选择 <strong>Yes</strong>， 输入 <strong>MvcSampleApp.Test</strong> 作为<strong>项目名称</strong>（<strong>Name</strong> of the Project）， 然后点击 <strong>确定</strong>（<strong>OK</strong>）。注：我安装的是Visual Studio 2008 SP1 简体中文版 + ASP.NET MVC 1.0，在我的系统上，创建测试项目的对话框为英文，因此这里加上英文对照。<br />
提示：当你创建一个新的MVC Web应用时， Visual Studio 会为你提供同时创建两个项目的选择，第一个项目是Web项目用来实现你的应用，第二个项目是一个测试项目，你可以在其中为你的MVC组件编写单元测试。<br />
<a href="http://file.wukangrui.com/attachments/2009/04/image2.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" src="http://file.wukangrui.com/attachments/2009/04/image-thumb2.png" border="0" alt="image" width="552" height="367" /></a><br />
<em>图二：创建 MVC 单元测试项目<br />
</em>提示：测试项目对话框中的 测试框架（Test Framework） 下拉列表现在只有一个 Visual Studio Unit Test，这个列表是可扩展的，在你的机器上安装了其他测试框架的时候，它将会包含其他的测试框架供你选择。由此你可以容易地用你喜欢的单元测试框架来为你的 ASP.NET MVC 应用编写单元测试。</li>
<li>在Visual Studio 中配置站点使用端口 50000。这一步是为了和本动手实验中的后续联系保持一致所必须的。
<ol type="a">
<li>在 <strong>解决方案浏览器</strong> 中，右击 <strong>MvcSampleApp</strong> 项目，并在上下文菜单中选择 <strong>属性</strong> 。</li>
<li>在 <strong>属性</strong> 页中，打开 <strong>Web</strong> 标签。</li>
<li>In the <strong>Servers</strong> section select <strong>Specific Port</strong>. 在 <strong>服务器</strong> 设置项下面，选择 <strong>使用Visual Studio开发服务器</strong>，并选中 <strong>特定端口</strong> 。</li>
<li>设置端口号为 <strong>50000</strong> 。</li>
<li>按下 <strong>Ctr + S</strong> 保存所做的改动。</li>
</ol>
<p><a href="http://file.wukangrui.com/attachments/2009/04/image3.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" src="http://file.wukangrui.com/attachments/2009/04/image-thumb3.png" border="0" alt="image" width="635" height="480" /></a><br />
<em>图三：配置本地测试属性</em></li>
</ol>
<h3  class="related_post_title">相关日志</h3><ul class="related_post"><li>2009-04-13 -- <a href="http://ofcss.com/2009/04/13/aspnetmvc-hands-on-labs-develop-application.html" title="[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用">[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用</a></li><li>2009-04-09 -- <a href="http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-introduction.html" title="[翻译]ASP.NET MVC动手系列1-1：ASP.NET MVC概述">[翻译]ASP.NET MVC动手系列1-1：ASP.NET MVC概述</a></li><li>2009-07-08 -- <a href="http://ofcss.com/2009/07/08/asp-net-mvc-best-practices-part-4.html" title="ASP.NET MVC 最佳实践（四）">ASP.NET MVC 最佳实践（四）</a></li><li>2009-07-07 -- <a href="http://ofcss.com/2009/07/07/asp-net-mvc-best-practices-part-3.html" title="ASP.NET MVC 最佳实践（三）">ASP.NET MVC 最佳实践（三）</a></li><li>2009-07-01 -- <a href="http://ofcss.com/2009/07/01/asp-net-mvc-best-practices-part-2.html" title="ASP.NET MVC 最佳实践（二）">ASP.NET MVC 最佳实践（二）</a></li><li>2009-06-28 -- <a href="http://ofcss.com/2009/06/28/asp-net-mvc-best-practices-part-1.html" title="ASP.NET MVC 最佳实践（一）">ASP.NET MVC 最佳实践（一）</a></li><li>2011-03-14 -- <a href="http://ofcss.com/2011/03/14/browser-rendering-optimizer-translation-extra.html" title="对《优化浏览器渲染》的补充【译】">对《优化浏览器渲染》的补充【译】</a></li><li>2009-06-22 -- <a href="http://ofcss.com/2009/06/22/whatever-hover-pseudo-class-without-javascript.html" title="Whatever:hover &#8211; 无需javascript让IE支持丰富伪类">Whatever:hover &#8211; 无需javascript让IE支持丰富伪类</a></li><li>2009-04-30 -- <a href="http://ofcss.com/2009/04/30/solution-for-js-intellisense-error.html" title="VS2008 中 JS IntelliSense出错的解决">VS2008 中 JS IntelliSense出错的解决</a></li><li>2011-03-10 -- <a href="http://ofcss.com/2011/03/10/css-border-tricks-with-collapsed-boxes-translation.html" title="CSS边框实现&ldquo;无图化&rdquo;设计【译】">CSS边框实现&ldquo;无图化&rdquo;设计【译】</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-create-application.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[翻译]ASP.NET MVC动手系列1-1：ASP.NET MVC概述</title>
		<link>http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-introduction.html</link>
		<comments>http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-introduction.html#comments</comments>
		<pubDate>Thu, 09 Apr 2009 14:41:35 +0000</pubDate>
		<dc:creator>小李刀刀</dc:creator>
				<category><![CDATA[所谓技术]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[MVC]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<category><![CDATA[动手实验]]></category>
		<category><![CDATA[翻译]]></category>

		<guid isPermaLink="false">http://wukangrui.net/2009/04/09/aspnetmvc-hands-on-labs-introduction.html</guid>
		<description><![CDATA[<p>MVC(Model View Controller) 架构模式通常分为三个主要部分： 模型（Models）：模型对象一般是在应用中实现逻辑域的部分。通常情况下，模型对象用于在数据库中取得和存储模型状态。 视图（Views）：视图是应用中显示用户界面（UI）的组件。典型状况下，用户界面是根据模型数据创建。比如一个产品表的编辑视图中可能包括基于当前产品对象状态的文本框、下拉列表以及单选、复选框等。 控制器（Controllers）：控制器是处理用户交互，操作对应模型、最后选择相应视图来渲染用户界面的组件。在一个MVC架构的应用中，视图只用于显示信息；控制器对用户的输入和交互行为进行处理和响应。 MVC模式可以帮助你将应用拆分的不同方面拆分以形成多层结构（输入逻辑、商业逻辑和用户界面逻辑等），并为这些不同的层提供宽松的耦合关系。这样你就能在开发过程中一次只将精力集中于实现其中的一个方面。除了在处理复杂事务的优势外，调试基于MVC模式的应用也比调试传统ASP.NET WEB应用程序更容易， 因此MVC模式鼓励使用测试驱动开发（TDD: Test-driven development）来创建应用程序。 ASP.NET MVC 框架为ASP.NET Web Forms开发人员提供了创建基于MVC的WEB应用的选择。ASP.NET MVC 框架是一个轻量级的、高可测试性的框架，它与基于Web Forms的应用程序一样，结合了现有的ASP.NET特性，例如母版页和基于用户（Membership-based）的认证等。 除此之外，MVC应用程序三个主要组件之间的宽耦合更方便进行多人并行开发。例如，一个开发人员开发试图，另一个开发人员开发逻辑控制器，与此同时还有一个开发人员在模型层进行商业逻辑开发。 目标 在这个动手实验室系列中，你将学习到： 认识和了解 ASP.NET MVC 框架； 创建一个 ASP.NET MVC 应用程序； 在创建 ASP.NET MVC 应用程序的过程中执行测试。 &#160; 系统需求 为了完成此实验，你必须具备以下的系统配置环境： Microsoft Visual &#8230; <a href="http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-introduction.html">Continue reading <span class="meta-nav">&#8594;</span></a></p>
]]></description>
			<content:encoded><![CDATA[<p>MVC(Model View Controller) 架构模式通常分为三个主要部分：</p>
<ul>
<li>模型（Models）：模型对象一般是在应用中实现逻辑域的部分。通常情况下，模型对象用于在数据库中取得和存储模型状态。</li>
<li>视图（Views）：视图是应用中显示用户界面（UI）的组件。典型状况下，用户界面是根据模型数据创建。比如一个产品表的编辑视图中可能包括基于当前产品对象状态的文本框、下拉列表以及单选、复选框等。</li>
<li>控制器（Controllers）：控制器是处理用户交互，操作对应模型、最后选择相应视图来渲染用户界面的组件。在一个MVC架构的应用中，视图只用于显示信息；控制器对用户的输入和交互行为进行处理和响应。</li>
</ul>
<p> <span id="more-442"></span>
<p>MVC模式可以帮助你将应用拆分的不同方面拆分以形成多层结构（输入逻辑、商业逻辑和用户界面逻辑等），并为这些不同的层提供宽松的耦合关系。这样你就能在开发过程中一次只将精力集中于实现其中的一个方面。除了在处理复杂事务的优势外，调试基于MVC模式的应用也比调试传统ASP.NET WEB应用程序更容易， 因此MVC模式鼓励使用测试驱动开发（TDD: Test-driven development）来创建应用程序。</p>
<p><strong>ASP.NET MVC</strong> 框架为ASP.NET Web Forms开发人员提供了创建基于MVC的WEB应用的选择。<strong>ASP.NET MVC</strong> 框架是一个轻量级的、高可测试性的框架，它与基于Web Forms的应用程序一样，结合了现有的ASP.NET特性，例如母版页和基于用户（Membership-based）的认证等。</p>
<p>除此之外，MVC应用程序三个主要组件之间的宽耦合更方便进行多人并行开发。例如，一个开发人员开发试图，另一个开发人员开发逻辑控制器，与此同时还有一个开发人员在模型层进行商业逻辑开发。</p>
<h3>目标</h3>
<p>在这个动手实验室系列中，你将学习到：</p>
<ul>
<li>认识和了解 ASP.NET MVC 框架；</li>
<li>创建一个 ASP.NET MVC 应用程序；</li>
<li>在创建 ASP.NET MVC 应用程序的过程中执行测试。</li>
</ul>
<p>&#160;</p>
<p> <a href="#" name="_Toc168302999"></a><a href="#" name="_Toc157870738"></a><br />
<h3>系统需求</h3>
<p>为了完成此实验，你必须具备以下的系统配置环境：</p>
<ul>
<li>Microsoft Visual Studio 2008 SP1</li>
<li>Microsoft ASP.NET MVC 1.0 </li>
<li>Microsoft SQL 2005 or Microsoft SQL 2008 (体验版或更高版本) </li>
<li>Adventure Works 示例数据库
<ul>
<li>Microsoft SQL 2005 下使用: <a href="http://www.codeplex.com/MSFTDBProdSamples/Release/ProjectReleases.aspx?ReleaseId=4004" target="_blank">AdventureWorksLT.msi</a>
<p><b>备注：</b> 该 msi 安装程序只是将示例数据库文件拷贝到你的文件系统中；你还需要手动在SQL SERVER服务器中附加该数据库。更多信息，请参考 <a href="http://technet.microsoft.com/zh-cn/library/ms190209.aspx" target="_blank">如何：在SQL Server Management Studio 中附加数据库</a>.            <br /><i>Vista 用户请注意：</i> 确认将数据库文件安装到 “C:Program Files”以外的目录， 因为该文件夹具有额外的权限设置，可能会由于文件的只读属性造成你无法在工程文件中进行保存。</p>
</li>
<li>Microsoft SQL 2008 下使用: <a href="http://www.codeplex.com/MSFTDBProdSamples/Release/ProjectReleases.aspx?ReleaseId=18407">AdventureWorks 2008 sample databases</a>
<p><b>备注：</b> 该 msi 安装程序只是将示例数据库拷贝到你的文件系统中；不过，你只需要用到 <strong>AdventureWorksLT</strong> 数据库。</p>
<p>&#160;</p>
</li>
</ul>
</li>
</ul>
<h3>安装</h3>
<p>本实验所需的所有必备组件可以用依赖性检测工具来检验。要确定所有项目都已正确配置，请执行以下步骤：</p>
<p>备注：要执行以下安装步骤，你需要在以管理员身份运行的命令提示窗口（cmd.exe）中运行所有脚本。</p>
<ol>
<li>如果以前从没有进行过依赖性检测，请运行依赖性检测工具。要进行检测，运行 CheckDepenencies.cmd 脚本，该脚本位于 <strong>%TrainingKitInstallation%LabsaspNetMvcSetup</strong> 目录。根据向导提示扫描并安装任何缺少的组件（安装后，如果需要可以重新扫描）。
<p>提示：为了方便起见，在这个实验中你将用到的绝大部分代码会安装在Visual Studio 的 代码片段（code snippets）中。CheckDependencies.cmd 结束时会自动加载 Visual Studio 安装管理器来安装代码片段。</p>
</li>
</ol>
<h3>练习</h3>
<p>这个动手实验中包含以下练习：</p>
<ol>
<li>创建 ASP.NET MVC 应用程序 </li>
<li>开发和完善 ASP.NET MVC 应用程序 </li>
<li>测试 ASP.NET MVC 应用程序</li>
</ol>
<p>完成整个实验预计需要时间: <b>90分钟。</b></p>
<p>补充说明:</p>
<p><a href="#" name="_Toc166647553"></a><a href="#" name="_Toc172462715"></a><a href="#" name="_Toc182141329"></a>每个练习中都包含一个起始解决方案，起始解决方案中缺少的代码将在练习工程中完成。因此如果你直接运行起始解决方案，将不能正常执行。    <br />在每个练习中你还可以找到一个 End 目录，其中包括了在整个练习结束时你应该得到的解决方案。在练习过程中如果你需要额外的帮助，可以参考该解决方案。</p>
<p>（文本翻译自 ASP.NET MVC Training Kit，下载地址：<a title="http://www.microsoft.com/downloads/details.aspx?FamilyID=1E0BE0B2-910A-4676-9F3A-41E4D9C0FC08&amp;displaylang=en" href="http://www.microsoft.com/downloads/details.aspx?FamilyID=1E0BE0B2-910A-4676-9F3A-41E4D9C0FC08&amp;displaylang=en">http://www.microsoft.com/downloads/details.aspx?FamilyID=1E0BE0B2-910A-4676-9F3A-41E4D9C0FC08&amp;displaylang=en</a>）</p>
<h3  class="related_post_title">相关日志</h3><ul class="related_post"><li>2009-04-13 -- <a href="http://ofcss.com/2009/04/13/aspnetmvc-hands-on-labs-develop-application.html" title="[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用">[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用</a></li><li>2009-04-09 -- <a href="http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-create-application.html" title="[翻译]ASP.NET MVC动手实验1-2：创建ASP.NET MVC应用">[翻译]ASP.NET MVC动手实验1-2：创建ASP.NET MVC应用</a></li><li>2009-07-08 -- <a href="http://ofcss.com/2009/07/08/asp-net-mvc-best-practices-part-4.html" title="ASP.NET MVC 最佳实践（四）">ASP.NET MVC 最佳实践（四）</a></li><li>2009-07-07 -- <a href="http://ofcss.com/2009/07/07/asp-net-mvc-best-practices-part-3.html" title="ASP.NET MVC 最佳实践（三）">ASP.NET MVC 最佳实践（三）</a></li><li>2009-07-01 -- <a href="http://ofcss.com/2009/07/01/asp-net-mvc-best-practices-part-2.html" title="ASP.NET MVC 最佳实践（二）">ASP.NET MVC 最佳实践（二）</a></li><li>2009-06-28 -- <a href="http://ofcss.com/2009/06/28/asp-net-mvc-best-practices-part-1.html" title="ASP.NET MVC 最佳实践（一）">ASP.NET MVC 最佳实践（一）</a></li><li>2009-04-30 -- <a href="http://ofcss.com/2009/04/30/solution-for-js-intellisense-error.html" title="VS2008 中 JS IntelliSense出错的解决">VS2008 中 JS IntelliSense出错的解决</a></li><li>2011-03-14 -- <a href="http://ofcss.com/2011/03/14/browser-rendering-optimizer-translation-extra.html" title="对《优化浏览器渲染》的补充【译】">对《优化浏览器渲染》的补充【译】</a></li><li>2011-03-10 -- <a href="http://ofcss.com/2011/03/10/css-border-tricks-with-collapsed-boxes-translation.html" title="CSS边框实现&ldquo;无图化&rdquo;设计【译】">CSS边框实现&ldquo;无图化&rdquo;设计【译】</a></li><li>2010-10-31 -- <a href="http://ofcss.com/2010/10/31/prevent-a-float-drop-in-ie6-translation.html" title="CSS技巧:如何避免IE6中的&quot;浮动下坠&quot;【译】">CSS技巧:如何避免IE6中的&quot;浮动下坠&quot;【译】</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://ofcss.com/2009/04/09/aspnetmvc-hands-on-labs-introduction.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>由动易开源说开来</title>
		<link>http://ofcss.com/2007/11/20/thinking-about-powereasy-opensource.html</link>
		<comments>http://ofcss.com/2007/11/20/thinking-about-powereasy-opensource.html#comments</comments>
		<pubDate>Tue, 20 Nov 2007 10:41:00 +0000</pubDate>
		<dc:creator>小李刀刀</dc:creator>
				<category><![CDATA[指手划脚]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[动易]]></category>
		<category><![CDATA[评论]]></category>

		<guid isPermaLink="false">http://wukangrui.net/?p=253</guid>
		<description><![CDATA[<p>以下引自 开源中漫步，与自由同行 ： 骤然回首，已近五年风雨。我们感谢所有一如既往喜爱与使用动易软件的朋友，有你们的支持，动易才能在风雨兼程中逐步发展。时值动易公司成立五周年来临之际，为了动易产品的进一步发展，经过公司的慎密研究与讨论，我们做出重大决定：自2008年1月11日起，动易将按计划逐步开放源代码，建立动易开源社区，同时将进一步加强实施“免费”产品策略！ 动易开源的消息，是我正在上课的时候，晓风吟月通过QQ发短信到我手机上告知的。我第一反应是：哪个版本？？随后的念头是动易开源，会带来多大的影响和变化？这件事情想了很久，联想到了很多很多。 Webboy说“我们要让技术平民化”，是菜鸟站长们把动易推上今天的高位。 关于 ASP.NET 开源 关于动易的开源 关于 ASP、ASP.NET、PHP、JAVA 动易2007版的开发 动易的合作者及竞争者 刀刀的建议 一、Webboy说“我们要让技术平民化”，是菜鸟站长们把动易推上今天的高位。 从动易辞职到现在差不多3个月了，这三个月没学习新技术，没怎么做网站。除了上课，做得最多的事情就是看技术文章（偶尔还翻译几篇），一直在期待动易新系统的正式发布。我在落伍者上看到有人这样评价phpcms：“就差不多是php里的动易，易用、强大……”。还有“动易的服务很好啊。” 相信我认识的很多站长，或者很多认识我的站长，都曾经或者仍然和动易有着不解之缘。这是一个真正让做网站变得再简单不过的系统。phpcms、dedecms、帝国cms、动网旗下的IWMS、康盛科技的SupeSite、风讯、国外的曼波……我用过或者至少试用过上面这些所有的建站系统。说到简单、易用，动易绝对是无与伦比的了。dede和帝国、phpcms的有些用户喜欢说这些系统扩展型强、开源、可以自由扩充……，但是对于任何一个技术一般点的站长，甚至对于有点技术的站长，谁都不可能否认动易的易用性和人性化设计。动易唯一的难度在于模板制作，模板制作之所以让有些人觉得难，是因为标签较多，要足够熟悉才能做好模板；是因为动态标签要熟悉数据结构，熟悉SQL查询&#38;mdash;&#38;mdash;而这些“难”，在其它的系统里哪一个没有？有些用惯了动易采集的站长转到动网IWMS或者SupeSite之后，跑到官方论坛去抱怨采集功能比动易差太多了。其实原理都是一样的，功能也差不多。但是一般人懂正则的有几个呢？或者，在它们过于简单的设置界面下，有多少站长能像用动易采集一样，甚至不需要看教程和说明，光靠后台提示就可以配置好呢？ 就是把大家需要的各种复杂功能“平民化”、“易用化”让动易迅速占据了ASP平台下CMS软件的老大地位。但是不可否认，在用户的平均技术水平方面，我得说动易的用户们恐怕是排在最后的。虽然动易的不开源，让插件编写很难进行，几年来动易系统的优秀插件没出过几个！也因为这个，对“二次开发”非常看重的一些技术型用户往往跑到别的系统阵营去了。但是没有能力进行二次开发的站长数量是如此之多，让离去的这很少的一部分用户显得无关紧要了。在这样一个背景下，动易开不开源，其意义和影响，不会特别大。 如果把dede这样的上手很难的CMS比做JAVA，把IWMS这样虽不难上手但是多年来一直应用得并不算广泛的系统比做ASP.NET，把SupeSite这样依托Discuz广泛用户群的系统比做依托免费开源环境的PHP，那么动易恰似WEB应用行业里的ASP，好用，好学，功能又强，尽管常常被所谓的“高手”们看做是菜鸟的工具。 动易为什么能取得如此成功？公司的盈利和壮大是商业用户的功劳，但我觉得这只是直接的原因。免费用户、菜鸟站长们的忠诚和动易一直以来的理念和遥遥领先的服务水平才是一切的根本。 二、关于 ASP.NET 开源 我们先不谈动易的开源，说说ASP.NET的开源。ASP.NET与ASP、PHP系统有所不同，ASP本身根本没法加密，只能通过完整的VB或者其它编程语言来编写DLL控件保护核心代码，PHP可以通过Zend加密，但是Zend环境一般来说支持PHP的系统都会支持，加密后的文件还是原来那个文件，并不单独生成组件。也就是说，不管加不加密，ASP和PHP，你要是有条件修改的，都只要一个记事本就可以动手。比如网吧，下载个FTP软件立刻就可以开始对站点源码进行修改。那么ASP.NET呢？尽管也可以完全在代码模式下运行，但是一个成熟完整的ASP.NET系统，要想全部是直接可改的ASPX源文件构成，这就有点折磨开发人员了。微软的、第三方的控件大多是编译成DLL的，你只能引用它，因为就算你开源，人家商业控件未必也开源，而要是为了开源放弃了很多优秀的控件，那ASP.NET的“快速开发”这个特性恐怕就无从谈起了，而程序员们绝大部分的时间用来做那些重复劳动都足以耗光了。就算一个控件也不用，新开发的系统本身也需要通过WebApplication和类库、控件等来构建多层结构，组织项目。那么，就算开发方提供了源代码，站长们还是不能拿个记事本随便在哪个网吧就能动手改起网站来。别说开发额外的插件了，有时候就算是做一些细小的改动，都要在本机建立开发调试环境，然后下载源码包（通常就是SDK了），在自己机器上把环境设置好以后，对着开发人员文档分析ASP.NET系统的结构，然后从哪里引用、从哪里改动……弄完之后，再自己编译发布，然后才能正式使用和生效。 我忘了是在哪里看到有人这样说：微软在.NET的泥潭里越陷越深了。这样说似乎有点过了，但是 asp.net 作为“非嵌入式脚本语言”，在修改起来，绝对是没法和asp和php比的，基于dotNet开发的软件，不管多小，要使用都必须要有dotNet Framwork支持，还要注意1.1版还是2.0版。由于上面提到的这种特性，对于绝大部分的站长来说，asp.net 系统的开源，只不过是让你有了“或许有更多人做插件来给我用”的盼头罢了。对于你自己，基本上开不开源没什么区别。高手写了篇教程文章，说在XXX系统的XXXX控件源代码里添加这样一段代码可以实现某某功能，你以为这个小小的修改像在ASP环境下一样，就是打开对应的文件，改几行代码吗？ 早在许多现在开源的PHP系统还没有开源的时候，就有一套著名的基于ASP.NET的 dotText 的博客系统开源免费提供，可是直到今天，使用 dotText 来建立个人博客的还是寥寥无几，偶尔有几个，用的也多是博客园汉化并二次开发的版本。真正用这个系统的，在国内也就是几个大的技术类博客网站&#38;mdash;&#38;mdash;cnblogs、csdn等。比用JSP的商业网站还少。 在 asp.net 2.0 都还没发布的时候，国外就已经有一套开源的ASP.NET全站系统，提供文章、下载、图片、博客、社区、聚合、影视……全套功能，相当完善，非常强大，那就是大名鼎鼎的Community Server，这套系统可以看作是微软的“样板工程”了，把ASP.NET的各种新技术新特性应用得淋漓尽致。多层结构、模板和程序逻辑完全分离……，国内那么多ASP.NET开发人员都研究它、学习它、参考它，那又如何呢？真正能够拿来用的站长还是没有多少。 &#8230; <a href="http://ofcss.com/2007/11/20/thinking-about-powereasy-opensource.html">Continue reading <span class="meta-nav">&#8594;</span></a></p>
]]></description>
			<content:encoded><![CDATA[<p>以下引自 <a href="http://www.powereasy.net/Announce/3308.html">开源中漫步，与自由同行</a> ：</p>
<blockquote><p>骤然回首，已近五年风雨。我们感谢所有一如既往喜爱与使用动易软件的朋友，有你们的支持，动易才能在风雨兼程中逐步发展。时值动易公司成立五周年来临之际，为了动易产品的进一步发展，经过公司的慎密研究与讨论，我们做出重大决定：自2008年1月11日起，动易将按计划逐步开放源代码，建立动易开源社区，同时将进一步加强实施“免费”产品策略！</p></blockquote>
<p>动易开源的消息，是我正在上课的时候，晓风吟月通过QQ发短信到我手机上告知的。我第一反应是：哪个版本？？随后的念头是动易开源，会带来多大的影响和变化？这件事情想了很久，联想到了很多很多。</p>
<ol>
<li><a href="#a">Webboy说“我们要让技术平民化”，是菜鸟站长们把动易推上今天的高位。</a></li>
<li><a href="#b">关于 ASP.NET 开源</a></li>
<li><a href="#c">关于动易的开源</a></li>
<li><a href="#d">关于 ASP、ASP.NET、PHP、JAVA</a></li>
<li><a href="#e">动易2007版的开发</a></li>
<li><a href="#f">动易的合作者及竞争者</a></li>
<li><a href="#g">刀刀的建议</a></li>
</ol>
<h3><a name="a"></a>一、Webboy说“我们要让技术平民化”，是菜鸟站长们把动易推上今天的高位。</h3>
<p>从动易辞职到现在差不多3个月了，这三个月没学习新技术，没怎么做网站。除了上课，做得最多的事情就是看技术文章（偶尔还翻译几篇），一直在期待动易新系统的正式发布。我在落伍者上看到有人这样评价phpcms：“就差不多是php里的动易，易用、强大……”。还有“动易的服务很好啊。”</p>
<p>相信我认识的很多站长，或者很多认识我的站长，都曾经或者仍然和动易有着不解之缘。这是一个真正让做网站变得再简单不过的系统。phpcms、dedecms、帝国cms、动网旗下的IWMS、康盛科技的SupeSite、风讯、国外的曼波……我用过或者至少试用过上面这些所有的建站系统。说到简单、易用，动易绝对是无与伦比的了。dede和帝国、phpcms的有些用户喜欢说这些系统扩展型强、开源、可以自由扩充……，但是对于任何一个技术一般点的站长，甚至对于有点技术的站长，谁都不可能否认动易的易用性和人性化设计。动易唯一的难度在于模板制作，模板制作之所以让有些人觉得难，是因为标签较多，要足够熟悉才能做好模板；是因为动态标签要熟悉数据结构，熟悉SQL查询&amp;mdash;&amp;mdash;而这些“难”，在其它的系统里哪一个没有？有些用惯了动易采集的站长转到动网IWMS或者SupeSite之后，跑到官方论坛去抱怨采集功能比动易差太多了。其实原理都是一样的，功能也差不多。但是一般人懂正则的有几个呢？或者，在它们过于简单的设置界面下，有多少站长能像用动易采集一样，甚至不需要看教程和说明，光靠后台提示就可以配置好呢？</p>
<p>就是把大家需要的各种复杂功能“平民化”、“易用化”让动易迅速占据了ASP平台下CMS软件的老大地位。但是不可否认，在用户的平均技术水平方面，我得说动易的用户们恐怕是排在最后的。虽然动易的不开源，让插件编写很难进行，几年来动易系统的优秀插件没出过几个！也因为这个，对“二次开发”非常看重的一些技术型用户往往跑到别的系统阵营去了。但是没有能力进行二次开发的站长数量是如此之多，让离去的这很少的一部分用户显得无关紧要了。在这样一个背景下，动易开不开源，其意义和影响，不会特别大。</p>
<p>如果把dede这样的上手很难的CMS比做JAVA，把IWMS这样虽不难上手但是多年来一直应用得并不算广泛的系统比做ASP.NET，把SupeSite这样依托Discuz广泛用户群的系统比做依托免费开源环境的PHP，那么动易恰似WEB应用行业里的ASP，好用，好学，功能又强，尽管常常被所谓的“高手”们看做是菜鸟的工具。</p>
<p>动易为什么能取得如此成功？公司的盈利和壮大是商业用户的功劳，但我觉得这只是直接的原因。免费用户、菜鸟站长们的忠诚和动易一直以来的理念和遥遥领先的服务水平才是一切的根本。</p>
<h3><a name="b"></a>二、关于 ASP.NET 开源</h3>
<p>我们先不谈动易的开源，说说ASP.NET的开源。ASP.NET与ASP、PHP系统有所不同，ASP本身根本没法加密，只能通过完整的VB或者其它编程语言来编写DLL控件保护核心代码，PHP可以通过Zend加密，但是Zend环境一般来说支持PHP的系统都会支持，加密后的文件还是原来那个文件，并不单独生成组件。也就是说，不管加不加密，ASP和PHP，你要是有条件修改的，都只要一个记事本就可以动手。比如网吧，下载个FTP软件立刻就可以开始对站点源码进行修改。那么ASP.NET呢？尽管也可以完全在代码模式下运行，但是一个成熟完整的ASP.NET系统，要想全部是直接可改的ASPX源文件构成，这就有点折磨开发人员了。微软的、第三方的控件大多是编译成DLL的，你只能引用它，因为就算你开源，人家商业控件未必也开源，而要是为了开源放弃了很多优秀的控件，那ASP.NET的“快速开发”这个特性恐怕就无从谈起了，而程序员们绝大部分的时间用来做那些重复劳动都足以耗光了。就算一个控件也不用，新开发的系统本身也需要通过WebApplication和类库、控件等来构建多层结构，组织项目。那么，就算开发方提供了源代码，站长们还是不能拿个记事本随便在哪个网吧就能动手改起网站来。别说开发额外的插件了，有时候就算是做一些细小的改动，都要在本机建立开发调试环境，然后下载源码包（通常就是SDK了），在自己机器上把环境设置好以后，对着开发人员文档分析ASP.NET系统的结构，然后从哪里引用、从哪里改动……弄完之后，再自己编译发布，然后才能正式使用和生效。</p>
<p>我忘了是在哪里看到有人这样说：微软在.NET的泥潭里越陷越深了。这样说似乎有点过了，但是 asp.net 作为“非嵌入式脚本语言”，在修改起来，绝对是没法和asp和php比的，基于dotNet开发的软件，不管多小，要使用都必须要有dotNet Framwork支持，还要注意1.1版还是2.0版。由于上面提到的这种特性，对于绝大部分的站长来说，asp.net 系统的开源，只不过是让你有了“或许有更多人做插件来给我用”的盼头罢了。对于你自己，基本上开不开源没什么区别。高手写了篇教程文章，说在XXX系统的XXXX控件源代码里添加这样一段代码可以实现某某功能，你以为这个小小的修改像在ASP环境下一样，就是打开对应的文件，改几行代码吗？</p>
<p>早在许多现在开源的PHP系统还没有开源的时候，就有一套著名的基于ASP.NET的 dotText 的博客系统开源免费提供，可是直到今天，使用 dotText 来建立个人博客的还是寥寥无几，偶尔有几个，用的也多是博客园汉化并二次开发的版本。真正用这个系统的，在国内也就是几个大的技术类博客网站&amp;mdash;&amp;mdash;cnblogs、csdn等。比用JSP的商业网站还少。</p>
<p>在 asp.net 2.0 都还没发布的时候，国外就已经有一套开源的ASP.NET全站系统，提供文章、下载、图片、博客、社区、聚合、影视……全套功能，相当完善，非常强大，那就是大名鼎鼎的Community Server，这套系统可以看作是微软的“样板工程”了，把ASP.NET的各种新技术新特性应用得淋漓尽致。多层结构、模板和程序逻辑完全分离……，国内那么多ASP.NET开发人员都研究它、学习它、参考它，那又如何呢？真正能够拿来用的站长还是没有多少。</p>
<p>所以，ASP.NET 产品是否开源，对于普通用户来说，作用真的不大。如果程序的接口做得好，不需要源代码你也能开发插件，很好地集成，而且还不影响升级。如果需要源代码才能做的修改和开发，那以后该产品更新的时候，每一次你都要重新分析，重新编写，重新编译，重新部署……</p>
<h3><a name="c"></a>三、关于动易的开源</h3>
<p>动易系统的开源，我的观点是要对ASP和ASP.NET两套版本分开来说。对于ASP版本，我觉得开源或许是一个不错的选择，毕竟ASP已经是一个日落西山的东西了，连他爹（微软）都不管他了，自然没什么前途了。动易以后的发展重点既然不是ASP了，把ASP版本开源能够让有能力的动易爱好者充分发挥自己的创意和技术，不断为ASP版本补充进的功能和特性，或许再过四、五年，动易的ASP版本还能在开源社区的集体劳动下，像现在的雷傲论坛一样，继续发挥作用也不一定。</p>
<p>但是对于动易ASP.NET版本的开源，我觉得不管是对动易还是对动易的大多数用户来说，或许意义并不太大。我们不妨拭目以待，到2009年1月18日，经过一年的开源，看能有多少由网友开发的优秀动易插件出现吧。我觉得，对于占绝大多数的动易普通免费用户，一个不开源的动易版本能够提高安全性，在使用上却和开源版本没什么区别；而对于购买服务的商业用户来说，既然肯花钱买服务，当然宁愿要官方的定制开发多过自己另外请技术员做二次开发。而有实力的高端用户，要么人家要自己开发；要么人家去买十几万几十万的高端产品整体部署了。像外交部、CCTV这样有钱又没必要自己开发而且还舍得花钱的那些主儿，动易恐怕很难抢过那些有政府背景的高端产品吧？</p>
<p>当然这只是我对动易两套产品开源以后的影响做的个人分析，动易选择重返开源社区，这绝对是我期待已久而且绝对支持的事情。这是两码事。</p>
<h3><a name="d"></a>四、关于 ASP、ASP.NET、PHP、JAVA</h3>
<p>ASP和PHP都是嵌入到WEB页面中，由解析器来解析执行的，为什么PHP就越来越有前途，而ASP则日落西山了呢？真的是ASP本身已经没法改善，没法提高了吗？这是微软说的。然后大家也都这样说了。因为除了微软没有人可以帮忙改进ASP，ASP是微软的，微软说它不行了，那你啥都别想，赶紧另谋出路就对了。</p>
<p>动网先锋当初作为国内ASP开发的技术先锋，它的ASP版本论坛程序几乎垄断ASP论坛市场，Discuz尽管很牛，在PHP论坛市场的占有率都还没有动网先锋在ASP里这么高。结果微软一放弃ASP，动网一下子黯然失色，沉寂了很长一段时间，最近推出新产品的时候对媒体介绍动网是“一向低调”。匆忙出击的动网又是ASP.NET又是PHP，尽管是匆忙出击，但的确不愧为技术先锋，很快地就三个平台的版本都推出来了。但是毕竟ASP.NET和PHP的众多优秀产品已经在那里拼杀了很久了。动网的ASP.NET版本和PHP版本吸引的，往往是以往动网ASP的拥护者们，打算放弃ASP而又舍不得动网，于是正好成为动网.NET和PHP版本的用户。至于Discuz、phpbb这些成熟的PHP产品的用户，恐怕没多少会转过来吧。动网现在终于又是开源社区、免费平台、商用平台占齐全了。就算ASP彻底灭亡，它也仍然同时拥有高端低端的产品线，上可和JAVA抗衡，下可分享PHP的收获。</p>
<p>康盛科技在PHP领域的位置稳了，Zend中国官方合作伙伴了，财大气粗了，于是开始到ASP.NET这个“看上去很美”的市场上来圈地发展，但是它幸运的是只需要再下点功夫在ASP.NET上就行，这样一来，它已经横跨LAMP(Linux/Unix+Apache+MySQL+PHP)这个免费平台和WIMA(Windows+IIS+MSSQL+ASP.NET)的这个商业平台，ASP.NET可以去和高端的JAVA拼，PHP正好趁着ASP让出市场的机会尽情接收放弃ASP后不愿意或者没办法转到ASP.NET的这些用户。</p>
<p>相比之下，动易现在手里的筹码就要少得多了，ASP版本的已经近乎停止更新，ASP.NET 的还在开发。PHP这个市场不但没有去挤，反倒由于微软的不厚道，失去了一些从ASP转向PHP的用户。所以在这个时候，动易恐怕要挺一阵子了。毕竟是产品真空期。</p>
<p>其实动易的普通用户大可不必着急改换门庭。ASP的2006SP6，你花了那么多时间学习，做模板、完善细节，好不容易做完了技术活儿，现在正是专专心心搞搞网站运作管理、推广宣传的时候，干嘛匆匆忙忙又要急着换版本呢？一个系统稳定下来，你的网站稳定下来，总需要一段时间的稳定期的。以前我做6571.NET的时候，用的动网论坛，动网每次一升级我就郁闷，因为新功能的需求毕竟不是那么急迫，可是论坛来回折腾的话就很难形成细致的管理体系和自己的论坛文化和气氛。我那时候经常是在动网发布补丁或者升级版本挺长时间之后才升级（安全更新除外，反正是开源的，安全更新的话自己改改相应的代码就行了）。</p>
<p>而现在换一套系统的话，你想想当初在动易ASP版本上是花了多少时间精力才熟悉到今天的程度，才达到今天的水平的？这时候去换用别的系统，那些系统任何一套本来就比动易难上手得多，再加上有用惯动易的习惯影响，你的网站上又要发布多久的“本站模板正在改版/完善中……”的公告呢？相信动易论坛上已经有去转了一圈又回来的朋友了吧。</p>
<h3><a name="e"></a>五、动易2007版的开发</h3>
<p>动易2007开发已经一年左右了，至今还在beta2版本，如果再考虑到1.1版本和2.0版本、不同数据库支持这些在目前的公测版都还没出现，不少功能在公测版中还没实现等等问题，估计正式版本肯定还要有相当长的一段时间。所以论坛上就有人开始质疑开发团队的技术了，所谓的“动易的开发人员也才刚刚接触新的开发语言”等。哪有什么新的开发语言？ASP.NET的开发语言&amp;mdash;&amp;mdash;VB、C#、C++、J++这些都是好几年的东西了。只不过在ASP.NET新版本中，在原来基础上增加了新的特性罢了。像动易开发团队的那些老程序员，拿来就用，什么问题都没有。架构开发、公共类、控件这些，都是熟练的再做，新接触的做具体实现部分。简单来说，就是创建具体的功能模块，然后按照统一的规范，结合具体模块的数据库，按照标准格式写一下代码。动易公司今天的开发团队已经不是当初那只团队了，人多了不说，新进来的ASP.NET程序员都比原来的ASP程序员多了。怎么会搞出“才刚刚接触新的开发语言”这样的闹剧呢？</p>
<p>动易2007之所以一再难产，我觉得有两方面的原因。微软的ASP.NET，从作为ASP+推出以来，一直就在不断地完善和发展。我们常常说ASP.NET怎么怎么好，实际上，在现有的WEB开发的众多语言中，可以说ASP.NET是最新、最年轻的一个。它的确吸取了其它语言的很多优秀的特性，但是在它创新的过程中，毕竟还有很多需要完善和丰富的地方。正因为如此，微软的ASP.NET更新实际上是很频繁的。而且每次更新往往都是增加几个新特性，增加新控件，增加新的库……</p>
<p>动易2007版的开发是从2006年末开始的，正是Ajax开始火热流行的时候，微软推出代号为Atlas的用于ASP.NET的开发包，我当时还买了一本刚刚出版的Atlas教材《Atlas基础教程》，收到书的时候，书中作者已经有很多地方说明所写代码在书出版的时候，Atlas已经进行了更新，所以代码又做了更新。然后译者又加“原书中为xxxxxx，在AtlasX月版中，该方法的签名已经改成XXXX……”，然后我按照译者最新订正的代码测试，依然是运行不了，上asp.net网站一查，版本已经又几经变动了。到了2007年1月，干脆Atlas整个项目都取消了，取而代之为ASP.NET AJAX Extentions。就是这样一个不断变化的开发语言，用它来开发，本身就有相当的难度了。</p>
<p>动易公司由于ASP.NET版本采用全新的系统架构，等于是完全重新开发一套系统，功能上参考2006版，但是系统结构、程序逻辑完全都要重新设计，除此之外，还要采用ASP.NET的最新特性。我们最初阶段写的代码中有的地方使用了ASP.NET Ajax Control Kit，具体版本号不记得了。但是没过太久，系统中完全加入了最新作为正式版发布的ASP.NET AJAX Extentions，这样对应的很多代码就失效了，需要重写；除此之外，还有微软企业库（Microsoft Enterprise Library），为动易系统的全新架构提供了一些底层的支持，比如数据工厂、缓存等。我最记得的是当时在cnblogs上有一个专门研究和讲解微软企业库的博客，作者发布系列文章来讲解微软企业库，结果系列教程还没完，微软企业库新版本发布了，又是很多地方向下不兼容。这是两个例子，实际上在2007的开发中，相信动易团队在之后还遇到过更多的此类问题。</p>
<p>或许有人会说，那为什么DiscuzNT这些版本都开发得那么顺利？有用过Discuz！PHP版本和NT版本的朋友肯定都发现了，Discuz的.NET版本最初的时候基本上是完全照搬了PHP版本的界面设计、功能设计等。等于说，数据结构，系统架构是现成的，只要实现编程语言的转换就可以了。这样一个完整的基础版本就很块地出来了。然后开发人员就可以在这个完整的系统基础上进行后续的改进，不管是要改基础的东西，还是附加新功能，都相当于升级版本开发，这就容易多了。而且他们在最初版本的开发中，基本上很少涉及ASP.NET 2.0中一些仍在不断改进的功能。最早的beta版的DiscuzNT，aspx文件的源代码绝对不像现在那样漂亮：）这是我当时几乎用Discuz而又放弃的原因呢。当然了，通过这样的方式，Discuz的ASP.NET版本不仅完成了快速的基础开发，而且很快就赶上了微软的变化，最新的特性一出来他们就可以加进去，加到一定程度就可以作为升级补丁或者新的版本发布了。</p>
<p>通过以上的这些推测，我得出的结论是动易2007版本的开发周期之所以长，是以为这并不是动易2006版的ASP.NET版本，而是一套全新架构的基于ASP.NET、XML、AJAX，系统架构得相当完善的全新全站系统。试想一套全新系统的开发，要是半年一年地就出来正式版了，你敢用吗？</p>
<p>这一点，我个人认为动易开发团队略有点失误了，其实完全可以就在动易2006的架构基础上，只是简单地采用ASP.NET实现出来就OK了，至于改善缓存体系，增加新功能，追踪微软最新技术这些，不妨放到基础版本较为稳定了之后才来做。毕竟升级版的开发要快得多。现在是追求完美，反而不被最终收益的用户理解了，而且还面临市场争夺的危机。</p>
<h3><a name="f"></a>六、动易的合作者及竞争者</h3>
<p>在P、D、O的战略合作之后，现在动易ASP.NET和动网ASP.NET继续保持战略合作。很多网友为此感到欢欣雀跃，因为官方层面的合作，将让以后两套ASP.NET版本的系统整合得更好。但是我个人认为，对于动易来说，与动网在ASP.NET领域合作，或许暂时可以缓解动易的一些危机，但是长远来看却极不可取。如果只是战略合作还好，如果是像以前三方合作时候那样的互不竞争，那就危险了。为什么？因为动网有IWMS，而动易没有论坛。在ASP领域，动网没有发行的CMS管理系统，虽然它的论坛实际上已经集成了内容发布、博客空间这些特征，但是毕竟没有成熟的CMS管理系统。但是在ASP.NET下就不同了，动网有一个非常成熟的IWMS全站系统，而且已经做到可以和动网的系列论坛完美整合了。这样的话，实际上动网现在在ASP市场上持有论坛+博客系统来和动易CMS合作，在ASP.NET领域则持有论坛+博客系统外加一个独立的全站系统来和动易合作。一旦从合作转为竞争，动网立刻就有完整的产品线，动易呢？缺一个论坛模块。虽然我这里用“论坛模块”，但是相信大家都很清楚，一个论坛系统的重要性足可以和一套CMS相提并论了。因为任何一个用CMS维护的站点都不会想缺少论坛。但是建立论坛未必要建立一套文章、下载、图片等等组成的全站系列。</p>
<p>我们再看另一个重量级的竞争对手：康盛科技。 康盛科技现在在PHP领域可以说是万事不求人，SupeSite全站系统+X-Space多人博客+Ecshop网店系统+Discuz论坛（通过5d6d系统的展示说明康盛科技的Discuz已经可以用于提供成熟的多用户论坛服务），而在ASP.NET这边，暂时还只有一个DiscuzNT论坛系统，但是这个论坛系统却已经包含了太多的Web2.0元素，个人博客空间、圈子、C2C电子商务……，这个DiscuzNT像变形金刚一样摇身一变，一个SupeSite/X-SpaceNT系统就可以出来。何况谁也不知道康盛科技有没有暗地里在开发配套的CMS系统。就算没有，按照康盛现在的表现，找一个差不多的ASP.NET全站系统收购过来也未必不可能。</p>
<p>所以在这种情况下，动易又是产品线最不完整的，如果再由于战略合作限制了自己的手脚，恐怕更为不利。当然动易也可以在广州研发中心另组几个团队，在保密的情况下同步开发论坛及其它平台的产品，然后在明年1月18日开源的时候，几套产品间隔几天相继发布，充分展示自己的实力，如果能够来这么一手的话，相信效果不仅仅是完善了产品线这么简单。至少同时还展示出动易多线开发的实力。</p>
<h3><a name="g"></a>七、刀刀的建议</h3>
<p>面对当前表面上看起来危机重重的局势，我不知道动易高层是否胸有成竹，早有打算。作为一个动易的学生、曾经的员工和永远的朋友，提出一点不成熟的建议，要是能对动易公司略有帮助固然好，即使没有，相信也没坏处。</p>
<p>以动易开发团队现在的实力，相信有实力马上组建一只3-5人的PHP开发团队，不需要另起炉灶重新设计，就在动易2006版的架构基础上，尽快地推出动易的PHP版本，目前PHP市场上的全站系统，还没有哪一套像动易这样易学易用的，只要能够完全地实现动易2006版的功能（其实可以收集用户建议，去掉一些无意义的部分），相信在留住对动易2007新版本信心有所动摇的用户之外，还能争取到一批新的客户；</p>
<p>加紧完善动易2007.NET版本的CMS基本功能，争取早上市，开源的相关修改和完善可以从ASP版开始，ASP.NET版本的什么时候能实现就什么时候再实现，先上市才是关键。动易2007.NET早期版本关键是实现和完善CMS相关功能，至于商城、Web2.0元素这些可以缓行，不要为这些拖累整个开发进度。动易的用户里面有20%需要商城或者网店吗？恐怕如果达到这个比例已经相当多了。以后或许很多个人站长用这个功能来为本地的商家提供网上商城服务，市场不可谓不广，但是首先要抱住了数量巨大的免费用户，占有了市场，赢得了口碑才可能赢来更多的商业用户（卖的不是服务吗？动易的服务口碑现在那么好，保住这个才是根本。）</p>
<p>如果要合作，不妨联合现在所有开源的WEB系统服务商，搞一个大规模的开源合作计划，这种合作不一定要是在技术上互补的，或者是双方要在程序上互相做多少支持的。完全可以更广泛，比如说行业性的合作啊，搞个什么联盟啊。甚至于，像动易、动网、康盛科技、Dede这些较有影响的WEB程序提供商，再联合一些技术社区，一些企业，一起来创建一个开源社区，或者像微软的 源码实验室， 或者 sourceforge 这类的支持，或者对国内开源WEB软件的一些开发制定些规范啦。总而言之一个宗旨：摊子多大都行，噱头是什么都行，但是就是不要束缚自己的手脚。</p>
<h3  class="related_post_title">相关日志</h3><ul class="related_post"><li>2006-06-04 -- <a href="http://ofcss.com/2006/06/04/climb-langya-mountain.html" title="狼牙山">狼牙山</a></li><li>2009-07-08 -- <a href="http://ofcss.com/2009/07/08/asp-net-mvc-best-practices-part-4.html" title="ASP.NET MVC 最佳实践（四）">ASP.NET MVC 最佳实践（四）</a></li><li>2009-07-07 -- <a href="http://ofcss.com/2009/07/07/asp-net-mvc-best-practices-part-3.html" title="ASP.NET MVC 最佳实践（三）">ASP.NET MVC 最佳实践（三）</a></li><li>2009-07-01 -- <a href="http://ofcss.com/2009/07/01/asp-net-mvc-best-practices-part-2.html" title="ASP.NET MVC 最佳实践（二）">ASP.NET MVC 最佳实践（二）</a></li><li>2009-06-28 -- <a href="http://ofcss.com/2009/06/28/asp-net-mvc-best-practices-part-1.html" title="ASP.NET MVC 最佳实践（一）">ASP.NET MVC 最佳实践（一）</a></li><li>2009-06-17 -- <a href="http://ofcss.com/2009/06/17/comment-to-movie-terminato-salvation.html" title="终结者2018 &#8211; 故事未完">终结者2018 &#8211; 故事未完</a></li><li>2009-06-04 -- <a href="http://ofcss.com/2009/06/04/china-internet-law-in-wrong-way.html" title="中国互联网管理需要清醒一下找找方向">中国互联网管理需要清醒一下找找方向</a></li><li>2009-05-01 -- <a href="http://ofcss.com/2009/05/01/comment-to-movie-nanjing.html" title="南京！南京">南京！南京</a></li><li>2009-04-30 -- <a href="http://ofcss.com/2009/04/30/solution-for-js-intellisense-error.html" title="VS2008 中 JS IntelliSense出错的解决">VS2008 中 JS IntelliSense出错的解决</a></li><li>2009-04-13 -- <a href="http://ofcss.com/2009/04/13/aspnetmvc-hands-on-labs-develop-application.html" title="[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用">[翻译]ASP.NET MVC动手实验1-3：开发ASP.NET MVC应用</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://ofcss.com/2007/11/20/thinking-about-powereasy-opensource.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

