﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-KiMoGiGi 技术文集-随笔分类-ASP.NET</title><link>http://www.cnitblog.com/seeyeah/category/3168.html</link><description>不在乎选择什么，而在乎坚持多久……</description><language>zh-cn</language><lastBuildDate>Thu, 29 Sep 2011 19:53:29 GMT</lastBuildDate><pubDate>Thu, 29 Sep 2011 19:53:29 GMT</pubDate><ttl>60</ttl><item><title>微软反跨站脚本库3.0 RTM</title><link>http://www.cnitblog.com/seeyeah/archive/2009/09/20/61497.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Sun, 20 Sep 2009 14:29:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2009/09/20/61497.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/61497.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2009/09/20/61497.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/61497.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/61497.html</trackback:ping><description><![CDATA[摘至：http://www.cnblogs.com/shanyou/archive/2009/07/16/1524515.html<br><br>
<p>跨站脚本Cross-Site Scripting（XSS）又叫CSS (Cross Site Script)
，跨站脚本攻击。它指的是恶意攻击者往Web页面里插入恶意html代码，当用户浏览该页之时，嵌入其中Web里面的html代码会被执行，从而达到恶意
用户的特殊目的。XSS属于被动式的攻击，因为其被动且不好利用，所以许多人常呼略其危害性。<br>跨站脚本Cross-Site Scripting（XSS）是最为流行的Web安全漏洞之一。据统计，2007年，跨站脚本类的安全漏洞的数目已经远远超出传统类型的安全漏洞(<a title="http://en.wikipedia.org/wiki/Cross-site_scripting" href="http://en.wikipedia.org/wiki/Cross-site_scripting">http://en.wikipedia.org/wiki/Cross-site_scripting</a>)</p>
<p>虽然在IE8中引入了客户端的XSS过滤器以减少XSS对用户造成的危害，但是XSS本质上是Web应用服务的漏洞，仅仅依赖客户端的保护措施是不够的。解决问题的根本是在Web应用程序的代码中消除XSS安全漏洞。</p>
<p>以下是在Web应用的开发中避免XSS安全漏洞的几个原则：</p>
<ul>
    <li>检查所有产生动态网页的代码</li>
    <li>判定动态网页的内容是否包括不安全的输入信息</li>
    <li>对输入进行校验</li>
    <li>对输出进行编码以过滤特殊字符 </li>
</ul>
<p>采用不同的Web开发工具，实施以上原则的具体步骤也不相同。当需要将一个字符串输出到Web网页时，但又不能完全确定这个字符串是否包括HTML
的特殊字符，例如&#8220;&lt;,&gt;,&amp;&#8221;等等，可以使用编码（HTMLEncode）以过滤这些特殊字符。在ASP.NET中有两种方法：一
种是使用HttpUtility，另一种就是使用微软提供的XSS库，最新版本是3.0
,采用MS-PL协议发布的开源项目，7月14日发布了，下载地址是：<a title="http://www.microsoft.com/downloads/details.aspx?FamilyId=051ee83c-5ccf-48ed-8463-02f56a6bfc09&amp;displaylang=en" href="http://www.microsoft.com/downloads/details.aspx?FamilyId=051ee83c-5ccf-48ed-8463-02f56a6bfc09&amp;displaylang=en">http://www.microsoft.com/downloads/details.aspx?FamilyId=051ee83c-5ccf-48ed-8463-02f56a6bfc09&amp;displaylang=en</a>。</p>
<p>AntiXss的使用方式与HttpUtility类似：</p>
<p>String Name = AntiXss.HtmlEncode(Request.QueryString["Name"]);</p>
<p>它们最大的区别在于HttpUtility.HtmlEncode采用的是黑名单验证（Black
list）方式。即HttpUtility.HtmlEncode仅仅过滤它知道的特殊字符，而允许其它的输入。AntiXss.HtmlEncode采
用的白名单验证（White list）方式。它只允许输出它认为合法的字符，而过滤掉其它的所有字符。</p>
<p>两者中，AntiXss.HtmlEncode要更为安全，是推荐的使用手段。</p>
<p>在asp.net 程序中避免 Cross-Site Scripting 攻击的正确方法：</p>
<p>(1) ValidateRequest = true</p>
<p>(2) 对于所有使用者的输入加以<strong> 编码</strong> 并检查长度 ： Application、Session、Url QueryString、Cookie、HTTP Header、数据库、文件、Form表单(根据输出的区域，使用以下相对应的七种编码方法)</p>
<p>&nbsp;</p>
<p>XSS Libray 包含如下的方法：</p>
<table border="0" cellpadding="2" cellspacing="0" width="987">
    <tbody>
        <tr>
            <td valign="top" width="200"><strong> Encoding Method</strong> </td>
            <td valign="top" width="785"><strong> Description</strong> </td>
        </tr>
        <tr>
            <td valign="top" width="200">HtmlEncode</td>
            <td valign="top" width="785">Encodes input strings for use in HTML </td>
        </tr>
        <tr>
            <td valign="top" width="200">HtmlAttributeEncode </td>
            <td valign="top" width="785">Encodes input strings for use in HTML attributes </td>
        </tr>
        <tr>
            <td valign="top" width="200">JavaScriptEncode </td>
            <td valign="top" width="785">Encodes input strings for use in JavaScript </td>
        </tr>
        <tr>
            <td valign="top" width="200">UrlEncode </td>
            <td valign="top" width="785">Encodes input strings for use in Universal Resource Locators (URLs)</td>
        </tr>
        <tr>
            <td valign="top" width="200">VisualBasicScriptEncode </td>
            <td valign="top" width="785">Encodes input strings for use in Visual Basic Script </td>
        </tr>
        <tr>
            <td valign="top" width="200">XmlEncode </td>
            <td valign="top" width="785">Encodes input strings for use in XML </td>
        </tr>
        <tr>
            <td valign="top" width="200">XmlAttributeEncode </td>
            <td valign="top" width="785">Encodes input strings for use in XML attributes </td>
        </tr>
    </tbody>
</table>
<p>具体的使用方法及示例，请参考MSDN： <a href="http://msdn.microsoft.com/en-us/library/aa973813.aspx">Microsoft Anti-Cross Site Scripting Library V1.5: Protecting the Contoso Bookmark Page</a>&nbsp;</p>
<p>AntiXSS Library v3.0 除了保留了老版本的一些静态的Encode工具方法(重新实现),另外最重要的就是新增了<br>AntiXSS HttpModule 用于统一 Encode 输出ASP.Net Server Web Control 为encode 输出的相关属性,如:Text属性等<br>原理大概是《利用 HttpModule,基于输出,统一控制、干预、处理(例如: 过滤关键字、AntiXSS) ASP.Net WebForm Control 展现属性的方案原型》<br><a href="http://www.cnblogs.com/Microshaoft/archive/2009/01/08/1371475.html" target="_blank"><font color="#f03331">http://www.cnblogs.com/Microshaoft/archive/2009/01/08/1371475.html</font></a><br><br></p>
<p>微软反跨站脚本库主页：<a title="http://msdn.microsoft.com/en-us/security/aa973814.aspx" href="http://msdn.microsoft.com/en-us/security/aa973814.aspx">http://msdn.microsoft.com/en-us/security/aa973814.aspx</a><br>XSS（跨站）攻击全解析: <a href="http://www4.it168.com/jtzt/shenlan/safe/xss/">http://www4.it168.com/jtzt/shenlan/safe/xss/</a><br>CodePlex站点: <a href="http://antixss.codeplex.com/">http://antixss.codeplex.com/</a></p>
<br>作者: <a href="http://shanyou.cnblogs.com/" target="_blank">自由、创新、研究、探索&#8230;&#8230;</a><br>
出处：<a href="http://shanyou.cnblogs.com/" target="_blank">http://shanyou.cnblogs.com/</a><br>
版权：本文版权归作者和博客园共有<br>
转载：欢迎转载，为了保存作者的创作热情，请按要求【转载】，谢谢<br>
要求：未经作者同意，必须保留此段声明；必须在文章中给出原文连接；否则必究法律责任 <br>
个人网站: <a href="http://www.openbeta.cn/">http://www.openbeta.cn/</a>
<br><br><img src ="http://www.cnitblog.com/seeyeah/aggbug/61497.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2009-09-20 22:29 <a href="http://www.cnitblog.com/seeyeah/archive/2009/09/20/61497.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对ashx请求用Gzip,Deflated压缩</title><link>http://www.cnitblog.com/seeyeah/archive/2009/09/15/61405.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Tue, 15 Sep 2009 01:06:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2009/09/15/61405.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/61405.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2009/09/15/61405.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/61405.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/61405.html</trackback:ping><description><![CDATA[摘至：<a id="ctl04_TitleUrl" class="postTitle2" href="http://www.cnblogs.com/liuju150/archive/2009/09/14/1566479.html">对ashx请求用Gzip,Deflated压缩</a><br /><br />//GZIP压缩<br /><br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">查看请求头部</span><span style="color: rgb(0, 128, 0);"><br /></span><span style="color: rgb(0, 0, 255);">string</span><span style="color: rgb(0, 0, 0);"> acceptEncoding </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> context.Request.Headers[</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Accept-Encoding</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">].ToString().ToUpperInvariant();<br /></span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (</span><span style="color: rgb(0, 0, 0);">!</span><span style="color: rgb(0, 0, 0);">String.IsNullOrEmpty(acceptEncoding))<br />{<br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">如果头部里有包含"GZIP”,"DEFLATE",表示你浏览器支持GZIP,DEFLATE压缩</span><span style="color: rgb(0, 128, 0);"><br /></span><span style="color: rgb(0, 0, 0);">    </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (acceptEncoding.Contains(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">GZIP</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">))<br />    {<br />        </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">向输出流头部添加压缩信息</span><span style="color: rgb(0, 128, 0);"><br /></span><span style="color: rgb(0, 0, 0);">        context.Response.AppendHeader(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Content-encoding</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">gzip</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);<br />        context.Response.Filter </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> GZipStream(context.Response.Filter, CompressionMode.Compress);<br />    }<br />    </span><span style="color: rgb(0, 0, 255);">else</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);"> (acceptEncoding.Contains(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">DEFLATE</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">))<br />    {<br />        </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">向输出流头部添加压缩信息</span><span style="color: rgb(0, 128, 0);"><br /></span><span style="color: rgb(0, 0, 0);">        context.Response.AppendHeader(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">Content-encoding</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">, </span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">deflate</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);<br />        context.Response.Filter </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);"> DeflateStream(context.Response.Filter, CompressionMode.Compress);<br />    }<br />}</span></div><br /><p>这样每次context.Response.Write出支的数据就压缩了</p><p>对效多的文本信息压缩可以压缩到原来三分之一到四分之一的样子</p><p>如果发送的信息只有几个字节就没有必要了</p><br /><br /><br /><img src ="http://www.cnitblog.com/seeyeah/aggbug/61405.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2009-09-15 09:06 <a href="http://www.cnitblog.com/seeyeah/archive/2009/09/15/61405.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>有关客户端浏览器缓存的Http头介绍</title><link>http://www.cnitblog.com/seeyeah/archive/2009/07/10/59991.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Fri, 10 Jul 2009 03:08:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2009/07/10/59991.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/59991.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2009/07/10/59991.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/59991.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/59991.html</trackback:ping><description><![CDATA[
		<p>做网站开发离不开缓存，缓存分好多种：服务器缓存，第三方缓存，浏览器缓存等。其中浏览器缓存是代价最小的，因为浏览器缓存依赖的是客户端，而几乎不耗费服务器端的资源。</p>
		<p>让浏览器做缓存需要给浏览器发送指定的Http头，告诉浏览器缓存多长时间，或者坚决不要缓存。作为.net的程序员，其实我们一直都在用这种方法，在OutputCache指令中指定缓存的Location为Client时，其实就是给浏览器发送了一个Http头，告诉浏览器这个Url要缓存多长时间，最后修改的时间。</p>
		<p>微软在OutputCacheModule中对这些缓存用到的Http头给我们进行了很好的封装，但是了解这些Http头可以更灵活的使用它们。</p>
		<p>和客户端缓存相关的Http头有以下几个，分别是：<br /><strong>1. Expires:+过期时间</strong><br />表示在指定时间后浏览器缓存失效，需要注意的是这儿的过期时间必须是HTTP格式的日期时间，其他的都会被解析成当前时间“之前”，缓存会马上过期，HTTP的日期时间必须是格林威治时间（GMT），而不是本地时间。举例： <br />Expires: Fri, 30 Oct 2009 14:19:41</p>
		<p>使用Expires过期必须要求服务器的时间是正确的，否则发送的http头就会出问题，在windows服务下可以设置时间服务器来同步时间<br /><strong>2. Cache-control: <br /></strong>Cache-control直译成中文就是缓存控制，它的作用就是缓存控制，这个http头的值有几种。<br />1) max-age=[秒] — 执行缓存被认为是最新的最长时间。类似于过期时间，这个参数是基于请求时间的相对时间间隔，而不是绝对过期时间，[秒]是一个数字，单位是秒：从请求时间开始到过期时间之间的秒数。 <br />2) s-maxage=[秒] — 类似于max-age属性，除了他应用于共享（如：代理服务器）缓存 <br />3) public — 标记认证内容也可以被缓存，一般来说： 经过HTTP认证才能访问的内容，输出是自动不可以缓存的； <br />4) no-cache — 强制每次请求直接发送给源服务器，而不经过本地缓存版本的校验。这对于需要确认认证应用很有用（可以和public结合使用），或者严格要求使用最新数据的应用（不惜牺牲使用缓存的所有好处）； <br />5) no-store — 强制缓存在任何情况下都不要保留任何副本 <br />6) must-revalidate — 告诉缓存必须遵循所有你给予副本的新鲜度的，HTTP允许缓存在某些特定情况下返回过期数据，指定了这个属性，你高速缓存，你希望严格的遵循你的规则。 <br />7) proxy-revalidate — 和 must-revalidate类似，除了他只对缓存代理服务器起作用 <br />举例:<br />Cache-Control: max-age=3600, must-revalidate </p>
		<p>很显然Cache-control可以提供比Expires更灵活的缓存控制，而且它不需要依赖于服务器时间。<br />在Asp.Net中微软把对Cache-control属性的设置封装到了HttpCachePolicy类中，我们可以通过Response.Cache来调用以下方法来做到对Cache-Control Http头值的控制：<br />Response.CacheControl;<br />Response.Cache.SetNoStore<br />Response.Cache.SetMaxAge<br />Response.Cache.SetProxyMaxAge<br />Response.Cache.SetRevalidation<br />            <br /><strong>3. Last-Modified/If-Modified-Since <br /></strong>这两个Http头是一对，前者表示某个地址的最近更新时间，是服务器端响应给客户端的；而后者是客户端浏览器发送给服务器的，告诉web服务器客户端有一个最后更改时间为什么时间的缓存，服务器端接收到If-Modified-Since头后则判断客户端缓存的这份url地址的缓存是否是最新的，如果是最新的则服务器端直接给客户端返回HttpStatus 304，意思是说这个内容在你上次请求之后没有变化过，你直接用缓存就可以了；如果服务器发现url的最后更新时间比If-Modified-Since 的值要新，则会输出新的内容。</p>
		<p>同样微软也为我们做了服务器端设置的封装，我们可以这样调用<br />Response.Cache.SetLastModified(DateTime)<br />Response.Cache.SetLastModifiedFromFileDependencies()</p>
		<p>如果有更复杂的需求就需要自己处理了。</p>
		<p>
				<strong>4. ETag/If-None-Match</strong>
				<br />ETag和Last-Modified类似，不过他发送的是一个字符串来标示url的版本，如果url变了则此标示也跟着变化，在浏览器发送If-None-Match时告诉浏览器内容已经变了，或者没变可以使用缓存。</p>
		<p>Iis会自动给静态文件加上Etag，在文件发生改变时重新生成一个Etag，这样对于一个网站中的n多个静态文件如：样式表，小图片等，客户端只下载一次就够了，可以减轻负载。</p>
		<p>在Asp.Net中我们可以用以下两个方法来设置<br />Response.Cache.SetETag(string)<br />Response.Cache.SetETagFromFileDependencies()</p>
		<p>尽管微软为我们做了很多封装，但是我们还是需要详细的了解之后才可以用好这几个Http头。</p>
		<div id="MySignature">
				<hr color="#f0f0f0" noshade="" size="1" />
				<p>请尊重作者的劳动，转载请保留链接 <a href="http://www.cnblogs.com/yukaizhao/">玉开的技术博客</a>   </p>
		</div>
<img src ="http://www.cnitblog.com/seeyeah/aggbug/59991.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2009-07-10 11:08 <a href="http://www.cnitblog.com/seeyeah/archive/2009/07/10/59991.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ASP.net 页面被关闭后，服务器端是否仍然执行中？ </title><link>http://www.cnitblog.com/seeyeah/archive/2008/07/28/47218.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Mon, 28 Jul 2008 15:37:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2008/07/28/47218.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/47218.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2008/07/28/47218.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/47218.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/47218.html</trackback:ping><description><![CDATA[摘至：<a id=ctl00_MainContent_ViewPost1_TitleUrl title="Title of this entry." href="http://blog.joycode.com/ghj/archive/2008/07/23/115199.aspx"><u><font color=#800080>ASP.net 页面被关闭后，服务器端是否仍然执行中？</font></u></a> <br>
<hr>
<p>问题：当一个正在执行中的ASPX页面执行到一半的时候，浏览器中你关闭了这个页面，服务器端对应的这个页面的代码仍然在执行么？</p>
<p>答案：除非你代码里面做了特殊判断，否则仍然正在执行。</p>
<p>&nbsp;</p>
<p><strong>注意点：</strong></p>
<p>1、客户端显示页面的时候，后台已经执行完了的页面对象早已经不存在了。当然这时候谈不上服务器段执行不执行的问题了。</p>
<p>2、页面还没有返回，处于等待状态的时候。关闭ASPX页面，才会涉及到上面提到的服务器端仍然在执行的情况。</p>
<p>3、客户端关闭的时候根本不向服务器发送指令。</p>
<p>4、除非你代码里面做了特殊判断，这里的特殊判断指用 if(!Response.IsClientConnected) 来检测状态而用代码终止运行。</p>
<p>下面的简单代码就是演示关闭页面后，看是否仍然在执行？</p>
<p>你可以在这个页面打开后， 还没有返回任何信息的时候把这个页面关闭，然后看指定目录下是否有对应文件被创建并填写内容。</p>
<pre>        <span style="COLOR: #0000ff">protected</span> <span style="COLOR: #0000ff">void</span> Page_Load(<span style="COLOR: #0000ff">object</span> sender, EventArgs e)
{
StringBuilder txt = <span style="COLOR: #0000ff">new</span> StringBuilder();
txt.AppendLine();
txt.AppendLine(DateTime.Now.ToString("<span style="COLOR: #8b0000">u</span>"));
txt.AppendLine("<span style="COLOR: #8b0000">asvd</span>");
Response.Write(DateTime.Now.ToString("<span style="COLOR: #8b0000">u</span>"));
Response.Write("<span style="COLOR: #8b0000">&lt;br /&gt;\r\n</span>");
Thread.Sleep(50000);
txt.AppendLine(DateTime.Now.ToString("<span style="COLOR: #8b0000">u</span>"));
Response.Write(DateTime.Now.ToString("<span style="COLOR: #8b0000">u</span>"));
Response.Write("<span style="COLOR: #8b0000">&lt;br /&gt;\r\n</span>");
<span style="COLOR: #008000">// 把一些信息写到另外一个文件,借此察看是否正在运行</span>
<span style="COLOR: #0000ff">string</span> dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "<span style="COLOR: #8b0000">logs</span>");
<span style="COLOR: #0000ff">if</span> (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
DateTime dt = DateTime.Now;
<span style="COLOR: #0000ff">string</span> shortfileName = <span style="COLOR: #0000ff">string</span>.Format("<span style="COLOR: #8b0000">errors_{0:0000}{1:00}{2:00}.log</span>", dt.Year, dt.Month, dt.Day);
<span style="COLOR: #0000ff">string</span> fileName = Path.Combine(dir, shortfileName);
StreamWriter sw;
<span style="COLOR: #0000ff">if</span> (File.Exists(fileName))
sw = File.AppendText(fileName);
<span style="COLOR: #0000ff">else</span>
sw = File.CreateText(fileName);
sw.Write(txt.ToString());
sw.Close();
sw = <span style="COLOR: #0000ff">null</span>;
}</pre>
<p>&nbsp;</p>
<p>作了特殊判断的情况简单例子：</p>
<p>注意： IsClientConnected 的判断在 VS.net 开发工具自带的开发站点 ASP.NET Development Server&nbsp; 是不支持的。 ASP.NET Development Server 永远返回 true 。</p>
<p>IIS 才是支持的。</p>
<pre>        <span style="COLOR: #0000ff">protected</span> <span style="COLOR: #0000ff">void</span> Page_Load(<span style="COLOR: #0000ff">object</span> sender, EventArgs e)
{
StringBuilder txt = <span style="COLOR: #0000ff">new</span> StringBuilder();
<span style="COLOR: #0000ff">for</span> (<span style="COLOR: #0000ff">int</span> i = 0; i &lt; 100; i++)
{
<span style="COLOR: #0000ff">if</span> (<span style="COLOR: #0000ff">this</span>.Response.IsClientConnected)
{
txt.AppendLine();
txt.AppendLine(DateTime.Now.ToString("<span style="COLOR: #8b0000">u</span>"));
txt.AppendLine(i.ToString());
Response.Write(DateTime.Now.ToString("<span style="COLOR: #8b0000">u</span>"));
Response.Write("<span style="COLOR: #8b0000">&lt;br /&gt;\r\n</span>");
Thread.Sleep(500);
}
<span style="COLOR: #0000ff">else</span>
{
Response.End();
<span style="COLOR: #0000ff">return</span>;
}
}
txt.AppendLine(DateTime.Now.ToString("<span style="COLOR: #8b0000">u</span>"));
Response.Write(DateTime.Now.ToString("<span style="COLOR: #8b0000">u</span>"));
Response.Write("<span style="COLOR: #8b0000">&lt;br /&gt;\r\n</span>");
<span style="COLOR: #008000">// 把一些信息写到另外一个文件,借此察看是否正在运行</span>
<span style="COLOR: #0000ff">string</span> dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "<span style="COLOR: #8b0000">logs</span>");
<span style="COLOR: #0000ff">if</span> (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
DateTime dt = DateTime.Now;
<span style="COLOR: #0000ff">string</span> shortfileName = <span style="COLOR: #0000ff">string</span>.Format("<span style="COLOR: #8b0000">errors_{0:0000}{1:00}{2:00}.log</span>", dt.Year, dt.Month, dt.Day);
<span style="COLOR: #0000ff">string</span> fileName = Path.Combine(dir, shortfileName);
StreamWriter sw;
<span style="COLOR: #0000ff">if</span> (File.Exists(fileName))
sw = File.AppendText(fileName);
<span style="COLOR: #0000ff">else</span>
sw = File.CreateText(fileName);
sw.Write(txt.ToString());
sw.Close();
sw = <span style="COLOR: #0000ff">null</span>;
}</pre>
<p>这个例子中是发现中断，就抛弃之前做的任何东西。</p>
<p>当然我们也可以简单的修改上述代码，让把已经处理完成的东西记录下来，类似下面的代码</p>
<pre>        <span style="COLOR: #0000ff">protected</span> <span style="COLOR: #0000ff">void</span> Page_Load(<span style="COLOR: #0000ff">object</span> sender, EventArgs e)
{
StringBuilder txt = <span style="COLOR: #0000ff">new</span> StringBuilder();
<span style="COLOR: #0000ff">for</span> (<span style="COLOR: #0000ff">int</span> i = 0; i &lt; 100; i++)
{
<span style="COLOR: #0000ff">if</span> (<span style="COLOR: #0000ff">this</span>.Response.IsClientConnected)
{
txt.AppendLine();
txt.AppendLine(DateTime.Now.ToString("<span style="COLOR: #8b0000">u</span>"));
txt.Append("<span style="COLOR: #8b0000">**********  </span>");
txt.AppendLine(i.ToString());
Response.Write(DateTime.Now.ToString("<span style="COLOR: #8b0000">u</span>"));
Response.Write("<span style="COLOR: #8b0000">&lt;br /&gt;\r\n</span>");
Thread.Sleep(500);
}
<span style="COLOR: #0000ff">else</span>
{
<span style="COLOR: #0000ff">break</span>;
}
}
txt.AppendLine(DateTime.Now.ToString("<span style="COLOR: #8b0000">u</span>"));
Response.Write(DateTime.Now.ToString("<span style="COLOR: #8b0000">u</span>"));
Response.Write("<span style="COLOR: #8b0000">&lt;br /&gt;\r\n</span>");
<span style="COLOR: #008000">// 把一些信息写到另外一个文件,借此察看是否正在运行</span>
<span style="COLOR: #0000ff">string</span> dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "<span style="COLOR: #8b0000">logs</span>");
<span style="COLOR: #0000ff">if</span> (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
DateTime dt = DateTime.Now;
<span style="COLOR: #0000ff">string</span> shortfileName = <span style="COLOR: #0000ff">string</span>.Format("<span style="COLOR: #8b0000">errors_{0:0000}{1:00}{2:00}.log</span>", dt.Year, dt.Month, dt.Day);
<span style="COLOR: #0000ff">string</span> fileName = Path.Combine(dir, shortfileName);
StreamWriter sw;
<span style="COLOR: #0000ff">if</span> (File.Exists(fileName))
sw = File.AppendText(fileName);
<span style="COLOR: #0000ff">else</span>
sw = File.CreateText(fileName);
sw.Write(txt.ToString());
sw.Close();
sw = <span style="COLOR: #0000ff">null</span>;
}</pre>
<p>需要注意的是， 使用 isClientConnected&nbsp;&nbsp; 是要占用一定的系统资源的。 </p>
<p>isClientConnected&nbsp;&nbsp; 实际上需要向客户端输出一点东西，然后才知道客户端是否仍然在线。</p>
<p>这样，除非你的应用非常耗时，否则建议你不要用 isClientConnected&nbsp;&nbsp; 。 免得判断 isClientConnected&nbsp;&nbsp; 使用的资源比你实际业务逻辑使用的资源还要多。</p>
<p>在任何情况下， Response.IsClientConnected 都要有些开销，所以，只有在执行至少要用 500 毫秒（如果想维持每秒几十页的吞吐量，这是一个很长的时间了）的操作前才使用它。作为通常的规则，不要在紧密循环的每次迭代中调用它，例如当绘制表中的行，可能每&nbsp; 20 行或每 50 行调用一次。 </p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong>参考资料：</strong></p>
<p>how to increase timeout on aspx page? <br><a title=http://p2p.wrox.com/topic.asp?TOPIC_ID=7504 href="http://p2p.wrox.com/topic.asp?TOPIC_ID=7504"><u><font color=#0000ff>http://p2p.wrox.com/topic.asp?TOPIC_ID=7504</font></u></a></p>
<p>asp.net能不能在客户端关闭后在后台继续将页面执行完毕？ <br><a title=http://topic.csdn.net/u/20070202/14/85ea5576-907a-4960-9c53-b206a05228e4.html href="http://topic.csdn.net/u/20070202/14/85ea5576-907a-4960-9c53-b206a05228e4.html"><u><font color=#0000ff>http://topic.csdn.net/u/20070202/14/85ea5576-907a-4960-9c53-b206a05228e4.html</font></u></a></p>
<p>在 ASP.NET 中使用计时器（Timer） <br><a title=http://blog.joycode.com/percyboy/articles/3595.aspx href="http://blog.joycode.com/percyboy/articles/3595.aspx"><u><font color=#0000ff>http://blog.joycode.com/percyboy/articles/3595.aspx</font></u></a></p>
<p>ASP.NET 2.0 中的异步页 <br><a title=http://www.microsoft.com/china/msdn/library/webservices/asp.net/issuesWickedCodetoc.mspx?mfr=true href="http://www.microsoft.com/china/msdn/library/webservices/asp.net/issuesWickedCodetoc.mspx?mfr=true"><u><font color=#0000ff>http://www.microsoft.com/china/msdn/library/webservices/asp.net/issuesWickedCodetoc.mspx?mfr=true</font></u></a></p>
<p>IsClientConnected的问题 <br><a title=http://topic.csdn.net/u/20080712/19/74fa4070-84bd-4fda-a99b-ac361f874738.html href="http://topic.csdn.net/u/20080712/19/74fa4070-84bd-4fda-a99b-ac361f874738.html"><u><font color=#0000ff>http://topic.csdn.net/u/20080712/19/74fa4070-84bd-4fda-a99b-ac361f874738.html</font></u></a></p>
<p>Response.IsClientConnected 原理和用法 <br><a title=http://blog.51ait.cn/article.asp?id=357 href="http://blog.51ait.cn/article.asp?id=357"><u><font color=#0000ff>http://blog.51ait.cn/article.asp?id=357</font></u></a>&nbsp;&nbsp;&nbsp; 第一个地址比较慢，可以看下一个地址 <br><a title=http://blog.joycode.com/ghj/archive/2008/07/23/115198.aspx href="http://blog.joycode.com/ghj/archive/2008/07/23/115198.aspx"><u><font color=#0000ff>http://blog.joycode.com/ghj/archive/2008/07/23/115198.aspx</font></u></a></p>
<img src ="http://www.cnitblog.com/seeyeah/aggbug/47218.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2008-07-28 23:37 <a href="http://www.cnitblog.com/seeyeah/archive/2008/07/28/47218.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>IIS5、IIS6、IIS7的ASP.net 请求处理过程比较 </title><link>http://www.cnitblog.com/seeyeah/archive/2008/07/28/47217.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Mon, 28 Jul 2008 15:36:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2008/07/28/47217.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/47217.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2008/07/28/47217.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/47217.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/47217.html</trackback:ping><description><![CDATA[摘至：<a id=ctl00_MainContent_ViewPost1_TitleUrl title="Title of this entry." href="http://blog.joycode.com/ghj/archive/2008/07/25/115200.aspx"><u><font color=#800080>IIS5、IIS6、IIS7的ASP.net 请求处理过程比较</font></u></a> <br>
<hr>
<p>ASP.NET是一个非常强大的构建Web应用的平台，它提供了极大的灵活性和能力以致于可以用它来构建所有类型的Web应用。 <br>绝大多数的人只熟悉高层的框架如： WebForms 和 WebServices --这些都在ASP.NET层次结构在最高层。</p>
<p>这篇文章的资料收集整理自各种微软公开的文档，通过比较 IIS5、IIS6、IIS7 这三代 IIS 对请求的处理过程， 让我们熟悉 ASP.NET的底层机制 并对请求(request)是怎么从Web服务器传送到ASP.NET运行时有所了解。通过对底层机制的了解，可以让我们对 ASP.net 有更深的理解。</p>
<p><strong>IIS 5 的 ASP.net 请求处理过程</strong></p>
<p><img src="http://blog.joycode.com/images/blog.joycode.com/ghj/1362/o_8001.gif"></p>
<p>对图的解释：</p>
<p>IIS 5.x 一个显著的特征就是 Web Server 和真正的 ASP.NET Application 的分离。作为 Web Server 的IIS运行在一个名为 InetInfo.exe 的进程上，InetInfo.exe 是一个Native Executive，并不是一个托管的程序，而我们真正的 ASP.NET Application 则是运行在一个叫做 aspnet_wp 的 Worker Process 上面，在该进程初始化的时候会加载CLR，所以这是一个托管的环境。</p>
<p>ISAPI：&nbsp; 指能够处理各种后缀名的应用程序。 ISAPI 是下面单词的简写 ：Internet Server Application Programe Interface，互联网服务器应用程序接口。</p>
<p>IIS 5 模式的特点：</p>
<p>1、首先，同一台主机上在同一时间只能运行一个 aspnet_wp 进程，每个基于虚拟目录的 ASP.NET Application 对应一个 Application Domain ，也就是说每个 Application 都运行在同一个 Worker Process 中，Application之间的隔离是基于 Application Domain 的，而不是基于Process的。 </p>
<p>2、其次，ASP.NET&nbsp; ISAPI 不但负责创建 aspnet_wp Worker Process，而且负责监控该进程，如果检测到 aspnet_wp 的 Performance 降低到某个设定的下限，ASP.NET&nbsp; ISAPI 会负责结束掉该进程。当 aspnet_wp 结束掉之后，后续的 Request 会导致ASP.NET ISAPI 重新创建新的 aspnet_wp Worker Process。 </p>
<p>3、最后，由于 IIS 和 Application 运行在他们各自的进程中，他们之间的通信必须采用特定的通信机制。本质上 IIS 所在的 InetInfo 进程和 Worker Process 之间的通信是同一台机器不同进程的通信（local interprocess communications），处于Performance的考虑，他们之间采用基于Named pipe的通信机制。ASP.NET ISAPI和Worker Process之间的通信通过他们之间的一组Pipe实现。同样处于Performance的原因，ASP.NET ISAPI 通过异步的方式将Request 传到Worker Process 并获得 Response，但是 Worker Process 则是通过同步的方式向 ASP.NET ISAPI 获得一些基于 Server 的变量。 </p>
<p>&nbsp;</p>
<p><strong>IIS6 的 ASP.net 请求处理过程</strong></p>
<p><img src="http://blog.joycode.com/images/blog.joycode.com/ghj/1362/o_8002.gif"></p>
<p>对图的解释：</p>
<p>IIS 5.x 是通过 InetInfo.exe 监听 Request 并把Request分发到Work Process。换句话说，在IIS 5.x中对Request的监听和分发是在User Mode中进行，在IIS 6中，这种工作被移植到kernel Mode中进行，所有的这一切都是通过一个新的组件：http.sys 来负责。 </p>
<p>注：为了避免用户应用程序访问或者修改关键的操作系统数据，windows提供了两种处理器访问模式：用户模式（User Mode）和内核模式（Kernel Mode）。一般地，用户程序运行在User mode下，而操作系统代码运行在Kernel Mode下。Kernel Mode的代码允许访问所有系统内存和所有CPU指令。</p>
<p>在User Mode下，http.sys接收到一个基于 aspx 的http request，然后它会根据IIS中的 Metabase 查看该基于该 Request 的 Application 属于哪个Application Pool， 如果该Application Pool不存在，则创建之。否则直接将 request 发到对应Application Pool 的 Queue中。</p>
<p>每个 Application Pool 对应着一个Worker Process：w3wp.exe，毫无疑问他是运行在User Mode下的。在IIS Metabase 中维护着 Application Pool 和worker process的Mapping。WAS（Web Administrative service）根据这样一个mapping，将存在于某个Application Pool Queue的request 传递到对应的worker process(如果没有，就创建这样一个进程)。在 worker process 初始化的时候，加载ASP.NET ISAPI，ASP.NET ISAPI 进而加载CLR。最后的流程就和IIS 5.x一样了：通过AppManagerAppDomainFactory 的 Create方法为 Application 创建一个Application Domain；通过 ISAPIRuntime 的 ProcessRequest处理Request，进而将流程进入到ASP.NET Http Runtime Pipeline。</p>
<p>&nbsp;</p>
<p><strong>IIS 7&nbsp; 的 ASP.net 请求处理过程</strong></p>
<p>&nbsp;</p>
<p>IIS7 站点启动并处理请求的步骤如下图：</p>
<p>步骤 1 到 6 ，是处理应用启动，启动好后，以后就不需要再走这个步骤了。</p>
<p><img height=617 src="http://blog.joycode.com/images/blog.joycode.com/ghj/1362/o_9010.jpg" width=690></p>
<p>上图的8个步骤分别如下：</p>
<p>1、当客户端浏览器开始HTTP 请求一个WEB 服务器的资源时，HTTP.sys 拦截到这个请求。 <br>2、HTTP.sys contacts WAS to obtain information from the configuration store. </p>
<p>3、WAS 向配置存储中心请求配置信息。applicationHost.config。 <br>4、WWW 服务接受到配置信息，配置信息指类似应用程序池配置信息，站点配置信息等等。 <br>5、WWW 服务使用配置信息去配置 HTTP.sys 处理策略。 <br>6、WAS starts a worker process for the application pool to which the request was made. </p>
<p>7、The worker process processes the request and returns a response to HTTP.sys. </p>
<p>8、客户端接受到处理结果信息。</p>
<p>W3WP.exe 进程中又是如果处理得呢？？ IIS 7 的应用程序池的托管管道模式分两种： 经典和集成。 这两种模式下处理策略各不相通。</p>
<p><font color=#ffffff>本文作者：郭红俊</font> <a title=http://blog.joycode.com/ghj href="http://blog.joycode.com/ghj"><font color=#ffffff><u>http://blog.joycode.com/ghj</u></font></a></p>
<p>&nbsp;</p>
<p><strong>IIS 6 以及 IIS7 经典模式的托管管道的架构</strong></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在IIS7之前，ASP.NET 是以 IIS ISAPI extension 的方式外加到 IIS，其实包括 ASP 以及 PHP，也都以相同的方式配置（PHP 在 IIS 采用了两种配置方式，除了 IIS ISAPI extension 的方式，也包括了 CGI 的方式，系统管理者能选择 PHP 程序的执行方式），因此客户端对 IIS 的 HTTP 请求会先经由 IIS 处理，然后 IIS 根据要求的内容类型，如果是 HTML 静态网页就由 IIS 自行处理，如果不是，就根据要求的内容类型，分派给各自的 IIS ISAPI extension；如果要求的内容类型是 ASP.NET，就分派给负责处理 ASP.NET 的 IIS ISAPI extension，也就是 aspnet_isapi.dll。下图是这个架构的示意图。 </p>
<p>IIS&nbsp; 7 应用程序池的 托管管道模式&nbsp; 经典&nbsp; 模式也是这样的工作原理。 这种模式是兼容IIS 6 的方式， 以减少升级的成本。</p>
<p><img src="http://blog.joycode.com/images/blog.joycode.com/ghj/1362/o_9003.jpg"></p>
<p>IIS6 的执行架构图，以及 IIS7&nbsp; 应用程序池配置成经典模式的执行架构图</p>
<p>&nbsp;</p>
<p><strong>IIS&nbsp; 7 应用程序池的 托管管道模式&nbsp; 集成模式</strong></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 而 IIS 7 完全整合 .NET 之后，架构的处理顺序有了很大的不同（如下图），最主要的原因就是 ASP.NET 从 IIS 插件（ISAPI extension）的角色，进入了 IIS 核心，而且也能以 ASP.NET 模块负责处理 IIS 7 的诸多类型要求。这些 ASP.NET 模块不只能处理 ASP.NET 网页程序，也能处理其他如 ASP 程序、PHP 程序或静态 HTML 网页，也因为 ASP.NET 的诸多功能已经成为 IIS 7 的一部份，因此 ASP 程序、PHP 程序或静态 HTML 网页等类型的要求，也能使用像是Forms认证（Forms Authentication）或输出缓存（Output Cache）等 ASP.NET 2.0 的功能（但须修改 IIS 7 的设定值）。也因为 IIS 7 允许自行以 ASP.NET API 开发并加入模块，因此 ASP.NET 网页开发人员将更容易扩充 IIS 7 和网站应用程序的功能，甚至能自行以 .NET 编写管理 IIS 7 的程序（例如以程控 IIS 7 以建置网站或虚拟目录）。</p>
<p><img src="http://blog.joycode.com/images/blog.joycode.com/ghj/1362/o_9004.jpg"></p>
<p>IIS 7 的执行架构图（集成托管信道模式下的架构）</p>
<p>&nbsp;</p>
<p><strong>小结</strong></p>
<p>IIS5 到 IIS6 的改进，主要是 HTTP.sys 的改进。</p>
<p>IIS6 到 IIS7 的改进，主要是 ISAPI 的改进。</p>
<p>&#160;</p>
<p>&nbsp;</p>
<p><strong>参考资料：</strong></p>
<p>ASP.NET Process Model之一：IIS 和 ASP.NET ISAPI <br><a title=http://www.cnblogs.com/artech/archive/2007/09/09/887528.html href="http://www.cnblogs.com/artech/archive/2007/09/09/887528.html"><u><font color=#0000ff>http://www.cnblogs.com/artech/archive/2007/09/09/887528.html</font></u></a></p>
<p>ASP.NET Internals &#8211; IIS and the Process Model <br><a title=http://dotnetslackers.com/articles/iis/ASPNETInternalsIISAndTheProcessModel.aspx href="http://dotnetslackers.com/articles/iis/ASPNETInternalsIISAndTheProcessModel.aspx"><u><font color=#0000ff>http://dotnetslackers.com/articles/iis/ASPNETInternalsIISAndTheProcessModel.aspx</font></u></a></p>
<p>模组化的IIS 7 与.NET 能力整合 <br><a title=http://www.microsoft.com/taiwan/technet/columns/profwin/33-iis7-componentization-integration.mspx href="http://www.microsoft.com/taiwan/technet/columns/profwin/33-iis7-componentization-integration.mspx"><u><font color=#0000ff>http://www.microsoft.com/taiwan/technet/columns/profwin/33-iis7-componentization-integration.mspx</font></u></a></p>
<p>Introduction to IIS 7.0 Architecture <br><a title=http://learn.iis.net/page.aspx/101/introduction-to-iis7-architecture/ href="http://learn.iis.net/page.aspx/101/introduction-to-iis7-architecture/"><u><font color=#0000ff>http://learn.iis.net/page.aspx/101/introduction-to-iis7-architecture/</font></u></a></p>
<img src ="http://www.cnitblog.com/seeyeah/aggbug/47217.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2008-07-28 23:36 <a href="http://www.cnitblog.com/seeyeah/archive/2008/07/28/47217.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Scott Mitchell的ASP.NET2.0数据指南中文版索引 【转载】</title><link>http://www.cnitblog.com/seeyeah/archive/2008/04/24/42710.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Thu, 24 Apr 2008 14:05:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2008/04/24/42710.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/42710.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2008/04/24/42710.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/42710.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/42710.html</trackback:ping><description><![CDATA[摘至：<a id=AjaxHolder_ctl01_TitleUrl href="http://www.cnblogs.com/lovecherry/archive/2006/07/02/440840.html"><u><font color=#800080>【翻译】Scott Mitchell的ASP.NET2.0数据指南中文版索引</font></u></a> <br>
<hr>
<a href="http://www.asp.net/learn/dataaccess/default.aspx?tabid=63" target=_blank><u><font color=#0000ff>英文版本</font></u></a><br><br>
<dt>
<p>导言</p>
<ul>
    <li><a href="http://blog.joycode.com/saucer/articles/77807.aspx" target=_blank><u><font color=#0000ff>创建一个数据访问层</font></u></a>
    <li><a href="http://reeezak.cnblogs.com/archive/2006/06/24/434439.html" target=_blank><u><font color=#0000ff>创建一个业务逻辑层</font></u></a>
    <li><a href="http://yuyijq.cnblogs.com/archive/2006/06/25/435218.html" target=_blank><u><font color=#0000ff>母板页和站点导航</font></u></a>&nbsp; </li>
</ul>
<dt>基本报表&nbsp;
<ul>
    <li><a href="http://eddie005.cnblogs.com/articles/UsingObjectDataSource.html" target=_blank><u><font color=#0000ff>使用ObjectDataSource展现数据</font></u><a href="http://www.asp.net/learn/dataaccess/tutorial04cs.aspx?tabid=63"></a> </a>
    <li><a href="http://eddie005.cnblogs.com/archive/2006/06/26/DeclarativeParameters.html" target=_blank><u><font color=#0000ff>声明参数</font></u></a>
    <li><a href="http://eddie005.cnblogs.com/archive/2006/06/27/SetParameters.html" target=_blank><u><font color=#0000ff>编程设置ObjectDataSource的参数值</font></u></a> </li>
</ul>
<dt>主/从&nbsp;
<ul>
    <li><a href="http://lidong.cnblogs.com/archive/2006/06/23/434105.html" target=_blank><u><font color=#0000ff>使用DropDownList过滤的主/从报表</font></u></a>&nbsp;&nbsp;&nbsp;
    <li><a href="http://lidong.cnblogs.com/archive/2006/06/28/437704.html" target=_blank><u><font color=#0000ff>使用两个DropDownList过滤的主/从报表</font></u></a>
    <li><a href="http://lidong.cnblogs.com/archive/2006/07/05/443203.html" target=_blank><u><font color=#0000ff>跨页面的主/从报表</font></u></a>&nbsp;
    <li><a href="http://lidong.cnblogs.com/archive/2006/07/05/443207.html" target=_blank><u><font color=#0000ff>使用GridView 和DetailView实现的主/从报表</font></u></a> </li>
</ul>
<dt>自定义格式化&nbsp;
<ul>
    <li><a href="http://zhzkl.cnblogs.com/archive/2006/07/01/440614.html" target=_blank><u><font color=#0000ff>基于数据的自定义格式化</font></u></a>
    <li><a href="http://reeezak.cnblogs.com/archive/2006/07/06/444315.html" target=_blank><font color=#0000ff><u>在GridView控件中使用TemplateField </u></font></a>
    <li><a href="http://reeezak.cnblogs.com/archive/2006/07/06/444700.html" target=_blank><u><font color=#0000ff>在DetailsView控件中使用TemplateField</font></u></a>
    <li><a href="http://reeezak.cnblogs.com/archive/2006/07/07/445611.html" target=_blank><u><font color=#0000ff>使用FormView 的模板</font></u></a> <a href="http://reeezak.cnblogs.com/archive/2006/07/09/446444.html" target=_blank>
    <li>在GridView的页脚中显示统计信息 </a></li>
</ul>
<dt>&nbsp;&nbsp;编辑插入和删除数据&nbsp;
<ul>
    <li><a href="http://www.cnblogs.com/eddie005/archive/2006/08/28/488507.html" target=_blank><u><font color=#0000ff>概述插入、更新和删除数据</font></u></a>&nbsp;
    <li><a href="http://www.cnblogs.com/eddie005/archive/2006/08/18/EventsOfDataWebControl.html" target=_blank><u><font color=#0000ff>研究插入、更新和删除的关联事件</font></u></a>
    <li><a href="http://www.cnblogs.com/eddie005/archive/2006/08/08/BLLAndDALExceptions.html" target=_blank><u><font color=#0000ff>在ASP.NET页面中处理BLL/DAL层的异常</font></u></a>
    <li><a href="http://truly.cnblogs.com/archive/2006/06/27/437107.html" target=_blank><u><font color=#0000ff>给编辑和新增界面增加验证控件</font></u></a>&nbsp;
    <li><a href="http://truly.cnblogs.com/archive/2006/06/29/438526.html" target=_blank><u><font color=#0000ff>定制数据修改界面</font></u></a>
    <li><a href="http://www.cnblogs.com/eddie005/archive/2006/08/01/OptimisticConcurrency.html" target=_blank><u><font color=#0000ff>实现开放式并发&nbsp;</font></u></a>&nbsp;
    <li><a href="http://chagel.cnblogs.com/archive/2006/07/19/454440.html" target=_blank><u><font color=#0000ff>为删除数据添加客户端确认</font></u></a>
    <li><a href="http://chagel.cnblogs.com/articles/456883.html" target=_blank><u><font color=#0000ff>基于用户对修改数据进行限制</font></u></a> </li>
</ul>
<p>分页和排序&nbsp; </p>
<ul>
    <li><a href="http://www.cnblogs.com/lovecherry/archive/2006/08/25/486077.html"><u><font color=#0000ff>分页和排序报表数据</font></u></a>
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/10/10/523992.html" target=_blank><u><font color=#0000ff>大数据量时提高分页的效率</font></u></a>
    <li><a href="http://www.cnblogs.com/lovecherry/archive/2006/09/05/494998.html" target=_blank><u><font color=#0000ff>排序自定义分页数据</font></u></a>
    <li><a href="http://blog.csdn.net/ksnoopy/archive/2006/08/27/1125097.aspx" target=_blank><u><font color=#0000ff>创建自定义排序用户界面</font></u></a>&nbsp;<a href="http://reeezak.cnblogs.com/archive/2006/07/06/444315.html" target=_blank></a> </li>
</ul>
<p>自定义按钮行为&nbsp; </p>
<ul>
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/10/04/521400.html" target=_blank><u><font color=#0000ff>GridView里的Button &nbsp;</font></u></a> </li>
</ul>
<p>使用DataList和Repeater显示数据&nbsp; </p>
<ul>
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/10/07/521593.html" target=_blank><u><font color=#0000ff>用DataList和Repeater来显示数据</font></u></a>&nbsp;
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/10/08/523252.html" target=_blank><u><font color=#0000ff>格式化DataList和Repeater的数据</font></u></a>
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/10/13/527119.html" target=_blank><u><font color=#0000ff>使用DataList来一行显示多条记录</font></u></a>&nbsp;
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/10/17/527593.html" target=_blank><u><font color=#0000ff>数据控件的嵌套</font></u></a> </li>
</ul>
<p>使用DataList和Repeater过滤数据&nbsp; </p>
<ul>
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/10/23/533180.html" target=_blank><u><font color=#0000ff>使用DropDownList过滤的主/从报表</font></u></a>
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/10/26/537273.html" target=_blank><u><font color=#0000ff>跨页面的主/从报表</font></u></a>
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/10/30/539128.html" target=_blank><u><font color=#0000ff>使用Repeater和DataList实现的主/从报表</font></u></a>&nbsp; </li>
</ul>
<p>使用DataList编辑和删除数据&nbsp; </p>
<ul>
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/11/03/546974.html" target=_blank><u><font color=#0000ff>综叙：在DataList里编辑和删除数据</font></u></a>&nbsp;
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/11/07/551210.html" target=_blank><u><font color=#0000ff>批量更新</font></u></a>&nbsp;
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/11/08/551745.html" target=_blank><u><font color=#0000ff>处理BLL和DAL的异常</font></u></a>
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/11/13/553095.html"><u><font color=#0000ff>在编辑和插入界面里添加验证控件</font></u></a>&nbsp;
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/11/15/559289.html"><u><font color=#0000ff>自定义DataList编辑界面</font></u></a>
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/11/21/561125.html"><u><font color=#0000ff>实现开放式并发</font></u></a>
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/11/27/568200.html"><u><font color=#0000ff>为删除数据添加客户端确认</font></u></a>
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/11/30/573628.html"><u><font color=#0000ff>基于用户对修改数据进行限制</font></u></a>&nbsp; </li>
</ul>
<p>DataList和Repeater的分页和排序<br></p>
<ul>
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/12/04/577194.html"><u><font color=#0000ff>DataList和Repeater数据分页</font></u></a>&nbsp;&nbsp;
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/12/08/586061.html"><u><font color=#0000ff>DataList和Repeater数据排序（一）</font></u></a>&nbsp;
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/12/14/592373.html"><u><font color=#0000ff>DataList和Repeater数据排序（二）</font></u></a>&nbsp;
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/12/14/592374.html"><u><font color=#0000ff>DataList和Repeater数据排序（三）</font></u></a>&nbsp; </li>
</ul>
<p>DataList和Repeater的自定义按钮行为</p>
<ul>
    <li><a href="http://www.cnblogs.com/ilovejolly/archive/2006/12/15/593555.html"><u><font color=#0000ff>DataList和Repeater里的自定义button</font></u></a> </li>
</ul>
<p>特别感谢<a href="http://www.cnblogs.com/ilovejolly/" target=_blank><u><font color=#0000ff>有些伤感</font></u></a>，近一半的文档都是他翻译的。<br><br>46之后可以在下面的地址找到：<br><br><font face="Times New Roman" color=#000000 size=3>http://www.cnblogs.com/Reeezak/archive/2007/08/13/853925.html</font></p>
<p><font face="Times New Roman" color=#000000 size=3>50之后可以在下面的地址找到：</font></p>
<p><font face="Times New Roman" color=#000000 size=3>http://blog.csdn.net/heker2007/archive/2007/06.aspx</font></p>
</dt>
<img src ="http://www.cnitblog.com/seeyeah/aggbug/42710.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2008-04-24 22:05 <a href="http://www.cnitblog.com/seeyeah/archive/2008/04/24/42710.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>告别ASP.NET操作EXCEL的烦恼</title><link>http://www.cnitblog.com/seeyeah/archive/2008/04/13/42320.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Sun, 13 Apr 2008 14:32:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2008/04/13/42320.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/42320.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2008/04/13/42320.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/42320.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/42320.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Copy From 告别ASP.NET操作EXCEL的烦恼(总结篇)公元19XX年前，关于EXCEL的操作就如滔滔江水，连绵不绝，真正操作EXCEL我也是从去年下半年开始的，有些比较复杂的年度报表之类的，做起来也有点费力，不过还是都能画出来了，关于EXCEL的报表导出，考虑到导出耗时的问题我主要采用AJAX来做的，分别捕捉几个起止状态，给客户端提示3个状态：正在检索数据。。。---》准备导...&nbsp;&nbsp;<a href='http://www.cnitblog.com/seeyeah/archive/2008/04/13/42320.html'>阅读全文</a><img src ="http://www.cnitblog.com/seeyeah/aggbug/42320.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2008-04-13 22:32 <a href="http://www.cnitblog.com/seeyeah/archive/2008/04/13/42320.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Silverlight技巧，诀窍，教程和链接 </title><link>http://www.cnitblog.com/seeyeah/archive/2008/04/11/42269.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Fri, 11 Apr 2008 15:26:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2008/04/11/42269.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/42269.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2008/04/11/42269.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/42269.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/42269.html</trackback:ping><description><![CDATA[<p>【原文地址】<a title=在Silverlight中做单元测试 href="http://weblogs.asp.net/scottgu/pages/silverlight-posts.aspx"><u><font color=#0000ff>Silverlight Tips, Tricks, Tutorials and Links Page </font></u></a></p>
<p>我将使用本页链接到有关Silverlight 2的文章和贴子（包括我写的和别人写的），请经常回来查看新的内容，因为我才开始。 </p>
<h3><u>入门教程</u></h3>
<ul>
    <li>
    <p><a href="http://blog.joycode.com/scottgu/archive/2008/02/23/114891.aspx" target=_blank mce_href="http://blog.joycode.com/scottgu/archive/2008/02/23/114891.aspx"><u><font color=#0000ff>Silverlight 2 初览</font></u></a>: 我建议先读这个贴子，如果你正考虑开始Silverlight 2开发的话。该文不仅对Silverlight 2提供的功能做了高层次的描述，还链接到了一个<a href="http://blog.joycode.com/scottgu/archive/2008/02/29/114918.aspx" target=_blank mce_href="http://blog.joycode.com/scottgu/archive/2008/02/29/114918.aspx"><u><font color=#0000ff>八个贴子的系列教程</font></u></a>，该教程对如何建造一个Digg客户端例程做了示范。这些教程贴子对Silverlight 2和WPF开发的概念提供了很好的介绍。</p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://blog.joycode.com/scottgu/archive/2008/03/02/114920.aspx" target=_blank mce_href="http://blog.joycode.com/scottgu/archive/2008/03/02/114920.aspx"><u><font color=#0000ff>使用Expression Blend开发Silverlight 2应用 -- 初览</font></u></a>: 我建议下载Expression Blend 2.5 三月份预览版，按本教程贴子的步骤进行。我认为它很好地展示了Expression Blend的一些常见功能，用它建造了一个IM聊天客户端例程。</p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://www.jeff.wilcox.name/2008/03/31/silverlight2-unit-testing/" target=_blank mce_href="http://www.jeff.wilcox.name/2008/03/31/silverlight2-unit-testing/"><u><font color=#0000ff>Unit Testing with Silverlight 2</font></u></a>: Silverlight开发团队的Jeff Wilcox有一篇精彩贴子，讨论如何使用Silverlight SDK中的单元测试框架单元测试Silverlight 2应用。</p>
    </li>
</ul>
<h3><u>入门讲座</u></h3>
<ul>
    <li>
    <p><a href="http://blogs.msdn.com/mharsh/archive/2008/03/05/slides-and-demos-from-my-mix-08-talk.aspx" target=_blank mce_href="http://blogs.msdn.com/mharsh/archive/2008/03/05/slides-and-demos-from-my-mix-08-talk.aspx"><u><font color=#0000ff>Building Rich Internet Applications with Silverlight (Part 1 and 2)</font></u></a>: Joe Stegman和Mike Harsh在MIX 08上做了这分成2个部分的讲座， 讨论如何使用Silverlight建造应用。你可以使用上面的链接观看他们讲座的录像和下载相关讲义和示范例程。</p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://scorbs.com/2008/03/09/mix08-creating-rich-dynamic-user-interfaces-with-silverlight-2-controls/" target=_blank mce_href="http://scorbs.com/2008/03/09/mix08-creating-rich-dynamic-user-interfaces-with-silverlight-2-controls/"><u><font color=#0000ff>Creating Rich, Dynamic User Interfaces with Silverlight 2 Controls</font></u></a>: Karen Corby在MIX 08上有个极棒的讲座，很好地解释了样式，控件模板，用户控件等等。你可以在<a href="http://scorbs.com/2008/03/14/flickrviewr/" target=_blank mce_href="http://scorbs.com/2008/03/14/flickrviewr/"><u><font color=#0000ff>这里</font></u></a>运行和下载她完整的FlickR应用。</p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://weblogs.asp.net/scottgu/archive/2008/03/09/my-presentations-in-arizona-this-tuesday.aspx" target=_blank mce_href="http://weblogs.asp.net/scottgu/archive/2008/03/09/my-presentations-in-arizona-this-tuesday.aspx"><u><font color=#0000ff>我的Silverlight入门讲座</font></u></a>: 你可以下载我最近在亚利桑那做的&#8220;Silverlight 2入门&#8221;讲座的讲义和示范例程，请随意在你自己的讲座中重用这些讲义。</p>
    </li>
</ul>
<h3><u>文档链接</u></h3>
<ul>
    <li>
    <p><a href="http://silverlight.net/quickstarts/managed.aspx" target=_blank mce_href="http://silverlight.net/quickstarts/managed.aspx"><u><font color=#0000ff>Silverlight 2 QuickStart Tutorials</font></u></a>: 这些示范教程在上手Silverlight时会非常有用。</p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://blogs.msdn.com/brada/archive/2008/03/16/silverlight-2-developer-poster.aspx" target=_blank mce_href="http://blogs.msdn.com/brada/archive/2008/03/16/silverlight-2-developer-poster.aspx"><u><font color=#0000ff>Silverlight 2 Developer Poster:</font></u></a> 下载和打印Silverlight 2开发者海报。</p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://blogs.msdn.com/sburke/archive/2008/03/05/silverlight-2-beta-1-controls-available-including-source-and-unit-tests.aspx" target=_blank mce_href="http://blogs.msdn.com/sburke/archive/2008/03/05/silverlight-2-beta-1-controls-available-including-source-and-unit-tests.aspx"><u><font color=#0000ff>Silverlight Control Source and Unit Tests</font></u></a>: Shawn Burke提供了内置的Silverlight控件的源码以及相关单元测试的链接。 </p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://msdn2.microsoft.com/en-us/library/bb404700.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/bb404700.aspx"><u><font color=#0000ff>MSDN Silverlight 2 Beta1 Online Documentation</font></u></a>: 这个网页提供了正式的Silverlight 2文档的链接。你可以在<a href="http://blogs.msdn.com/silverlight_sdk/archive/2008/03/03/silverlight-offline-documentation-march-2008.aspx" target=_blank mce_href="http://blogs.msdn.com/silverlight_sdk/archive/2008/03/03/silverlight-offline-documentation-march-2008.aspx"><u><font color=#0000ff>这里</font></u></a>下载离线版本。</p>
    </li>
</ul>
<h3><u>教程和例程</u></h3>
<ul>
    <li>
    <p><a href="http://silverlight.net/Learn/videocat.aspx?cat=2" target=_blank mce_href="http://silverlight.net/Learn/videocat.aspx?cat=2"><u><font color=#0000ff>"How Do I?" with Silverlight</font></u></a>: 这个链接连到了含有一堆Silverlight 2录像教程的网页。</p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://sessions.visitmix.com/?selectedSearch=Hard Rock&amp;searchPlink=true" target=_blank><u><font color=#0000ff>Hard Rock Memorabilia Sample</font></u></a>: Vertigo Software公司的Scott Stanfield讨论三月份发布的使用Silverlight 2建造的很酷的<a href="http://memorabilia.hardrock.com/" target=_blank mce_href="http://memorabilia.hardrock.com/"><u><font color=#0000ff>Hard Rock Memorabilia</font></u></a>网站。</p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://geekswithblogs.net/SilverBlog/archive/2008/03/25/silverlight-2.0-application-with-multiscaleimage-control-and-deep-zoom-composer.aspx" target=_blank mce_href="http://geekswithblogs.net/SilverBlog/archive/2008/03/25/silverlight-2.0-application-with-multiscaleimage-control-and-deep-zoom-composer.aspx"><u><font color=#0000ff>Using Deep Zoom with Silverlight 2</font></u></a>: Jacek Ciereszko有一个很好的博客贴子，描述了如何使用Silverlight 2的 &#8220;Deep Zoom&#8221;功能实现跟Hard Rock例程类似的图像放大功能。</p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://silverlight.net/blogs/msnow/archive/2008/03/25/introduction.aspx" target=_blank mce_href="http://silverlight.net/blogs/msnow/archive/2008/03/25/introduction.aspx"><u><font color=#0000ff>Game Programming with Silverlight:</font></u></a> Mike Snow正打造一个分成十一个部分的博客系列，讨论如何使用Silverlight开发在线游戏。</p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://www.jeff.wilcox.name/2008/04/01/link-cloud/" target=_blank mce_href="http://www.jeff.wilcox.name/2008/04/01/link-cloud/"><u><font color=#0000ff>Homepage "Link Cloud" application</font></u></a>: Jeff Wilcox讨论他建造的一个很酷的标签云应用。</p>
    </li>
</ul>
<h3><u>部署体验</u></h3>
<ul>
    <li>
    <p><a href="http://timheuer.com/blog/archive/2008/03/25/creating-a-great-silverlight-deployment-experience.aspx" target=_blank mce_href="http://timheuer.com/blog/archive/2008/03/25/creating-a-great-silverlight-deployment-experience.aspx"><u><font color=#0000ff>Providing a Great Silverlight Deployment Experience</font></u></a>: Tim Heuer有个很好的贴子，讨论可以为你所用的技术来优化Silverlight安装体验。</p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://blogs.msdn.com/brada/archive/2008/03/14/using-silverlight-2-on-a-production-web-server.aspx" target=_blank mce_href="http://blogs.msdn.com/brada/archive/2008/03/14/using-silverlight-2-on-a-production-web-server.aspx"><u><font color=#0000ff>Using Silverlight 2 on a Production Web Server:</font></u></a> Brad Abrams有个很好的贴子，讨论在把Silverlight .xap文件拷贝到IIS web服务器时需要注意的几件事情。 </p>
    </li>
</ul>
<h3><u>控件模板</u></h3>
<ul>
    <li>
    <p><a href="http://mattberseth.com/blog/2008/03/creating_a_custom_skin_for_sil.html" target=_blank mce_href="http://mattberseth.com/blog/2008/03/creating_a_custom_skin_for_sil.html"><u><font color=#0000ff>Creating a Custom Skin for Silverlight's Button Control:</font></u></a> Matt Berseth的一个精彩好贴，讨论Silverlight控件的皮肤和样式功能。 </p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://blogs.msdn.com/sburke/archive/2008/03/22/tutorial-writing-a-templated-silverlight-2-control.aspx" target=_blank mce_href="http://blogs.msdn.com/sburke/archive/2008/03/22/tutorial-writing-a-templated-silverlight-2-control.aspx"><u><font color=#0000ff>Writing a Templated Silverlight Control</font></u></a>: Shawn Burke有个精彩贴子，详细描述了如何创建定制的Silverlight模板控件。</p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://blogs.msdn.com/delay/archive/2008/03/22/improving-everyone-s-access-to-silverlight-2-s-generic-xaml-resources-silverlightdefaultstylebrowser-tool-and-source-code.aspx" target=_blank mce_href="http://blogs.msdn.com/delay/archive/2008/03/22/improving-everyone-s-access-to-silverlight-2-s-generic-xaml-resources-silverlightdefaultstylebrowser-tool-and-source-code.aspx"><u><font color=#0000ff>Improving Access to Silverlight's Generic.xaml Resources</font></u></a> (也阅读 <a href="http://blogs.msdn.com/delay/archive/2008/03/22/improved-access-to-silverlight-2-s-generic-xaml-resources-silverlightdefaultstylebrowser-available-via-clickonce.aspx" target=_blank mce_href="http://blogs.msdn.com/delay/archive/2008/03/22/improved-access-to-silverlight-2-s-generic-xaml-resources-silverlightdefaultstylebrowser-available-via-clickonce.aspx"><u><font color=#0000ff>这里的Part 2</font></u></a>): David Anson有个很棒的工具，示范如何查看和提取内置Silverlight控件的默认XAML样式。</p>
    </li>
</ul>
<h3><u>ListBox和ScrollViewer场景</u></h3>
<ul>
    <li>
    <p><a href="http://blogs.msdn.com/delay/archive/2008/03/05/lb-sv-faq-examples-notes-tips-and-more-for-silverlight-2-beta-1-s-listbox-and-scrollviewer-controls.aspx" target=_blank mce_href="http://blogs.msdn.com/delay/archive/2008/03/05/lb-sv-faq-examples-notes-tips-and-more-for-silverlight-2-beta-1-s-listbox-and-scrollviewer-controls.aspx"><u><font color=#0000ff>Ultimate ListBox and ScrollViewer Frequently Asked Questions Guide</font></u></a>: David Anson，Silverlight ListBox和ScrollViewer控件的开发者，编篡了这个很棒的，关于所有这些控件的FAQ以及你可以使用它们的场景。</p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://blogs.msdn.com/delay/archive/2008/03/12/lb-sv-why-three-wacky-uses-for-silverlight-2-s-listbox-and-scrollviewer.aspx" target=_blank mce_href="http://blogs.msdn.com/delay/archive/2008/03/12/lb-sv-why-three-wacky-uses-for-silverlight-2-s-listbox-and-scrollviewer.aspx"><u><font color=#0000ff>Cool (wacky) uses of the ListBox and ScrollViewer controls</font></u></a>: David Anson的又一篇精彩贴子，展示了数据和控件模板的灵活性。</p>
    </li>
</ul>
<h3><u>DataGrid场景</u></h3>
<ul>
    <li>
    <p><a href="http://blogs.msdn.com/scmorris/archive/2008/03/21/using-the-silverlight-datagrid.aspx" target=_blank mce_href="http://blogs.msdn.com/scmorris/archive/2008/03/21/using-the-silverlight-datagrid.aspx"><u><font color=#0000ff>Using the Silverlight DataGrid</font></u></a> and <a href="http://blogs.msdn.com/scmorris/archive/2008/03/27/defining-columns-for-a-silverlight-datagrid.aspx" target=_blank mce_href="http://blogs.msdn.com/scmorris/archive/2008/03/27/defining-columns-for-a-silverlight-datagrid.aspx"><u><font color=#0000ff>Defining Columns for a Silverlight DataGrid</font></u></a>: Scott Morrison开始了一个精彩贴子系列，描述如何使用新的Silverlight 2 DataGrid控件（他是相关项目的主管，绝对精通！）</p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://blogs.msdn.com/swiss_dpe_team/archive/2008/03/17/silverlight-2-beta1-wcf-linq-to-sql-a-powerfull-combination.aspx" target=_blank mce_href="http://blogs.msdn.com/swiss_dpe_team/archive/2008/03/17/silverlight-2-beta1-wcf-linq-to-sql-a-powerfull-combination.aspx"><u><font color=#0000ff>Using Silverlight 2's DataGrid with WCF + LINQ to SQL</font></u></a>: 这个15分钟的录像博客示范了如何在服务器上建造一个LINQ to SQL对象模型，然后用WCF将其发布，然后示范了如何建造一个使用了新的Silverlight DataGrid控件的Silverlight客户端，该客户端调用WCF服务获取LINQ to SQL数据，将其绑定到DataGrid上。 </p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2008/03/27/10290.aspx" target=_blank mce_href="http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2008/03/27/10290.aspx"><u><font color=#0000ff>Simple Editing of Web Service Data in a DataGrid:</font></u></a> Mike Taulty有一篇好贴，展示如何在服务器上建造WCF服务，然后从Silverlight 2客户端使用它来获取数据，绑定到DataGrid，允许用户更新数据行，添加/删除数据行，然后使用 Silverlight 2 Beta1将数据保存到服务器上。 </p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://mattberseth.com/blog/2008/03/sorting_with_silverlight_2s_da.html" target=_blank mce_href="http://mattberseth.com/blog/2008/03/sorting_with_silverlight_2s_da.html"><u><font color=#0000ff>Sorting with Silverlight 2's DataGrid Control:</font></u></a> Silverlight 2 Beta1中的DataGrid控件还没有内置的列数据排序支持（将在Beta2中提供），但这并没有阻止Matt Berseth！ 在这个贴子里，他展示了如何使用一个定制的header列方法来实现排序。也看一下Matt的 <a href="http://mattberseth.com/blog/2008/03/a_quick_look_at_silverlight_2s.html" mce_href="http://mattberseth.com/blog/2008/03/a_quick_look_at_silverlight_2s.html"><u><font color=#0000ff>这里</font></u></a>的贴子，该文提供了一个DataGrid测试页面，展示了目前DataGrid的若干特性。 </p>
    </li>
</ul>
<h3><u>Web Services 和 Networking</u></h3>
<ul>
    <li>
    <p><a href="http://blogs.msdn.com/silverlight_sdk/archive/2008/03/27/web-services-and-silverlight.aspx" target=_blank mce_href="http://blogs.msdn.com/silverlight_sdk/archive/2008/03/27/web-services-and-silverlight.aspx"><u><font color=#0000ff>Web Services and Silverlight</font></u></a>: 这个有用的贴子讨论了如何在Silverlight中使用web服务，并且提供了相关快速入门例程和文档的链接。</p>
    </li>
</ul>
<ul>
    <li>
    <p><a href="http://timheuer.com/blog/archive/2008/03/14/calling-web-services-with-silverlight-2.aspx" target=_blank mce_href="http://timheuer.com/blog/archive/2008/03/14/calling-web-services-with-silverlight-2.aspx"><u><font color=#0000ff>Calling Web Services with Silverlight 2 (Part 1)</font></u></a> and <a href="http://timheuer.com/blog/archive/2008/03/19/calling-services-with-silverlight-part-2.aspx" target=_blank mce_href="http://timheuer.com/blog/archive/2008/03/19/calling-services-with-silverlight-part-2.aspx"><u><font color=#0000ff>(Part 2)</font></u></a>: Tim Heuer的几篇精彩贴子，讨论如何使用Silverlight调用web服务。</p>
    </li>
</ul>
<h3><u>Video场景</u></h3>
<ul>
    <li>
    <p><a href="http://sessions.visitmix.com/?selectedSearch=Encoding Video for Microsoft Silverlight&amp;searchPlink=true" target=_blank><u><font color=#0000ff>Encoding Video for Microsoft Silverlight Delivery</font></u></a>: Ben Waggoner在MIX 08上的讲座，讨论编码Silverlight录像的最佳实践。</p>
    </li>
</ul>
<h3><u>Accessibility 和 508相容性</u></h3>
<ul>
    <li>
    <p><a href="http://www.visitmix.com/blogs/Joshua/Silverlight-2-Accessibility-with-Mark-Rideout/" target=_blank mce_href="http://www.visitmix.com/blogs/Joshua/Silverlight-2-Accessibility-with-Mark-Rideout/"><u><font color=#0000ff>Accessibility in Silverlight 2</font></u></a>: Silverlight开发团队的Mark Rideout讨论 Silverlight 2 中的accessibility支持，以及如何使用Silverlight建造与section 508相容的accessible方案。</p>
    </li>
</ul>
<p>希望本文对你有所帮助，</p>
<p>Scott</p>
<img src ="http://www.cnitblog.com/seeyeah/aggbug/42269.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2008-04-11 23:26 <a href="http://www.cnitblog.com/seeyeah/archive/2008/04/11/42269.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>英语技术文章【20080303】</title><link>http://www.cnitblog.com/seeyeah/archive/2008/03/03/40407.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Mon, 03 Mar 2008 15:12:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2008/03/03/40407.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/40407.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2008/03/03/40407.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/40407.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/40407.html</trackback:ping><description><![CDATA[1、<strong>Writing Object-Oriented JavaScript<br></strong>详细介绍用OO的方式写JS<br><a href="http://www.codeproject.com/KB/aspnet/JsOOP1.aspx">http://www.codeproject.com/KB/aspnet/JsOOP1.aspx</a><br><a href="http://www.codeproject.com/KB/aspnet/JsOOP2.aspx">http://www.codeproject.com/KB/aspnet/JsOOP2.aspx</a><br><a href="http://www.codeproject.com/KB/aspnet/JsOOP3.aspx">http://www.codeproject.com/KB/aspnet/JsOOP3.aspx</a><br><br>2、<span class=ArticleTopTitle id=ctl00_ArticleTopHeader_ArticleTitle><strong>Declarative QueryString Parameter Binding</strong><br>使用Attribute绑定QueryString值到变量的方法，Attribute的又一个有趣的使用方法。<br><a href="http://www.codeproject.com/KB/aspnet/WebParameter.aspx">http://www.codeproject.com/KB/aspnet/WebParameter.aspx</a><br><br></span>
<img src ="http://www.cnitblog.com/seeyeah/aggbug/40407.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2008-03-03 23:12 <a href="http://www.cnitblog.com/seeyeah/archive/2008/03/03/40407.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ASP.NET AJAX Controls and Extenders Tutorial</title><link>http://www.cnitblog.com/seeyeah/archive/2008/02/25/40141.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Mon, 25 Feb 2008 15:19:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2008/02/25/40141.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/40141.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2008/02/25/40141.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/40141.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/40141.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Refrence From ..ASP.NET AJAX Controls and Extenders TutorialBy James AshleyIntroductionWhen you open up Visual Studio 2008 to create a project, you will notice that it has two new web temp...&nbsp;&nbsp;<a href='http://www.cnitblog.com/seeyeah/archive/2008/02/25/40141.html'>阅读全文</a><img src ="http://www.cnitblog.com/seeyeah/aggbug/40141.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2008-02-25 23:19 <a href="http://www.cnitblog.com/seeyeah/archive/2008/02/25/40141.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用 Request.QueryString 接受参数时，跟编码有关的一些问题 </title><link>http://www.cnitblog.com/seeyeah/archive/2007/12/23/38008.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Sun, 23 Dec 2007 15:01:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/12/23/38008.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/38008.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/12/23/38008.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/38008.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/38008.html</trackback:ping><description><![CDATA[转载：<a class=singleposttitle id=viewpost_ascx_TitleUrl href="http://blog.joycode.com/ghj/archive/2007/12/07/112382.aspx"><u><font color=#800080>使用 Request.QueryString 接受参数时，跟编码有关的一些问题</font></u></a> <br>
<hr>
<div class=postbody>
<p>&#160;</p>
<p>我们先来看以下几个请求，看a.aspx 页面用Request.QueryString接受到的是啥信息？</p>
<table cellSpacing=2 cellPadding=2 width=609 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=60>&nbsp;</td>
            <td vAlign=top width=237>页面URL</td>
            <td vAlign=top align=middle width=302>Request.QueryString["info"]接受到的值</td>
        </tr>
        <tr>
            <td vAlign=top width=60>案例一</td>
            <td vAlign=top width=237>a.aspx?info=%25</td>
            <td vAlign=top align=middle width=302>
            <p>% </p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=60>案例二</td>
            <td vAlign=top width=237>a.aspx?info=%bc%bc%ca%f5</td>
            <td vAlign=top align=middle width=302>
            <p>????</p>
            </td>
        </tr>
    </tbody>
</table>
<p>情况分析：</p>
<p><strong>案例一</strong></p>
<p>a.aspx?info=%25 为何 Request.QueryString["info"]接受到的值是 % ，而不是 %25，是因为Request.QueryString 替我们在接受到值后，做了一次URL解码。 HttpUtility.UrlDecode("%25")&nbsp; 的计算结果就是 % </p>
<p>上面的这个案例一虽然看起来很简单。但是我们在一些特殊场景时候，就会因为这个而极度郁闷。</p>
<p>比如以下几种情况：</p>
<p>你有一个自己的加密算法，而这个加密算法，某些情况下会计算出带百分号的结果，而这个结果你是要通过URL参数的方式传递给其它页面的。<br>这时候你就苦恼的发现，某些时候某个功能就不能用。</p>
<p>如果解决案例一碰到的情况呢？</p>
<p>解决方案一：</p>
<p><strong>把需要传递的参数传递前作一次 HttpUtility.UrlEncode ，</strong><br>记得是按照 UTF-8 的编码的 UrlEncode 。这样如果我们希望客户端接受到的是 %25&nbsp; 就应该传递的是 %2525 。</p>
<p>切记，不可在接受方每次接受后，自作聪明的都做一次 UrlEncode 。而是在发送方做 UrlEncode 。<br>如果接受方接受后作 UrlEncode 的话，就会出现下面情况：<br>发送方发送 a.aspx?info=%25 ，这时候如果接受方&nbsp; 接受后作 UrlEncode 的话，一切正确<br>发送方发送 a.aspx?info=% ，这时候如果接受方&nbsp; 接受后作 UrlEncode 的话，则就乱了。</p>
<p>另：这套方案中切记， UrlEncode&nbsp; 和 UrlDecode 的次数应该一一对应。不能多一次，也不能少一次。<br>有人就会说，这还会出现次数不对应么？ 比如下面情况，一个不留意就很可能出现次数不对应。而出现不是你所期望的情况。<br>比如我们有这样类似的功能： </p>
<p>a.aspx 页面中，根据传入的 from 参数，自动跳转到 from 参数（用Request.QueryString["from"]来接受这个参数）设置的页面。<br>b.aspx 页面也是同样的逻辑，根据传入的 from 参数（用Request.QueryString["from"]来接受这个参数），自动跳转到指定的页面。<br>c.aspx 页面也是同样的逻辑，根据传入的 from 参数（用Request.QueryString["from"]来接受这个参数），自动跳转到指定的页面。<br><br><br>这样我们就可能书写下面的链接地址：<br>a.aspx?from=b.aspx&nbsp; <br>a.aspx?from=b.aspx?from=c.aspx<br>a.aspx?from=b.aspx?from=c.aspx?from=http://blog.joycode.com/ghj/</p>
<p>下面再复杂一点，我给下面几个链接，其中都有 a 这个参数，请告诉我 a 这个参数是被那个页面接受到了？<br>说明：&nbsp; HttpUtility.UrlEncode("&amp;")&nbsp; == "%26"&nbsp;&nbsp;&nbsp;&nbsp; HttpUtility.UrlEncode("%")&nbsp; == "%25"</p>
<table cellSpacing=0 cellPadding=2 width=576 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=307>地址</td>
            <td vAlign=top align=middle width=267>a 参数会被那个页面接受到</td>
        </tr>
        <tr>
            <td vAlign=top width=307>a.aspx?from=b.aspx?from=c.aspx&amp;a=1</td>
            <td vAlign=top align=middle width=267>a 参数被 a.aspx 页面接受到了</td>
        </tr>
        <tr>
            <td vAlign=top width=307>a.aspx?from=b.aspx?from=c.aspx%26a=1</td>
            <td vAlign=top align=middle width=267>a 参数被 b.aspx 页面接受到了</td>
        </tr>
        <tr>
            <td vAlign=top width=307>a.aspx?from=b.aspx?from=c.aspx%2526a=1</td>
            <td vAlign=top align=middle width=267>a 参数被 c.aspx 页面接受到了</td>
        </tr>
    </tbody>
</table>
<p>如果想不明白，就想想下面这句话<br>每一次用 Request.QueryString 获取参数时候，就作了一次 HttpUtility.UrlDecode。<br></p>
<p>解决方案二：</p>
<p>不用 Request.QueryString ，而是自己实现一个获取查询参数的方法。细节我在案例二讲完后再告诉大家，因为这个解决方案也处理了案例二的一些情况。<br></p>
<p><strong>案例二</strong></p>
<p>a.aspx?info=%bc%bc%ca%f5 传给我们的信息其实是使用 GB2312 编码后的&#8220;技术&#8221; 这两个汉字。<br>不信，你可以用下面表达式计算的结果就是 %bc%bc%ca%f5<br>HttpUtility.UrlEncode("技术", System.Text.Encoding.GetEncoding("GB2312")) </p>
<p>ASP.net 系统内部，在处理 Request.QueryString 等情况时候，都是使用的 UTF-8 的编码，我们如果不存在多系统并存的问题时候，这个问题一点都不存在。<br>但是，当需要跟其它系统交互式后，问题就可能会出现。<br>如果你不了解案例二这里情况时，你就会被这个问题苦恼死。</p>
<p>比如下面这两个地址提到的问题：</p>
<p>ASP.net中的Server.UrlEncode函数和ASP中的Server.URLEncode函数返回的值竟然不一样<br><a title=http://blog.joycode.com/ghj/archive/2003/10/20/2992.aspx href="http://blog.joycode.com/ghj/archive/2003/10/20/2992.aspx"><u><font color=#0000ff>http://blog.joycode.com/ghj/archive/2003/10/20/2992.aspx</font></u></a>
<p>PHP与aspx之间中文通过URL如何传递？ <br><a title=http://topic.csdn.net/u/20071018/19/8a4066af-a08c-4214-91e9-ed4caf977e07.html href="http://topic.csdn.net/u/20071018/19/8a4066af-a08c-4214-91e9-ed4caf977e07.html"><u><font color=#0000ff>http://topic.csdn.net/u/20071018/19/8a4066af-a08c-4214-91e9-ed4caf977e07.html</font></u></a></p>
<p>案例二的解决方案<br><strong>使用带编码的 HttpUtility.ParseQueryString 函数</strong></p>
<p>就是采用类似下面代码的方式，来获得指定格式编码的查询文本参数。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Collections.Specialized.NameValueCollection nv =<br>System.Web.HttpUtility.ParseQueryString(Request.Url.Query, System.Text.Encoding.GetEncoding("GB2312")); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Response.Write(nv["Tag"]);
<p>&nbsp;</p>
<p>要说我为啥知道上面几种解决方案，是因为我用 Reflector 看了 Request.QueryString 的实现代码。在查看代码时候，我们会看到这样一个 internal 方法：<br>System.Web.HttpValueCollection 类的内部方法：<br>internal void FillFromString(string s, bool urlencoded, Encoding encoding)</p>
<p>这个内部方法实现了，按需解密查询参数的功能，但是遗憾的是，在QueryString 的处理函数中，强制指定了解析 QueryString 时，必须作一次 HttpUtility.UrlDecode。参看如下代码：</p>
<p>public static NameValueCollection ParseQueryString(string query, Encoding encoding)<br>{<br>&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp; return new HttpValueCollection(query, false, true, encoding);<br>}</p>
<p>如果我们不想采用案例一的解决方案一，我们就需要自己写一个解析查询信息的代码。我们完全可以照抄 System.Web.HttpValueCollection 类的 internal void FillFromString(string s, bool urlencoded, Encoding encoding) 方法来改写。但郁闷的是：如果你用 Reflector 察看这个函数的实现时候，Reflector 出来的代码是错误的。正确的方法如下：是在施凡帮助下完成的。</p>
<p><strong>自己实现从 URL 查询文本 Query 中解析出我们自己需要的文本的方法</strong></p>
<p>/// &lt;summary&gt;<br>/// 根据 URL 中的 查询文本 Query 解析成一个 NameValueCollection<br>/// 在装配脑袋帮助下 郭红俊 改编自 System.Web.HttpValueCollection 类的内部方法：<br>/// internal void FillFromString(string s, bool urlencoded, Encoding encoding)<br>/// &lt;/summary&gt;<br>/// &lt;param name="query"&gt;需要解析的查询文本&lt;/param&gt;<br>/// &lt;param name="urlencoded"&gt;解析文本时候是否需要URL解码&lt;/param&gt;<br>/// &lt;param name="encoding"&gt;解析文本时候，按照那种URL编码进行解码&lt;/param&gt;<br>/// &lt;returns&gt;&lt;/returns&gt;<br>public static NameValueCollection FillFromString(string query, bool urlencoded, Encoding encoding)<br>{<br>&nbsp;&nbsp;&nbsp; NameValueCollection queryString = new NameValueCollection();<br>&nbsp;&nbsp;&nbsp; if (string.IsNullOrEmpty(query))<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return queryString;<br>&nbsp;&nbsp;&nbsp; } </p>
<p>&nbsp;&nbsp;&nbsp; // 确保 查询文本首字符不是 ?<br>&nbsp;&nbsp;&nbsp; if (query.StartsWith("?"))<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; query = query.Substring(1, query.Length - 1);<br>&nbsp;&nbsp;&nbsp; }
<p>&nbsp;&nbsp;&nbsp; int num1 = (query != null) ? query.Length : 0;<br>&nbsp;&nbsp;&nbsp; // 遍历每个字符<br>&nbsp;&nbsp;&nbsp; for (int num2 = 0; num2 &lt; num1; num2++)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int num3 = num2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int num4 = -1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (num2 &lt; num1)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; switch (query[num2])<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case '=':<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (num4 &lt; 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; num4 = num2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case '&amp;':<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto BREAKWHILE;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; num2++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
<p>&nbsp;&nbsp;&nbsp; BREAKWHILE:
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string name = null;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string val = null;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (num4 &gt;= 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name = query.Substring(num3, num4 - num3);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; val = query.Substring(num4 + 1, (num2 - num4) - 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; val = query.Substring(num3, num2 - num3);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (urlencoded)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; queryString.Add(HttpUtility.UrlDecode(name, encoding), HttpUtility.UrlDecode(val, encoding));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; queryString.Add(name, val);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ((num2 == (num1 - 1)) &amp;&amp; (query[num2] == '&amp;'))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; queryString.Add(null, string.Empty);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }
<p>&nbsp;&nbsp;&nbsp; return queryString;
<p>}
<p>用上面的代码，我们就可以按需解析自己需要的查询参数，而不是受限的使用Request.QueryString 。
<p><strong>小结</strong>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Request.QueryString 替我们件事情：每次接受到参数后，都做 UrlEncode ，并且是按照 UTF-8编码做的 UrlEncode 。 这在大多数情况下没有任何问题，但是一些情况下，会给我们带来麻烦，本文就是分析这些可能给我们带来麻烦的场景，以及解决方法。
<p><strong>参考资料：</strong>
<p>使用 Reflector ; 查看代码时候，碰到的一个Reflector 的bug <br><a title=http://blog.joycode.com/ghj/archive/2006/12/06/88646.aspx href="http://blog.joycode.com/ghj/archive/2006/12/06/88646.aspx"><u><font color=#0000ff>http://blog.joycode.com/ghj/archive/2006/12/06/88646.aspx</font></u></a>
<p>解密不同编码的的参数。 <br><a title=http://blog.joycode.com/ghj/archive/2006/04/19/74894.aspx href="http://blog.joycode.com/ghj/archive/2006/04/19/74894.aspx"><u><font color=#0000ff>http://blog.joycode.com/ghj/archive/2006/04/19/74894.aspx</font></u></a></p>
</div>
<img src ="http://www.cnitblog.com/seeyeah/aggbug/38008.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-12-23 23:01 <a href="http://www.cnitblog.com/seeyeah/archive/2007/12/23/38008.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ASP.NET HttpModule for handling session end with StateServer</title><link>http://www.cnitblog.com/seeyeah/archive/2007/12/14/37713.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Fri, 14 Dec 2007 15:56:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/12/14/37713.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/37713.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/12/14/37713.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/37713.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/37713.html</trackback:ping><description><![CDATA[<p><span class=ArticleTopDescr id=ctl00_ArticleTopHeader_ArticleDescr>This article explains how to manage sessions ending when using the ASP.NET StateServer (which does not fire the Session_End event)</span> <br><br><u><font color=#0000ff>Link:<a href="http://www.codeproject.com/KB/aspnet/SessionEndStatePersister.aspx">http://www.codeproject.com/KB/aspnet/SessionEndStatePersister.aspx</a></font></u></p>
<img src ="http://www.cnitblog.com/seeyeah/aggbug/37713.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-12-14 23:56 <a href="http://www.cnitblog.com/seeyeah/archive/2007/12/14/37713.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ASP.NET MVC Overview 【From ASP.NET3.5 Extensive QuickStart】</title><link>http://www.cnitblog.com/seeyeah/archive/2007/12/10/37557.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Mon, 10 Dec 2007 13:26:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/12/10/37557.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/37557.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/12/10/37557.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/37557.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/37557.html</trackback:ping><description><![CDATA[链接：<a href="http://quickstarts.asp.net/3-5-extensions/mvc/MVCOverview.aspx">http://quickstarts.asp.net/3-5-extensions/mvc/MVCOverview.aspx</a><br>我的翻译：<a class=titlelink id=Editor_Results_rprSelectionList_ctl01_LinkTitle href="http://www.cnitblog.com/seeyeah/articles/37563.html"><font style="COLOR: #0000ff" color=#000000>ASP.NET MVC 概述</font></a><br>
<hr>
<h2>Introduction</h2>
<p>The Model View Controller (MVC) architectural pattern separates an application into three main components: the model, the view, and the controller. The ASP.NET MVC framework provides an alternative to the ASP.NET Web-forms pattern for creating MVC-based Web applications. The ASP.NET MVC framework is a lightweight, highly testable presentation framework that (as with Web-forms-based applications) is integrated with existing ASP.NET features, such as master pages and membership-based authentication. The MVC framework is defined in the <span class=keyword>System.Web.Mvc</span> namespace and is a fundamental, supported part of the <span class=keyword>System.Web</span> namespace. </p>
<p>MVC is a standard design pattern that many developers are familiar with. Some types of Web applications will benefit from the MVC framework. Others will continue to use traditional ASP.NET application pattern that is based on Web forms and postbacks. Other types of Web applications will combine the two approaches; neither approach precludes the other.</p>
<p>The MVC framework does not use view state or server-based forms. This makes it ideal for developers who want full control over the behavior of an application. </p>
<p>On the other hand, Web forms-based applications support an event model that preserves state over HTTP, which benefits line-of-business Web application development. The Web-forms-based application provides dozens of events that are supported in hundreds of server controls. MVC uses a Front Controller pattern that enables you design an application that completely separates the tasks of presentation, business logic, and data access, and that supports a rich routing infrastructure. Web forms applications use a page-controller pattern that adds functionality to individual pages. These two patterns can be mixed as needed.</p>
<p>The MVC components are as follows:</p>
<ul>
    <li>
    <p><strong>Models</strong> - Model objects are the parts of your application that implement the domain logic. Often, model objects also retrieve and store model state in a database. For example, a Product object might retrieve information from a database, operate on it, and then write updated information back to a Products table in SQL Server. </p>
    <div class=alert><strong>note</strong>
    <p>In smaller applications, the model is often a conceptual separation rather than a physical one. For example, if the application only reads a data set and sends it to the view, the application does not have a physical model layer and associated classes. In that case, the data set takes on the role of a model object.</p>
    </div>
    <li>
    <p><strong>Views</strong> - Views are the components that display the application's user interface (UI). Typically, this UI is created from the model data. An example would be an edit view of a Products table that displays text boxes, drop-down lists, and check boxes based on the current state of a Product object.</p>
    <li>
    <p><strong>Controllers</strong> - Controllers are the components that handle user interaction, manipulate the model, and ultimately select a view to render that displays UI. In an MVC application, the view only displays information; the controller handles and responds to user input and interaction. For example, the controller handles query-string values, and passes these values to the model, which in turn queries the database by using the values.</p>
    </li>
</ul>
<p>The MVC pattern helps you to create applications that separate the different aspects of the application (input logic, business logic, and UI logic), while providing a loose coupling between these elements. The pattern specifies where each kind of logic should exist in the application. The UI logic belongs in the view. Input logic belongs in the controller. Business logic belongs in the model. This separation helps you manage complexity when you build an application, because it enables you to focus on one aspect of the implementation at a time. For example, you can focus on the view without depending on the business logic.</p>
<p>In addition to managing complexity, the MVC pattern makes testing applications easier than testing a traditional ASP.NET Web application. For example, in a traditional ASP.NET Web application, a single class is used both to display output and to respond to user input. Writing automated tests for traditional ASP.NET applications can be complex, because to test an individual page, you must instantiate the page class, all its child controls, and additional dependent classes in the application. Because so many classes are instantiated to run the page, it can be hard to write tests that focus exclusively on individual portions of the application. Tests for traditional ASP.NET applications can therefore be more difficult to implement than test within an MVC application. Moreover, tests within a traditional ASP.NET application require a Web server. The MVC framework decouples the components and makes heavy use of interfaces, which makes it possible to test individual components in isolation from the rest of the framework.</p>
<p>The loose coupling between the three main components of an MVC application also promotes parallel development. For instance, one developer can work on the view, a second developer can work on the controller logic, and a third developer can focus on the business logic in the model.</p>
<h2 class=subsectionTitle>Features of the ASP.NET MVC Framework</h2>
<p>The ASP.NET MVC framework provides the following features:</p>
<ul>
    <li>
    <p>Separation of application tasks (input logic, business logic, and UI logic), testability, and test-driven development (TDD) by default. All core contracts within the MVC framework are interface-based and can be tested by using mock objects. You can unit-test the application without having to run the controllers in an ASP.NET process, which makes unit testing fast and flexible. You can use any unit-testing framework that is compatible with the .NET Framework.</p>
    <li>
    <p>An extensible and pluggable framework. The components of the ASP.NET MVC framework are designed so that they can be easily replaced or customized. You can plug in your own view engine, URL routing policy, action-method parameter serialization, and other components. The ASP.NET MVC framework also supports using existing dependency injection and Inversion of Control (IOC) container models.</p>
    <li>
    <p>A powerful URL-mapping component that lets you build applications that have comprehensible and searchable URLs. URLs do not have to include file-name extensions, and are designed to support URL naming patterns that work well for search engine optimization (SEO) and representational state transfer (REST) addressing. For example, you could map the <span class=code>/products/edit/4</span> URL to the <span class=code>Edit</span> action of a <span class=code>ProductsController</span> class in your project, or map the <span class=code>/Blogs/YourSite/10-10-2007/SomeTopic/</span> URL to a <span class=code>DisplayPost</span> action of a <span class=code>BlogEngineController</span> class. URLs do not have to reference file names.</p>
    <li>
    <p>Support for using the markup in existing ASP.NET pages (.aspx files), user controls (.ascx files), and master pages (.master files) markup files as view templates. You can use existing ASP.NET features with the ASP.NET MVC framework, such as nested master pages, in-line expressions (<span class=code>&lt;%=&nbsp;</span><span class=code>%&gt;)</span>, declarative server controls, templates, data-binding, localization, and so on.</p>
    <li>
    <p>Support for existing ASP.NET features. ASP.NET MVC lets you use features such as forms and Windows authentication, URL authorization, membership and roles, output and data caching, session and profile state management, health monitoring, configuration system, the provider architecture, and other areas of ASP.NET.</p>
    </li>
</ul>
<h2 class=subsectionTitle>Background</h2>
<p>In an ASP.NET Web Site, URLs typically map to files stored on disk (typically .aspx files). These .aspx files include code or markup that is processed in order to respond to the request.</p>
<p>The ASP.NET MVC framework maps URLs to server code in a different way. Instead of mapping URLs to ASP.NET pages or handlers, the framework maps URLs directly to controller classes. Controller classes handle incoming requests, such as user input and interactions, and also execute appropriate application and data logic based on user input. A controller class typically calls a separate view component that generates the HTML output as the response.</p>
<p>The ASP.NET MVC framework separates the model, view, and controller components. The model component maintains state by typically persisting data inside a database. The view component is selected by the controller and renders the appropriate UI. By default, the ASP.NET MVC framework uses the existing ASP.NET page (.aspx), master page (.master), and user control (.ascx) types for rendering to the browser. The controller component locates the appropriate action method in the controller, gets the values to use as the action method's arguments, handles all errors that might occur during the execution of the action, and renders the requested view. Each set of components exists in a separate subdirectory of an MVC Web application project. </p>
<h2 class=subsectionTitle>URL Mapping</h2>
<p>The ASP.NET MVC framework includes a URL-mapping engine that provides flexibility for mapping URLs to controller classes. You can use the mapping engine to define routing rules that the ASP.NET MVC framework uses to evaluate incoming URLs and to select a controller to execute. You can also have the routing engine automatically parse variables defined in the URL and have the ASP.NET MVC framework pass these to the controller as parameter arguments. </p>
<h2 class=subsectionTitle>The MVC Framework and Postbacks</h2>
<p>ASP.NET MVC framework does not use the traditional ASP.NET postback model for interactions with the server. Instead, all end-user interactions are routed to a controller class. This maintains separation between UI logic and business logic and helps testability. As a result, ASP.NET view state and ASP.NET page life-cycle events are not integrated with MVC-based views.</p>
<h2 class=subsectionTitle>Creating an ASP.NET MVC Application</h2>
<p>The ASP.NET MVC framework includes Visual Studio project templates that let you create Web applications that are structured to support the MVC pattern. The <span class=ui>MVC Web Application</span> template is used to create a new Web application that is configured with the subdirectories, templates, and configuration-file entries that are required for an ASP.NET MVC application.</p>
<p>By default, when you create a new Web application by using this template, Visual Studio creates a solution and adds two projects to the solution. The first project is a Web project where you can implement your application. The second project is a testing project that you can write unit tests for your MVC components.</p>
<p>You can use any unit-testing framework that is compatible with the .NET Framework to test ASP.NET MVC applications. Visual Studio 2008 Professional includes built-in testing project support for MSTest.</p>
<h2 class=subsectionTitle>Understanding Web Application MVC Project Structure</h2>
<p>When you create an ASP.NET MVC Web application project, MVC components are separated based on the following project folders:</p>
<ul>
    <li>
    <p>Views folder. The Views folder is the recommended location for views. The views use .aspx, .ascx, and .master files, as well as any other files that are related to rendering views. The Views directory contains a folder for each controller that is named with the controller prefix. When loading a view, by default the ASP.NET MVC framework will look for an .aspx file with the requested view name in the Views\controllerName folder. There is also a default directory named Common that does not correspond to any controller. This is a useful location to put master pages, scripts, CSS files, and other files that are used when rendering a view. </p>
    <li>
    <p>Controller folder. The Controllers folder is the recommended location for controllers. </p>
    <li>
    <p>Model folder. The Models folder is the recommended location for classes that represent the application model for your MVC Web application. Typically, this includes code that defines the logic for interaction with the data store.</p>
    <li>
    <p>App_Data. The physical store for data. This folder has the same role as it does in Web-forms based ASP.NET Web applications.</p>
    </li>
</ul>
<p>In addition to the folders listed previously, a MVC Web application uses the following application elements:</p>
<ul>
    <li>
    <p>Global.asax and Global.asax.cs (or Global.asax.vb in Visual Basic). In Global.asax.cs, routes are initialized in the <span class=keyword>Application_Start</span> method. The following example shows a typical Global.asax file that includes default routing logic.</p>
    <div class=code>
    <pre>public class Global : System.Web.HttpApplication {
    protected void Application_Start(object sender, EventArgs e) {
    RouteTable.Routes.Add(new Route {
    Url = "[controller]/[action]/[id]",
    Defaults = new { action = "Index", id = (string)null },
    RouteHandler = typeof(MvcRouteHandler)
    });
    }
    }</pre>
    </div>
    <li>
    <p>Configuration. The MVC Web application Web.config file registers HTTP handlers and HTTP modules. The <span class=keyword>httpHandlers</span> section registers the <span class=keyword>MvcHandler</span> class, which routes requests to the appropriate controller. The following example shows the <span class=keyword>httpHandlers</span> section for an ASP.NET MVC application.</p>
    <div class=code>
    <pre>&lt;httpHandlers&gt;
    &lt;add verb="*" path="*.mvc" type="System.Web.Mvc.MvcHandler,
    System.Web.Extensions, Version=3.6.0.0, Culture=neutral,
    PublicKeyToken=31BF3856AD364E35"/&gt;
    &lt;/httpHandlers&gt;</pre>
    </div>
    <p>The <span class=keyword>httpModules</span> section registers the <span class=keyword>UrlRoutingModule</span> class, which parses the URL and routes requests to the appropriate handler. This entry enables the application to host MVC and non-MVC handlers in the same project. The following example shows the <span class=keyword>httpModules</span> section for an ASP.NET MVC application.</p>
    <div class=code>
    <pre>&lt;httpModules&gt;
    &lt;add name="UrlRoutingModule"
    type="System.Web.Mvc.UrlRoutingModule,
    System.Web.Extensions, Version=3.6.0.0, Culture=neutral,
    PublicKeyToken=31BF3856AD364E35" /&gt;
    &lt;/httpModules&gt;</pre>
    </div>
    </li>
</ul>
<p>When you create an MVC Web application project, the solution includes a Test project. You can create tests by using the MVC templates and create mock implementations of the intrinsic interfaces.</p>
<h2 class=subsectionTitle>Understanding the MVC Project Execution Process</h2>
<p>Requests to a Web site that is built by using the MVC framework are routed through an ASP.NET HTTP module and to an HTTP handler.</p>
<div class=alert><strong>note</strong>
<p>When an ASP.NET MVC Web application runs in IIS 7, no file name extension is required for MVC projects. However, in IIS6, the handler requires that you map the .mvc file name extension to the ASP.NET ISAPI DLL.</p>
</div>
<p>The module and handler are the entry points to the ASP.NET MVC framework and perform the following actions:</p>
<ul>
    <li>
    <p>Select the appropriate controller in an MVC Web application.</p>
    <li>
    <p>Obtain a specific controller instance.</p>
    <li>
    <p>Call the controller's <span class=keyword>Execute</span> method.</p>
    </li>
</ul>
<p>The following table lists the stages of execution for an MVC Web project:</p>
<table class=authoredTable cellSpacing=0 cellPadding=0>
    <tbody>
        <tr>
            <td>
            <p>Stage</p>
            </td>
            <td>
            <p>Details</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>Initial request</p>
            </td>
            <td>
            <p>In the Global.asax file, routes are added to the <span class=keyword>RouteTable</span> object.</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>Routing</p>
            </td>
            <td>
            <p>The <span class=keyword>UrlRoutingModule</span> module creates the <span class=keyword>RouteData</span> object from the matched <span class=keyword>Route</span> object in the <span class=keyword>RouteTable</span> instance. Route data is used to determine which controller to request, and which action to invoke.</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>Map to controller</p>
            </td>
            <td>
            <p>The <span class=keyword>MvcRouteHandler</span> handler attempts to create the type name for the controller, based on data in the <span class=keyword>RouteData</span> instance.</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>Invoke controller builder</p>
            </td>
            <td>
            <p>The handler calls the global static <span class=keyword>CreateController</span> method of the <span class=keyword>ControllerBuilder</span> class, obtaining an <span class=keyword>IController</span> instance.</p>
            <p>If an <span class=keyword>IController</span> instance is not returned, the handler returns an HTTP 500 error that indicates a server error. </p>
            </td>
        </tr>
        <tr>
            <td>
            <p>Create controller</p>
            </td>
            <td>
            <p>The <span class=keyword>ControllerBuilder</span> instance creates a new controller directly, or uses an <span class=keyword>IControllerFactory</span> object to create the controller.</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>Execute controller</p>
            </td>
            <td>
            <p>The <span class=keyword>MvcHandler</span> instance is added to the context and calls the controller's <span class=keyword>Execute</span> method.</p>
            </td>
        </tr>
    </tbody>
</table>
<h2 class=subsectionTitle>Example</h2>
<p>The following code shows the default structure that is created by Visual Studio when you create a new MVC Web application.</p>
<div class=live-code>
<div id=ctl00_MainBody_ctl00_LanguageChoicesDiv><span>Language:</span> <input id=ctl00_MainBody_ctl00_ctl01 onclick="document.getElementById('ctl00_MainBody_ctl00_ViewButton').href='/3-5-extensions/ViewSample.aspx?sref=MVCIntroProject%23MvcApplication%5CDefault.aspx&amp;lang=cs';" type=radio CHECKED value=ctl01 name=ctl00$MainBody$ctl00$LanguageChoices><label for=ctl00_MainBody_ctl00_ctl01>C#</label> </div>
<a class=view-button id=ctl00_MainBody_ctl00_ViewButton href="http://quickstarts.asp.net/3-5-extensions/ViewSample.aspx?sref=MVCIntroProject%23MvcApplication%5CDefault.aspx&amp;lang=cs" target=_blank><u><font color=#0000ff><span>View</span> </font></u></a></div>
<p class=disclaimer>This topic is ASP.NET 3.5 Extensions Preview pre-release documentation and is unsupported by Microsoft. Blank topics are included as placeholders and existing content is subject to change in future releases. To provide feedback or ask questions about the release, please go to the <a href="http://go.microsoft.com/fwlink/?LinkID=105855"><u><font color=#0000ff>ASP.NET 3.5 Extensions Preview forums</font></u></a>.</p>
<img src ="http://www.cnitblog.com/seeyeah/aggbug/37557.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-12-10 21:26 <a href="http://www.cnitblog.com/seeyeah/archive/2007/12/10/37557.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>英语技术文章【20071208】</title><link>http://www.cnitblog.com/seeyeah/archive/2007/12/08/37494.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Sat, 08 Dec 2007 09:06:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/12/08/37494.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/37494.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/12/08/37494.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/37494.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/37494.html</trackback:ping><description><![CDATA[<p>关于ASP.NET的ViewState讨论<strong><br>
<hr>
<br>1、Keep ASP.NET ViewState out of ASPX Page for Performance Improvement</strong><br>How you could improve performance of your ASP.NET projects, keeping ViewState on the server instead of on the ASPX page.&nbsp;<br><a href="http://www.codeproject.com/KB/viewstate/ServerViewState.aspx">http://www.codeproject.com/KB/viewstate/ServerViewState.aspx</a>&nbsp;<br><br><strong>2、DataGrid's ViewState optimization</strong><br><span class=ArticleTopDescr id=ctl00_ArticleTopHeader_ArticleDescr>How to reduce DataGrid's ViewState size, while maintaining all DataGrid's functionalities.</span> <br><a href="http://www.codeproject.com/KB/viewstate/DataGridViewState.aspx">http://www.codeproject.com/KB/viewstate/DataGridViewState.aspx</a><br><br><strong>3、Retaining State for Dynamically Created Controls in ASP.NET applications</strong><br>When creating controls on the fly on your ASP.NET pages viewstate is a bit hard to track. Learn how it works and how to help your application remembers where to put what. <br><a href="http://www.codeproject.com/KB/viewstate/retainingstate.aspx">http://www.codeproject.com/KB/viewstate/retainingstate.aspx</a><br><br><strong>4、<span class=ArticleTopTitle id=ctl00_ArticleTopHeader_ArticleTitle>ViewState Compression</span></strong>
<p><span class=ArticleTopDescr id=ctl00_ArticleTopHeader_ArticleDescr>How to compress the ViewState of ASP.NET pages and save bandwidth.</span> <br><a href="http://www.codeproject.com/KB/viewstate/ViewStateCompression.aspx">http://www.codeproject.com/KB/viewstate/ViewStateCompression.aspx</a><br></p>
<img src ="http://www.cnitblog.com/seeyeah/aggbug/37494.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-12-08 17:06 <a href="http://www.cnitblog.com/seeyeah/archive/2007/12/08/37494.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Read Asp.net Request Param and Session dynamically into properties</title><link>http://www.cnitblog.com/seeyeah/archive/2007/12/02/37186.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Sun, 02 Dec 2007 08:02:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/12/02/37186.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/37186.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/12/02/37186.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/37186.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/37186.html</trackback:ping><description><![CDATA[P.S.<br>在页面获取Session、Request Param等值的一个有趣的方法，有点意思&#8230;&#8230; <img height=20 src="http://www.cnitblog.com/Emoticons/QQ/cool.gif" width=20 border=0><br>
<hr>
摘自：<a href="http://www.jigar.net/articles/viewhtmlcontent261.aspx">http://www.jigar.net/articles/viewhtmlcontent261.aspx</a><br><br>Read Asp.net Request Param and Session dynamically into properties<br><br><a href="http://www.jigar.net/viewcontent.ashx?contentid=265"><u><font color=#800080>Download Source Code</font></u></a> |&nbsp; <a href="http://www.jigar.net/demo/propertyreader.aspx"><u><font color=#800080>Run Sample</font></u></a><br><br>Article shows how you can use Attributes and Reflection to dynamically populate property with value from param, session or context without writing any addition code.&nbsp; I have used this technique in past and I hope you will also find it useful.<br>
<h3>Why do I need this?</h3>
I am a lazy programmer, I won&#8217;t write a code that I do not have to, I also believe that amount of mistakes that I make while writing code is directly proportional to number of lines of the code that I write.<br><br>Why would I write following code?
<pre><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">int</span> id;
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">string</span> userName;
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">void</span> Page_Init(object sender, EventArgs e){
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">if</span> (!<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">string</span>.IsNullOrEmpty(Request.QueryString[<span style="FONT-SIZE: 11px; COLOR: rgb(102,102,102); FONT-FAMILY: Courier New; BACKGROUND-COLOR: rgb(237,237,237)">"id"</span>])){
id <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">int</span>.Parse(Request.QueryString[<span style="FONT-SIZE: 11px; COLOR: rgb(102,102,102); FONT-FAMILY: Courier New; BACKGROUND-COLOR: rgb(237,237,237)">"id"</span>]);
}
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">if</span>(Request.Cookies[<span style="FONT-SIZE: 11px; COLOR: rgb(102,102,102); FONT-FAMILY: Courier New; BACKGROUND-COLOR: rgb(237,237,237)">"userName"</span>] !<span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">null</span> &amp;&amp;
Request.Cookies[<span style="FONT-SIZE: 11px; COLOR: rgb(102,102,102); FONT-FAMILY: Courier New; BACKGROUND-COLOR: rgb(237,237,237)">"userName"</span>].Value !<span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">null</span>){
userName <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> Request.Cookies[<span style="FONT-SIZE: 11px; COLOR: rgb(102,102,102); FONT-FAMILY: Courier New; BACKGROUND-COLOR: rgb(237,237,237)">"userName"</span>].Value;
}
}
</pre>
If I can just write following.
<pre>[PageProperty]
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">string</span> id;
[PageProperty(ReadFrom=DictionaryType.Cookie)]
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">string</span> userName;</pre>
<h3>Lets define what are we interested in reading</h3>
Most common place where we need to read data is query string dictionary but we will also read from additional places like Form, Session, Cookie, Headers and Dictionary that comes with Context. We will create flag that will define places that we can read data from.
<pre>[flags]
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">public</span> enum DictionaryType
{
QueryString <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> 1,
Form <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> 2,
Session <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> 4,
Context <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> 8,
Cookie <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> 16,
Header <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> 32
}</pre>
<h3>Next step will be to create Attribute class for our properties and fields.</h3>
<br>Attributes provide a powerful method of associating declarative information with code. We need two properties in our attribute <br>
<ol>
    <li>Key which we need to pass to read data from dictionary, If user do not provide this key then we will assume that name of property/field is key.
    <li>Enumeration that defines dictionary to read data from which will default to query string.</li>
</ol>
<pre><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">class</span> PageProperty : Attribute {
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">private</span> DictionaryType readFrom <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> DictionaryType.QueryString;
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">private</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">string</span> key;
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">string</span> Key {
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">get</span> { <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">return</span> key; }
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">set</span> { key <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> value; }
}
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">public</span> DictionaryType ReadFrom {
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">get</span> { <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">return</span> readFrom; }
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">set</span> { readFrom <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> value; }
}
}
</pre>
<p>Once we have above class we can tag fields in our page with above attribute.</p>
<pre>[PageProperty(ReadFrom <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> DictionaryType.Cookie)]
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">int</span> userId;
</pre>
<h3>Reading attributes using reflection and populating values.</h3>
<ol>
    <li>We will use reflection to inspect page class to find all property or public fields with PageProperty Attribute.&nbsp;
    <li>Next step will be to get key and DictionaryType.
    <li>And once we know source and target to populate and we will get information from appropriate dictionary and populate it. </li>
</ol>
We will warp this logic into a small static class with static Read method which takes reference of page object.<br><br>
<pre><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">public</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">static</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">void</span> Read(object pageObject)
{
<span style="FONT-SIZE: 11px; COLOR: green; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">// read for all fields.
</span>      FieldInfo[] fields <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> (pageObject.GetType()).GetFields();
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">foreach</span> (FieldInfo fieldInfo <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">in</span> fields)
{
object[] attributes
<span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> fieldInfo.GetCustomAttributes(typeof(PageProperty), <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">false</span>);
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">if</span> (attributes.Length &gt; 0)
{
PageProperty pageProperty <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> attributes[0] as PageProperty;
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">string</span> key <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> pageProperty.Key;
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">if</span> (key == <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">null</span>) key <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> fieldInfo.Name;
object val <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> getValue(key, pageProperty.ReadFrom);
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">if</span> (val !<span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">null</span>)
{
fieldInfo.SetValue(pageObject
, changeType(val, fieldInfo.FieldType));
}
}
&#8230;&#8230;.
</pre>
<br><br>
<h3>Retriving value from Dictionary.</h3>
Since we are using flags user might specify more than one place to read from, for example user might specify following
<pre>[PageProperty(ReadFrom <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span> DictionaryType.QueryString | DictionaryType.Cookie)]</pre>
To handle this we will go through all selected enumerations and return first with value, so in above mentioned example if query string value was present it will return query string value, otherwise it will return value from cookie.
<pre><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">private</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">static</span> object getValue(<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">string</span> key, DictionaryType readFrom){
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">if</span> ((readFrom &amp; DictionaryType.QueryString)
== DictionaryType.QueryString &amp;&amp;
HttpContext.Current.Request.QueryString[key] !<span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">null</span>)
{
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">return</span> HttpContext.Current.Request.QueryString[key];
} <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">else</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">if</span> ((readFrom &amp; DictionaryType.Form)
== DictionaryType.Form &amp;&amp;
HttpContext.Current.Request.Form[key] !<span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">=</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">null</span>)
{
<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: white">return</span> HttpContext.Current.Request.Form[key];
}
&#8230;..
</pre>
<br>How to use Property Reader in your web page.<br><br>
<ol>
    <li>Drop Jigar.Web.PagePropertyReader.dll in to bin directory of your web project
    <li>In your Page class add following line in Pre_Init Event. <br>PropertyReader.Read(this);<br>Alternatively you can also create PageBase class by inheriting from System.Web.UI.Page and override PreInit event.
    <li>Finally in your page just create public property or field with PageProperty Attribute and you are done.</li>
</ol>
<br>Source code includes sample web application which shows usage of PropertyReader. It also includes test page with different senerios.<br>
<h3>What about performance cost associated with reflection?</h3>
Reflection is definitely going to add some performance cost to your application, you will have to analyze value vs. cost analysis for your application and decide best for you. <br><br>Instead of reflection you might also want to try using Reflection.Emit to improve performance.<br>
<img src ="http://www.cnitblog.com/seeyeah/aggbug/37186.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-12-02 16:02 <a href="http://www.cnitblog.com/seeyeah/archive/2007/12/02/37186.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>英语技术文章 收藏夹</title><link>http://www.cnitblog.com/seeyeah/archive/2007/11/20/36522.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Mon, 19 Nov 2007 16:55:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/11/20/36522.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/36522.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/11/20/36522.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/36522.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/36522.html</trackback:ping><description><![CDATA[<p><strong>1、Asynchronous Method Invocation</strong><br>How to use .NET to call methods in a non-blocking mode.<br><a href="http://www.codeproject.com/csharp/AsyncMethodInvocation.asp">http://www.codeproject.com/csharp/AsyncMethodInvocation.asp</a> <br>我的翻译：<a id=viewpost1_TitleUrl href="http://www.cnitblog.com/seeyeah/articles/36862.html">[2007-12-1]异步调用方法</a><br></p>
<p><strong><br>2、.NET Delegates: A C# Bedtime Story<br></strong>An exploratory story of delegates and events for C# programmers told in the style of a bedtime story.<br><a href="http://www.codeproject.com/csharp/delegate_bedtime.asp">http://www.codeproject.com/csharp/delegate_bedtime.asp</a> <br><strong><br>3、Dependency Injection for Loose Coupling</strong><br>This article describes the use of Dependency Injection, or Inversion-of-Control, to promote loose coupling and convenient unit testing.<br><a href="http://www.codeproject.com/cs/design/DependencyInjection.asp">http://www.codeproject.com/cs/design/DependencyInjection.asp</a> <br>我的翻译：<a id=viewpost1_TitleUrl href="http://www.cnitblog.com/seeyeah/articles/36861.html">[2007-11-26]使用依赖注入解耦</a></p>
<p>&nbsp;</p>
<img src ="http://www.cnitblog.com/seeyeah/aggbug/36522.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-11-20 00:55 <a href="http://www.cnitblog.com/seeyeah/archive/2007/11/20/36522.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>英文技术文章 </title><link>http://www.cnitblog.com/seeyeah/archive/2007/11/18/36472.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Sun, 18 Nov 2007 13:21:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/11/18/36472.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/36472.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/11/18/36472.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/36472.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/36472.html</trackback:ping><description><![CDATA[<hr>
<p><strong style="FONT-SIZE: 18pt">Events and Delegates simplified</strong><br>This article shows you how to design events for your classes.<br><a href="http://www.codeproject.com/csharp/events.asp">http://www.codeproject.com/csharp/events.asp</a><br>我的翻译：<a id=viewpost1_TitleUrl href="http://www.cnitblog.com/seeyeah/articles/36521.html">[2007-11-19]事件和委托的简单说明</a> </p>
<p><strong style="FONT-SIZE: 24pt"><span style="FONT-SIZE: 18pt"><br>ASP.NET Best Practices for High Performance Applications</span></strong><br>This article lists the techniques that you can use to maximize the performance of your ASP.NET applications. It provides common issues, design guidelines, and coding tips to build optimal and robust solutions.<br><a href="http://www.codeproject.com/aspnet/ASPNET_Best_Practices.asp">http://www.codeproject.com/aspnet/ASPNET_Best_Practices.asp</a> </p>
我的翻译：<a id=viewpost1_TitleUrl href="http://www.cnitblog.com/seeyeah/articles/36759.html"><strong>高性能ASP.NET应用程式的最佳实践</strong></a>&nbsp;<br>
<img src ="http://www.cnitblog.com/seeyeah/aggbug/36472.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-11-18 21:21 <a href="http://www.cnitblog.com/seeyeah/archive/2007/11/18/36472.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Could not load file or assembly 'System.Web.Extensions, Version=3.5.0.0 </title><link>http://www.cnitblog.com/seeyeah/archive/2007/11/16/36357.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Fri, 16 Nov 2007 06:05:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/11/16/36357.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/36357.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/11/16/36357.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/36357.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/36357.html</trackback:ping><description><![CDATA[前两天,在编写ASP.NET 2.0的项目时，用到了Ajax Extension，.NET 2.0用的Ajax Extention是 1.0.61025.0 版本的。碰巧在开发的电脑上装了VS2008。问题出现了，再编译2.0这个项目，然后发不到Server上之后，运行Server上的这个WebSite，出现错误<strong>Could not load file or assembly 'System.Web.Extensions, Version=3.5.0.0......'</strong>, 这时候打开Web.Config，会发现在&lt;assemblies&gt;节中有两个配置节：<br>
<p>&nbsp;</p>
<div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">add&nbsp;assembly</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">System.Web.Extensions.Design,&nbsp;Version=3.5.0.0, <br>Culture=neutral,&nbsp;PublicKeyToken=31BF3856AD364E35</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">/&gt;</span><span style="COLOR: rgb(0,0,0)"><br></span><span style="COLOR: rgb(0,0,0)">&lt;</span><span style="COLOR: rgb(0,0,0)">add&nbsp;assembly</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">System.Web.Extensions,&nbsp;Version=3.5.0.0, <br>Culture=neutral,&nbsp;PublicKeyToken=31BF3856AD364E35</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">/&gt;</span></div>
<br>&nbsp;&nbsp;&nbsp; 从上面的代码可以看到，编译器把System.Web.Extensions的3.5的版本编译进来了。把这个版本号改成1.0.61025.0，再编译一次，部署到Server上，运行Server上的WebSite。仍然会出现这个错误。<br>解决这个问题也很简单。到VS2008 Beta 2 的<a title=Download href="http://msdn2.microsoft.com/en-us/vstudio/aa700831.aspx" target=_blank><u><font color=#0000ff>Download</font></u></a><a></a>页面，他们说：<br>
<blockquote><em><strong>IMPORTANT:</strong> After the Beta 2 installation has finished, you should run this </em><a href="http://go.microsoft.com/fwlink/?linkid=95792"><em><u><font color=#0000ff>script</font></u></em></a><em> to ensure that the installation of .NET Framework 3.5 Beta 2 will not affect the development of ASP.NET AJAX 1.0 applications.<br></em></blockquote>&nbsp;&nbsp;&nbsp; 原来我没有下载并运行这个脚本，在网上搜一下发现并非一个人遇到这个问题，我相信并不是所有人读过VS下载页面的这个文章。安装VS2008后，下载这个脚本运行一下，这个问题就解决了。<br><br>
<hr>
引用<a class=singleposttitle id=AjaxHolder_ctl01_TitleUrl href="http://www.cnblogs.com/johnson2007/archive/2007/11/14/958834.html"><u><font color=#800080>Could not load file or assembly 'System.Web.Extensions, Version=3.5.0.0</font></u></a> 
<img src ="http://www.cnitblog.com/seeyeah/aggbug/36357.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-11-16 14:05 <a href="http://www.cnitblog.com/seeyeah/archive/2007/11/16/36357.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Model View Presenter with ASP.NET By Billy McCafferty. </title><link>http://www.cnitblog.com/seeyeah/archive/2007/11/10/36126.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Sat, 10 Nov 2007 13:44:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/11/10/36126.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/36126.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/11/10/36126.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/36126.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/36126.html</trackback:ping><description><![CDATA[引用 <a href="http://www.codeproject.com/aspnet/ModelViewPresenter.asp">http://www.codeproject.com/aspnet/ModelViewPresenter.asp</a><br>
<hr>
<ul class=download>
    <li><a href="http://www.codeproject.com/aspnet/ModelViewPresenter/AMostTrivialExample.zip"><u><font color=#0000ff>Download trivial example of MVP - 14.7 Kb</font></u></a>
    <li><a href="http://www.codeproject.com/aspnet/ModelViewPresenter/EventHandlingMvp.zip"><u><font color=#0000ff>Download simple Event-Handling MVP - 16.0 Kb</font></u></a>
    <li><a href="http://www.codeproject.com/aspnet/ModelViewPresenter/EnterpriseApplicationMvp.zip"><u><font color=#0000ff>Download sample MVP Enterprise Solution - 2605.2 Kb</font></u></a> </li>
</ul>
<h3>Author's note added October 15, 2007 - Suggestion of MVC over MVP</h3>
<p>As we progress as developers, we strive to seek out the "best" way to perform our craft. The chosen methods to attain this lofty goal always bring with them a number of developmental trade-offs. Some techniques may simplify the code but lessen fine grained control while others enable greater power while introducing complexity. <a href="http://www.codeproject.com/aspnet/ModelViewPresenter.asp"><u><font color=#800080>Model-View-Presenter with ASP.NET</font></u></a> is a wonderful example of the latter. MVP aims to facilitate test-driven development of ASP.NET applications with the unfortunate introduction of added complexity. So while developers will be more confient in the quality of their product, MVP hinders their ability to easily maintain code. Yes, the tests will (hopefully) inform the developer that a bug has been introduced, but the inherent complexity of MVP makes it difficult for later team members to become comfortable with the code base and maintain it as project development continues.</p>
<p>Fortunately, as time progresses quickly in our field, resources and tools become available which enhance our ability to write powerful applications while simplifying the coding process itself. The introduction of <a href="http://www.hibernate.org/343.html"><u><font color=#0000ff>NHibernate</font></u></a>, for example, eliminated vast amounts of data-access code while still providing powerful facilities for managing transactions and dynamically querying data. <a href="http://www.castleproject.org/"><u><font color=#0000ff>Castle MonoRail</font></u></a> (and <a href="http://codebetter.com/blogs/jeffrey.palermo/archive/2007/03/16/Big-News-_2D00_-MVC-framework-for-ASP.NET-in-the-works-_2D00_-level-300.aspx"><u><font color=#0000ff>Microsoft's upcoming</font></u></a> <a href="http://codebetter.com/blogs/jeffrey.palermo/archive/2007/10/05/altnetconf-scott-guthrie-announces-asp-net-mvc-framework-at-alt-net-conf.aspx"><u><font color=#0000ff>spin-off</font></u></a> <a href="http://www.hanselman.com/blog/ScottGuMVCPresentationAndScottHaScreencastFromALTNETConference.aspx"><u><font color=#0000ff>of this framework</font></u></a>) now does for writing testable and maintainable .NET web applications what NHibernate (and the upcoming <a href="http://msdn2.microsoft.com/en-us/library/aa697427(vs.80).aspx"><u><font color=#0000ff>LINQ to Entities</font></u></a>) did for ADO.NET. This is not to say that the previous techniques were necessarily wrong, but that they were only applicable considering the developer's toolset that was available at the time of selection.</p>
<p>In adapting to the evolution of our field, it is important for developers to note when an accepted technique is no longer valuable in light of current alternatives. Specifically, MVP was a very powerful technique for writing ground-up, test-driven ASP.NET applications but is no longer a strong candidate for consideration when compared to the time saving benefits and simplicity of Castle MonoRail and Microsoft's upcoming MVC framework. Oddly, it is sometimes difficult to "give up" on something that worked perfectly fine before, but that's the nature of our business ... one tenet that's not likely to change anytime soon.</p>
<p>Of this article, I believe it to be of continued value to those maintaining legacy applications built upon MVP and for those interested in learning a solid domain driven architecture which is <a href="http://www.codeproject.com/aspnet/ModelViewPresenter.asp#ApplicationArchitecture"><u><font color=#800080>discussed further below</font></u></a> and in detail <a href="http://devlicio.us/blogs/billy_mccafferty/archive/2006/10/05/_2800_My_2900_-Default-ASP.NET-Architecture.aspx"><u><font color=#0000ff>in another post</font></u></a>.</p>
<p>In summary, although I still believe that MVP is the best technique for developing ground-up ASP.NET solutions, I believe that there are off-the-shelf frameworks which make the entire job a heck of a lot simpler.</p>
<h2>Introduction</h2>
<p>After years of maintaining thousands of lines of ASP spaghetti code, Microsoft has finally given us a first class web development platform: ASP.NET. ASP.NET instantly brought a basic separation of concerns between presentation and business logic by introducing the code-behind page. Although introduced with good intentions and perfect for basic applications, the code-behind still lacks in a number of aspects when developing enterprise web applications: </p>
<ul>
    <li>The code-behind invites melding the layers of presentation, business logic and data-access code. This occurs because the code-behind page often serves the role of an event handler, a workflow controller, a mediator between presentation and business rules, and a mediator between presentation and data-access code. Giving the code-behind this many responsibilities often leads to unmanageable code. In an enterprise application, a principle of good design is to maintain proper separation of concerns among the tiers and to keep the code-behind as clean as possible. With Model-View-Presenter, we'll see that the code-behind is greatly simplified and kept strictly to managing presentation details.
    <li>Another drawback to the code-behind model is that it is difficult to reuse presentation logic between code-behind pages without enlisting helper/utility classes that consolidate the duplicated code. Obviously, there are times that this provides an adequate solution. However, it often leads to incohesive classes that act more like ASP includes than first class objects. With proper design, every class should be cohesive and have a clear purpose. A class named <em>ContainsDuplicatePresentationCodeBetweenThisAndThat.cs</em> usually doesn't qualify.
    <li>Finally, it becomes nearly prohibitive to properly unit test code-behind pages as they are inseparably bound to the presentation. Options such as NUnitAsp may be used, but they are time consuming to implement and difficult to maintain. They also slow down unit-test performance considerably, where unit tests should always be blazingly fast. </li>
</ul>
<p>Various techniques may be employed to promote a better separation of concerns from the code-behind pages. For example, the <a href="http://www.castleproject.org/index.php/MonoRail" target=_blank><u><font color=#0000ff>Castle MonoRail</font></u></a> project attempts to emulate some of the benefits of Ruby-On-Rails but abandons the ASP.NET event model in the process. <a href="http://mavnet.sourceforge.net/" target=_blank><u><font color=#0000ff>Maverick.NET</font></u></a> is a framework that optionally supports the ASP.NET event model but leaves the code-behind as the controller in the process. Ideally, a solution should be employed that leverages the ASP.NET event model while still allowing the code-behind to be as simple as possible. The Model-View-Presenter pattern does just that without relying on a third party framework to facilitate this goal.</p>
<h2>Model-View-Presenter</h2>
<p>Model-View-Presenter (MVP) is a variation of the Model-View-Controller (MVC) pattern but specifically geared towards a page event model such as ASP.NET. For a bit of history, MVP was originally used as the framework of choice behind <a href="http://www.mimuw.edu.pl/~sl/teaching/00_01/Delfin_EC/Overviews/ModelViewPresenter.htm" target=_blank><u><font color=#0000ff>Dolphin Smalltalk</font></u></a>. The primary differentiator of MVP is that the Presenter implements an Observer design of MVC but the basic ideas of MVC remain the same: the model stores the data, the view shows a representation of the model, and the presenter coordinates communications between the layers. MVP implements an Observer approach with respect to the fact that the Presenter interprets events and performs logic necessary to map those events to the proper commands to manipulate the model. For more reading on MVC vs. MVP, take a look at <a href="http://www.darronschall.com/weblog/archives/000113.cfm"><u><font color=#800080>Darron Schall's concise entry</font></u></a> on the subject. What follows is a detailed examination of MVP in the form of three example projects.</p>
<p>Author's note: Martin Fowler has suggested that MVP be split between two "new" patterns called Supervising Controller and Passive View. Go <a href="http://geekswithblogs.net/billy/archive/2006/07/20/85815.aspx"><u><font color=#0000ff>here</font></u></a> for a very short synopsis of the split. The content described herein is more consistent with Supervising Controller as the View is aware of the Model.</p>
<h2>A most trivial example</h2>
<ul class=download>
    <li><a href="http://www.codeproject.com/aspnet/ModelViewPresenter/AMostTrivialExample.zip"><u><font color=#0000ff>Download trivial example of MVP - 14.7 Kb</font></u></a> </li>
</ul>
<p>In this example project, the client wants a page that shows the current time. Thank goodness they started us off with something easy! The ASPX page that will show the time is the "View." The "Presenter" is responsible for determining the current time -- i.e. the "Model" -- and giving the Model to the View. As always, we start with a unit test: </p>
<div class=precollapse id=premain0 style="WIDTH: 100%"><img id=preimg0 style="CURSOR: pointer" height=9 src="http://www.codeproject.com/images/minus.gif" width=9 preid="0"><span id=precollapse0 style="MARGIN-BOTTOM: 0px; CURSOR: pointer" preid="0"> Collapse</span></div>
<pre lang=cs id=pre0 style="MARGIN-TOP: 0px">[TestFixture]
public class CurrentTimePresenterTests
{
[Test]
public void TestInitView()
{
MockCurrentTimeView view = new MockCurrentTimeView();
CurrentTimePresenter presenter = new CurrentTimePresenter(view);
presenter.InitView();
Assert.IsTrue(view.CurrentTime &gt; DateTime.MinValue);
}
private class MockCurrentTimeView : ICurrentTimeView
{
public DateTime CurrentTime
{
set { currentTime = value; }
<span class=cs-comment>// This getter won't be required by ICurrentTimeView,</span>
<span class=cs-comment>// but it allows us to unit test its value.</span>
get { return currentTime; }
}
private DateTime currentTime = DateTime.MinValue;
}
}</pre>
<p><img height=314 alt="MVP Diagram" src="http://www.codeproject.com/aspnet/ModelViewPresenter/MvpDiagram.gif" width=531></p>
<p>The above unit test, along with the diagram, describes the elements of the MVP relationship. The very first line creates an instance of <code>MockCurrentTimeView</code>. As seen with this unit test, all of the Presenter logic can be unit tested without having an ASPX page, i.e. the View. All that is needed is an object that implements the View interface; accordingly, a mock view is created that stands in place of the "real" view. </p>
<p>The next line creates an instance of the Presenter, passing an object that implements <code>ICurrentTimeView</code> via its constructor. In this way, the Presenter can now manipulate the View. As seen in the diagram, the Presenter only talks to a View interface. It does not work with a concrete implementation directly. This allows multiple Views, implementing the same View interface, to be used by the same Presenter. </p>
<p>Finally, the Presenter is asked to <code>InitView()</code>. This method will get the current time and pass it to the View via a public property exposed by <code>ICurrentTimeView</code>. A unit-test assertion is then made that the <code>CurrentTime</code> on the view should now be greater than its initial value. A more detailed assertion could certainly be made if needed. </p>
<p>All that needs to be done now is to get the unit test to compile and pass! </p>
<h3>ICurrentTimeView.cs: the View interface</h3>
<p>As a first step towards getting the unit test to compile, <em>ICurrentTimeView.cs</em> should be created. This View interface will provide the conduit of communication between the Presenter and the View. In the situation at hand, the View interface needs to expose a public property that the Presenter can use to pass the current time, the Model, to the View. </p>
<pre lang=cs>public interface ICurrentTimeView
{
DateTime CurrentTime { set; }
}</pre>
<p>The View only needs a setter for the current time since it just needs to show the Model, but providing a getter allows <code>CurrentTime</code> to be checked within the unit test. So instead of adding a getter to the interface, it can be added to <code>MockCurrentTimeView</code> and need not be defined in the interface at all. In this way, the exposed properties of the View can be unit tested without forcing extraneous setters/getters to be defined in the View Interface. The described unit test above shows this technique. </p>
<h3>CurrentTimePresenter.cs: the Presenter</h3>
<p>The presenter will handle the logic of communicating with the Model and passing Model values to the View. The Presenter, needed to make the unit test compile and pass, is as follows. </p>
<pre lang=cs>public class CurrentTimePresenter
{
public CurrentTimePresenter(ICurrentTimeView view)
{
if (view == null)
throw new ArgumentNullException(<span class=cpp-string>"view may not be null"</span>);
this.view = view;
}
public void InitView()
{
view.CurrentTime = DateTime.Now;
}
private ICurrentTimeView view;
}</pre>
<p>Once the above items have been developed -- the unit test, the mock view, the view and the presenter -- the unit test will now compile and pass successfully. The next step is creating an ASPX page to act as the real View. As a quick side, take note of the <code>ArgumentNullException</code> check. This is a technique known as "Design by Contract." Putting checks like this all over your code will greatly cut down on tracking down bugs. For more information about Design by Contract, see <a href="http://archive.eiffel.com/doc/manuals/technology/contract"><u><font color=#0000ff>this article</font></u></a> and <a href="http://www.codeproject.com/csharp/designbycontract.asp"><u><font color=#0000ff>this article</font></u></a>.</p>
<h3>ShowMeTheTime.aspx: the View</h3>
<p>The actual View needs to do the following: </p>
<ol>
    <li>The ASPX page needs to provide a means for displaying the current time. As shown below, a simple label will be used for display.
    <li>The code-behind must implement <code>ICurrentTimeView</code>.
    <li>The code-behind needs to create the Presenter, passing itself to the Presenter's constructor.
    <li>After creating the Presenter, <code>InitView()</code> needs to be called to complete the MVP cycle. </li>
</ol>
<h4>The ASPX page</h4>
<pre lang=aspnet>...
&lt;asp:Label id="lblCurrentTime" runat="server" /&gt;
...</pre>
<h4>The ASPX code-behind page</h4>
<pre lang=cs>public partial class ShowMeTheTime : Page, ICurrentTimeView
{
protected void Page_Load(object sender, EventArgs e)
{
CurrentTimePresenter presenter = new CurrentTimePresenter(this);
presenter.InitView();
}
public DateTime CurrentTime
{
set { lblCurrentTime.Text = value.ToString(); }
}
}</pre>
<h3>Is that it?</h3>
<p>In a word, yes. But there is much more to the story! A drawback to the above example is that MVP seems like a lot of work for such little gain. We've gone from having one ASPX page to having a Presenter class, a View interface and a unit testing class. The gain has been the ability to unit test the Presenter, i.e. the ability to conveniently unit test code that would normally be found in the code-behind page. As is the case with trivial examples, the advantages of MVP shine when developing and maintaining enterprise web applications, not when writing "hello world"-like samples. The following topics elaborate the usage of MVP within an enterprise, ASP.NET application. </p>
<h2>MVP within enterprise ASP.NET applications</h2>
<ul>
    <li><strong><a href="http://www.codeproject.com/aspnet/ModelViewPresenter.asp#EncapsulatingViews"><u><font color=#800080>Encapsulating Views with user controls</font></u></a>:</strong> This topic discusses the MVP approach of User Control-as-View.
    <li><strong><a href="http://www.codeproject.com/aspnet/ModelViewPresenter.asp#EventHandlingWithMvp"><u><font color=#800080>Event handling with MVP</font></u></a>:</strong> This topic discusses passing events to the Presenter along with considerations of page validation, IsPostBack and sending messages back to the View.
    <li><strong><a href="http://www.codeproject.com/aspnet/ModelViewPresenter.asp#PageRedirects"><u><font color=#800080>Page redirects with MVP &amp; PageMethods</font></u></a>:</strong> This topic includes how to handle page redirects using PageMethods with a User Control-as-View architecture.
    <li><strong><a href="http://www.codeproject.com/aspnet/ModelViewPresenter.asp#PresentationSecurity"><u><font color=#800080>Presentation security with MVP</font></u></a>:</strong> This topic discusses handling basic security constraints for hiding/showing sections of a View.
    <li><strong><a href="http://www.codeproject.com/aspnet/ModelViewPresenter.asp#ApplicationArchitecture"><u><font color=#800080>Application architecture with MVP</font></u></a> (Advanced):</strong> The whole shebang! This topic puts forth a recommended approach to architecting an MVP, enterprise application complete with an NHibernate data-access layer. </li>
</ul>
<h2><a name=EncapsulatingViews>I. Encapsulating Views with user controls</a></h2>
<p>In the previous, simple example, the ASPX page itself acted as the View. Treating the ASPX in this way was sufficient in that the page had only one simple purpose - to show the current time. But in more representative projects, it is often the case that a single page will have one or more sections of functionality whether they be WebParts, user controls, etc. In these more typical of enterprise applications, it is important to keep functionality logically separated and to make it easy to move/replicate functionality from one area to another. With MVP, user controls can be used to encapsulate Views while the ASPX pages act as "View Initializers" and page redirectors. Extending the previous example, we need only modify the ASPX page to implement the change. This is another benefit of MVP; many changes can be made to the View layer without having to modify the Presenter and Model layers. </p>
<h3>ShowMeTheTime.aspx redux: the View initializer</h3>
<p>With this new approach, using user controls as the view, <em>ShowMeTheTime.aspx</em> is now responsible for the following: </p>
<ol>
    <li>The ASPX page needs to declare the user control which will implement <code>ICurrentTimeView</code>.
    <li>The ASPX code-behind needs to create the Presenter, passing the user control to the Presenter's constructor.
    <li>After giving the View to the Presenter, the ASPX needs to call <code>InitView()</code> to complete the MVP cycle. </li>
</ol>
<h4>The ASPX page</h4>
<pre lang=aspnet>...
&lt;%@ Register TagPrefix="mvpProject"
TagName="CurrentTimeView" Src="./Views/CurrentTimeView.ascx" %&gt;
&lt;mvpProject:CurrentTimeView id="currentTimeView" runat="server" /&gt;
...</pre>
<h4>The ASPX code-behind page</h4>
<pre lang=cs>public partial class ShowMeTheTime : Page
<span class=cs-comment>// No longer implements ICurrentTimeView</span>
{
protected void Page_Load(object sender, EventArgs e)
{
InitCurrentTimeView();
}
private void InitCurrentTimeView()
{
CurrentTimePresenter presenter =
new CurrentTimePresenter(currentTimeView);
presenter.InitView();
}
}</pre>
<h3>CurrentTimeView.ascx: the user control-as-view</h3>
<p>The user control now represents the bare-bones View. It is as "dumb" as it can be - which is exactly how we want a View to be. </p>
<p><strong>The ASCX page</strong> </p>
<pre lang=aspnet>...
&lt;asp:Label id="lblCurrentTime" runat="server" /&gt;
...</pre>
<h4>The ASCX code-behind page</h4>
<pre lang=cs>public partial class Views_CurrentTimeView : UserControl, ICurrentTimeView
{
public DateTime CurrentTime
{
set { lblCurrentTime.Text = value.ToString(); }
}
}</pre>
<h3>Pros and cons of the user Control-as-View approach</h3>
<p>Obviously, the primary drawback to the User Control-as-View approach to MVP is that it adds yet another piece to the equation. The entire MVP relationship is now made up of: unit test, presenter, view interface, view implementation (the user control) and the view initializer (the ASPX page). Adding this additional layer of indirection adds to the overall complexity of the design. The benefits of the user control-as-View approach include: </p>
<ul>
    <li>The View can be easily moved from one ASPX page to another. This happens regularly in a mid to large sized web application.
    <li>The View can be easily reused by different ASPX pages without duplicating much code at all.
    <li>The View can be initialized differently by different ASPX pages. For example, a user control could be written that displays a listing of projects. From the reporting section of the site, the user may view and filter all the projects available. From another section of the site, the user may only view a subset of the projects and not have the ability to run filters. In implementation, the same View can be passed to the same presenter, but then each ASPX page, in its respective section of the site, would call a different method on the Presenter to initialize the View in a unique way.
    <li>Additional Views can be added to the ASPX page without adding much additional, coding overhead. Simply include the new user control-as-view into the ASPX page and link it to its Presenter in the code-behind. Placing multiple sections of functionality within the same ASPX page, without using user controls, quickly creates a maintenance headache. </li>
</ul>
<h2><a name=EventHandlingWithMvp>II. Event handling with MVP</a></h2>
<ul class=download>
    <li><a href="http://www.codeproject.com/aspnet/ModelViewPresenter/EventHandlingMvp.zip"><u><font color=#0000ff>Download simple event-handling MVP - 16.0 Kb</font></u></a> </li>
</ul>
<p>The previous example described, essentially, a one-way round of communications between a Presenter and its View. The Presenter communicated with the Model and delivered it to the View. In most situations, events occur which need to be handed off to the Presenter for action. Furthermore, some events depend on whether or not a form was valid and whether or not IsPostBack had occurred. For example, there are some actions, such as data-binding, that may not be done when IsPostBack.</p>
<p>Disclaimer: Page.IsPostBack and Page.IsValid are web specific keywords. Therefore the following may make the presenter layer, as described, slightly invalid in non-web environments. However, with minor modifications it will work fine for WebForms, WinForms or mobile applications. In any case, the theory is the same but I welcome suggestions for making the presenter layer transferable to any .NET environment.</p>
<h3>A simple event handling sequence</h3>
<p>Continuing with the earlier example, assume that requirements now dictate that the user may enter a number of days to be added to the current time. The time shown in the View should then be updated to show the current time plus the number of days supplied by the user, assuming the user provided valid inputs. When not IsPostBack, the current time should be displayed. When IsPostBack, the Presenter should respond to the event accordingly. The sequence diagram below shows what occurs upon the user's initial request (top half of diagram) and what happens when the user clicks the "Add Days" button (bottom half of diagram). A more thorough review of the sequence follows the diagram. </p>
<a name=EventHandlingDiagram><img height=672 alt="Screenshot - BasicEventHandling.GIF" src="http://www.codeproject.com/aspnet/ModelViewPresenter/BasicEventHandling.GIF" width=634> </a>
<p>
<h4>A) User Control-as-View created</h4>
<p>This step simply represents the inline user control declaration found in the ASPX page. During page initialization, the user control gets created. It's included on the diagram to emphasize the fact that the user control implements <code>ICurrentTimeView</code>. During Page_Load, the ASPX code-behind then creates an instance of the Presenter, passing the User Control-as-View via its constructor. So far, everything looks identical to what was described in the section <a href="http://www.codeproject.com/aspnet/ModelViewPresenter.asp#EncapsulatingViews"><u><font color=#800080>"Encapsulating Views with User Controls</font></u></a>." </p>
<h4>B) Presenter attached to View</h4>
<p>In order for an event to be passed from the user control, the View, to the Presenter, it must have a reference to an instance of <code>CurrentTimePresenter</code>. To do this, the View Initializer, <em>ShowMeTheTime.aspx</em>, passes the Presenter to the View for later use. Contrary to initial reaction, this does not cause a bi-directional dependency between the Presenter and the View. Instead, the Presenter depends on the View interface and the View implementation depends on the Presenter to pass events off to. To see how it all works, let's take a step back to look at how all the pieces are now implemented. </p>
<h3>ICurrentTimeView.cs: the View interface</h3>
<pre lang=cs>public interface ICurrentTimeView
{
DateTime CurrentTime { set; }
string Message { set; }
void AttachPresenter(CurrentTimePresenter presenter);
}</pre>
<h3><a name=EventHandlingPresenter>CurrentTimePresenter.cs: the Presenter</a></h3>
<div class=precollapse id=premain10 style="WIDTH: 100%"><img id=preimg10 style="CURSOR: pointer" height=9 src="http://www.codeproject.com/images/minus.gif" width=9 preid="10"><span id=precollapse10 style="MARGIN-BOTTOM: 0px; CURSOR: pointer" preid="10"> Collapse</span></div>
<pre lang=cs id=pre10 style="MARGIN-TOP: 0px">public class CurrentTimePresenter
{
public CurrentTimePresenter(ICurrentTimeView view)
{
if (view == null)
throw new ArgumentNullException(<span class=cpp-string>"view may not be null"</span>);
this.view = view;
}
public void InitView(bool isPostBack)
{
if (! isPostBack)
{
view.CurrentTime = DateTime.Now;
}
}
public void AddDays(string daysUnparsed, bool isPageValid)
{
if (isPageValid)
{
view.CurrentTime =
DateTime.Now.AddDays(double.Parse(daysUnparsed));
}
else
{
view.Message = <span class=cpp-string>"Bad inputs...no updated date for you!"</span>;
}
}
private ICurrentTimeView view;
}</pre>
<h3>CurrentTimeView.ascx: the View</h3>
<h4>The ASCX page</h4>
<pre lang=aspnet>...
&lt;asp:Label id="lblMessage" runat="server" /&gt;&lt;br /&gt;
&lt;asp:Label id="lblCurrentTime" runat="server" /&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;asp:TextBox id="txtNumberOfDays" runat="server" /&gt;
&lt;asp:RequiredFieldValidator ControlToValidate="txtNumberOfDays" runat="server"
ErrorMessage="Number of days is required" ValidationGroup="AddDays" /&gt;
&lt;asp:CompareValidator
ControlToValidate="txtNumberOfDays" runat="server"
Operator="DataTypeCheck" Type="Double" ValidationGroup="AddDays"
ErrorMessage="Number of days must be numeric" /&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;asp:Button id="btnAddDays" Text="Add Days" runat="server"
OnClick="btnAddDays_OnClick" ValidationGroup="AddDays" /&gt;
...</pre>
<h4><a name=EventPasser>The ASCX code-behind page</a></h4>
<div class=precollapse id=premain12 style="WIDTH: 100%"><img id=preimg12 style="CURSOR: pointer" height=9 src="http://www.codeproject.com/images/minus.gif" width=9 preid="12"><span id=precollapse12 style="MARGIN-BOTTOM: 0px; CURSOR: pointer" preid="12"> Collapse</span></div>
<pre lang=cs id=pre12 style="MARGIN-TOP: 0px">public partial class Views_CurrentTimeView : UserControl, ICurrentTimeView
{
public void AttachPresenter(CurrentTimePresenter presenter)
{
if (presenter == null)
throw new ArgumentNullException(<span class=cpp-string>"presenter may not be null"</span>);
this.presenter = presenter;
}
public string Message
{
set { lblMessage.Text = value; }
}
public DateTime CurrentTime
{
set { lblCurrentTime.Text = value.ToString(); }
}
protected void btnAddDays_OnClick(object sender, EventArgs e)
{
if (presenter == null)
throw new FieldAccessException(<span class=cpp-string>"presenter has"</span> +
<span class=cpp-string>" not yet been initialized"</span>);
presenter.AddDays(txtNumberOfDays.Text, Page.IsValid);
}
private CurrentTimePresenter presenter;
}</pre>
<h3>ShowMeTheTime.aspx: the View initializer</h3>
<h4>The ASPX page</h4>
<pre lang=aspnet>...
&lt;%@ Register TagPrefix="mvpProject"
TagName="CurrentTimeView" Src="./Views/CurrentTimeView.ascx" %&gt;
&lt;mvpProject:CurrentTimeView id="currentTimeView" runat="server" /&gt;
...</pre>
<h4>The ASPX code-behind page</h4>
<pre lang=cs>public partial class ShowMeTheTime : Page
<span class=cs-comment>// No longer implements ICurrentTimeView</span>
{
protected void Page_Load(object sender, EventArgs e)
{
InitCurrentTimeView();
}
private void InitCurrentTimeView()
{
CurrentTimePresenter presenter =
new CurrentTimePresenter(currentTimeView);
currentTimeView.AttachPresenter(presenter);
presenter.InitView(Page.IsPostBack);
}
}</pre>
<h4>C) Presenter InitView</h4>
<p>As defined in the requirements, the Presenter should only show the current time if not IsPostBack. The important action to note is that the Presenter should decide what to do according to IsPostBack. It should not be the job of the ASPX code-behind to make this decision. As seen in the code above, the ASPX code-behind does no check for IsPostBack. It simply passes the value to the Presenter to determine what action to take. </p>
<p>This may lead to the question, "But what happens if another user control-as-view caused the post-back to occur?" In the scenario at hand, the current time would remain in the view state of the label and be displayed again after post back. This may be OK depending on the needs of the client. In general, it's a good question to ask of any Presenter: what impact will a post-back from another user control have on the View? In fact, it's a good question to ask even if you're not using MVP. There may be actions that should always occur, regardless of IsPostBack, while other initialization steps may be bypassed. View state settings obviously have a large impact on this decision, as well. </p>
<p>When not IsPostBack, <a href="http://www.codeproject.com/aspnet/ModelViewPresenter.asp#EventHandlingDiagram"><u><font color=#800080>as shown in the diagram</font></u></a>, the Presenter then sets the <code>CurrentTime</code> of the view via its interface. Sequence diagram purists may raise the point that the diagram implies two messages are being sent -- one from CurrentTimePresenter to ICurrentTimeView and then one from ICurrentTimeView to CurrentTimeView.ascx -- when in fact only one is being sent from CurrentTimePresenter to CurrentTimeView.ascx, polymorphically. The interface "middleman" is included to emphasize that the Presenter does not depend on the concrete View directly. </p>
<h4>D) Presenter InitView after IsPostBack</h4>
<p>In the preceding steps, the user made the HTTP request, the Presenter set the current time on the View, and the HTTP response was delivered to the user. Now, the user clicks the "Add Days" button, which causes a post-back. Everything occurs as before until <code>InitView</code> is called on the Presenter. At this point, the Presenter tests for IsPostBack and does not set the <code>CurrentTime</code> on the View. </p>
<h4>E) Button click handled by user control</h4>
<p>After the Page_Load of the ASPX page has occurred, the OnClick event is then raised to the user control. The View should not handle the event itself; it should immediately pass the event on to the Presenter for action. By looking at the <a href="http://www.codeproject.com/aspnet/ModelViewPresenter.asp#EventPasser"><u><font color=#800080>code-behind of the user control</font></u></a>, you can see that it makes sure it has been given a valid presenter -- more "Design by Contract" -- and then hands the command off to the Presenter. <a href="http://www.codeproject.com/aspnet/ModelViewPresenter.asp#EventHandlingPresenter"><u><font color=#800080>The Presenter</font></u></a> then verifies that the page was valid and sets the time or error message accordingly. </p>
<p>The above has been an exhaustive analysis of a complete MVP cycle with event handling. Once you get the hang of MVP, it takes very little time to get all the pieces in place. Remember to always begin with a unit test and let the unit tests drive the development. The unit tests not only help ensure that the MVP pieces are working correctly, they also serve as the point for defining the communications protocol among the pieces. A Visual Studio code snippet for an MVP unit test can be found in <a href="http://www.codeproject.com/aspnet/ModelViewPresenter.asp#AppendixB"><u><font color=#800080>Appendix B</font></u></a>. We'll now take a look at look at handling page redirection. </p>
<h2><a name=PageRedirects>III. Page redirects with MVP &amp; PageMethods</a></h2>
<p>In developing enterprise application, application flow is always a concern. Who's going to take care of page redirects? Should action redirects be stored in a configurable XML file? Should a third party tool such as <a href="http://mavnet.sourceforge.net/" target=_blank><u><font color=#0000ff>Maverick.NET</font></u></a> or <a href="http://www.springframework.net/" target=_blank><u><font color=#0000ff>Spring.NET</font></u></a> handle page flow? Personally, I like to keep the page redirects as close to the action as possible. In other words, I feel that storing action/redirects in an external XML file leads to further indirection that can be tedious to understand and maintain. As if we don't have enough to worry about already! On the other hand, hard-coded redirects in the ASPX code-behind are fragile, tedious to parse and not strongly typed. To solve this problem, the free <a href="http://metasapiens.com/PageMethods"><u><font color=#0000ff>download</font></u></a> PageMethods allows you to have strongly typed redirects. So instead of writing <code>Response.Redirect(<span class=cpp-string>"../Project/ShowProjectSummary?projectId="</span> + projectId.ToString() + <span class=cpp-string>"&amp;userId="</span> + userId.ToString())</code>, PageMethods provides a strongly typed redirect that would look more like <code>Response.Redirect(MyPageMethods.ShowProjectSummary.ShowSummaryFor(projectId, userId))</code>. The redirect is strongly typed and, therefore, checked at compile time. </p>
<p>An MVP related question concerning page redirects remains: who should be responsible for making a redirect and how should the redirect be initiated? I believe there are a number of valid answers to this question but will propose a solution that I've found to be rather successful. Add one event to the Presenter for each outcome that is possible. For example, assume a website is made up of two pages. The first page lists a number of projects; the second page, reached by clicking "Edit" next to one of the project names, allows the user to update the project's name. After updating the project name, the user should be redirected to the project listing page again. To implement this, the Presenter should raise an event showing that the project name was successfully changed and then the View Initializer, the ASPX page, should execute the appropriate redirect. Note that the following is illustrative and not associated with the "current time" example discussed thus far. </p>
<h4>Presenter</h4>
<pre lang=aspnet>...
public event EventHandler ProjectUpdated;
public void UpdateProjectNameWith(string newName)
{
...
if (everythingWentSuccessfully)
{
ProjectUpdated(this, null);
}
else
{
view.Message = "That name already exists.  Please provide a new one!";
}
}
...</pre>
<h4>ASPX code-behind</h4>
<pre lang=aspnet>...
protected void Page_Load(object sender, EventArgs e)
{
EditProjectPresenter presenter =
new EditProjectPresenter(editProjectView);
presenter.ProjectUpdated += new EventHandler(HandleProjectUpdated);
presenter.InitView();
}
private void HandleProjectUpdated(object sender, EventArgs e)
{
Response.Redirect(
MyPageMethods.ShowProjectSummary.Show(projectId, userId));
}
...</pre>
<p>Taking this approach keeps page redirection out of the Presenter and out of the View. As a rule of thumb, the Presenter should never require a reference to <code>System.Web</code>. Furthermore, disassociating redirects from the View -- i.e. the user control -- and allows the View to be used again by other View Initializers, i.e. other ASPX pages. At the same time, it leaves application flow up to each individual View Initializer. This is the greatest benefit of using an event based model of redirection with User Control-as-View MVP. </p>
<h2><a name=PresentationSecurity>IV. Presentation security with MVP</a></h2>
<p>Oftentimes, a column, button, table or whatever should be shown/hidden based on the permissions of the user viewing the website. Likewise, an item may be hidden when a View is included in one View Initializer vs. being included in different View Initializer. The security should be determined by the Presenter but the View should handle how that decision should be implemented. Picking up again with the "current time" example, assume that the client only wants the "Add Days" section to be available for users on even days, e.g. 2, 4, 6. The client likes to keep the users guessing! The View could encapsulate this area within a panel, as follows: </p>
<pre lang=aspnet>...
&lt;asp:Panel id="pnlAddDays" runat="server" visible="false"&gt;
&lt;asp:TextBox id="txtNumberOfDays" runat="server" /&gt;
&lt;asp:RequiredFieldValidator
ControlToValidate="txtNumberOfDays" runat="server"
ErrorMessage="Number of days is required" ValidationGroup="AddDays" /&gt;
&lt;asp:CompareValidator ControlToValidate="txtNumberOfDays" runat="server"
Operator="DataTypeCheck" Type="Double" ValidationGroup="AddDays"
ErrorMessage="Number of days must be numeric" /&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;asp:Button id="btnAddDays" Text="Add Days" runat="server"
OnClick="btnAddDays_OnClick" ValidationGroup="AddDays" /&gt;
&lt;/asp:Panel&gt;
...</pre>
<p>Note that the panel's visibility is pessimistically set to <code><span class=cpp-keyword>false</span></code>. Although it would not make much difference in this case, it is better to be pessimistic about showing secure elements than the other way around. The code-behind of the View would then expose a setter to show/hide the panel: </p>
<pre lang=aspnet>...
public bool EnableAddDaysCapabilities
{
set { pnlAddDays.Visible = value; }
}
...</pre>
<p>Note that the View does not expose the panel directly. This is intentionally done for two reasons: 1) exposing the panel directly would require that the Presenter have a reference to <code>System.Web</code>, something we want to avoid, and 2) exposing the panel ties the Presenter to an "implementation detail" of the View. The more a Presenter is tied to how a View is implemented, the less likely it will be reusable with other Views. As with other OOP scenarios, the pros and cons of exposing implementation details of the View need to be weighed against looser coupling to the Presenter. </p>
<p>Finally, during InitView, the Presenter checks if the user should be allowed to use the add-days functionality and sets the permission on the View accordingly: </p>
<pre lang=aspnet>...
public void InitView()
{
view.EnableAddDaysCapabilities = (DateTime.Now.Day % 2 == 0);
}
...</pre>
<p>This simple example can be extended to a varied number of scenarios including security checks. Note that this is not a replacement for built-in .NET security, but it serves to augment it for finer control. </p>
<h2><a name=ApplicationArchitecture>V. Application architecture with MVP</a></h2>
<ul class=download>
    <li><a href="http://www.codeproject.com/aspnet/ModelViewPresenter.asp#Downloads"><u><font color=#800080>Download sample MVP Enterprise Solution</font></u></a> </li>
</ul>
<p>Finally! How does all of this fit together in a data-driven, enterprise application? "Enterprise application," in this instance, is an application that has logically separated tiers including presentation, domain and data-access layers. The following graph shows an overview of a fully architected solution with discussion following. </p>
<p><img height=649 src="http://www.codeproject.com/aspnet/ModelViewPresenter/ApplicationArchitecture.gif" width=600></p>
<p>Each raised box represents a distinct specialization of the application. Each gray box then represents a separate physical assembly, e.g. <em>MyProject.Web.dll</em>, <em>MyProject.Presenters.dll</em>, <em>MyProject.Core.dll</em>. The arrows represent dependencies. For example, the <em>.Web</em> assembly depends on the <em>.Presenters</em> and <em>.Core</em> assemblies. The assemblies avoid bi-directional dependency using the techniques Dependency Inversion and Dependency Injection. My preferred means of Dependency Injection -- "DI" in the above graph -- to the View Initializers is via the <a href="http://www.castleproject.org/index.php/Windsor_Container" target=_blank><u><font color=#0000ff>Castle Windsor</font></u></a> project. The data layer then uses the ORM framework, <a href="http://www.hibernate.org/343.html" target=_blank><u><font color=#0000ff>NHibernate</font></u></a>, for communicating with the database. </p>
<p>For a primer on Dependency Injection, read the CodeProject article entitled "<a href="http://www.codeproject.com/csharp/DependencyInjection.asp"><u><font color=#0000ff>Dependency Injection for Loose Coupling</font></u></a>." Additionally, for a complete overview of this architecture, sans the <em>.Presenters</em> layer and Castle Windsor integration, read the CodeProject article entitled "<a href="http://www.codeproject.com/aspnet/NHibernateBestPractices.asp"><u><font color=#800080>NHibernate Best Practices with ASP.NET</font></u></a>." This article also describes how to set up and run the sample application. Yes, these are both shameless plugs for other articles I have written, but both are required reading to fully appreciate the sample solution. Please feel free to raise any questions concerning the architecture.</p>
<h2>In summary</h2>
<p>At first glance, implementing MVP looks like a lot of extra work. In fact, it will slow development a bit during the initial stages of development. However, after using it in all stages of enterprise application development, the long-term benefits of using the approach far outweigh the initial feelings of discomfort with the pattern. MVP will greatly extend your ability to unit test and keep code more maintainable throughout the lifetime of the project, especially during the maintenance phase. When it comes right down to it, I'm not suggesting that you use MVP on all your enterprise ASP.NET projects, just the projects that you want to work! ;) In all seriousness, MVP is not appropriate in all situations. An application's architecture should fit the task at hand and complexity should not be added unless warranted. Obviously, MVP and User-Control-as-View MVP are just two architectural options among many. However, if used appropriately, MVP allows you to be confident in your presentation logic by making most of the code that would have been in a code-behind, testable and maintainable. </p>
<h2>Appendix A: additional references</h2>
<ul>
    <li><a href="http://www.martinfowler.com/eaaDev/ModelViewPresenter.html" target=_blank><u><font color=#0000ff>Martin Fowler's soon-to-be-published overview of MVP</font></u></a>
    <li><a href="http://www.objectmentor.com/resources/articles/TheHumbleDialogBox.pdf" target=_blank><u><font color=#0000ff>The humble dialog box</font></u></a>
    <li><a href="http://www.darronschall.com/weblog/archives/000113.cfm" target=_blank><u><font color=#800080>MVC vs. MVP</font></u></a>
    <li><a href="http://codebetter.com/blogs/jeffrey.palermo/archive/2005/04/02/128598.aspx" target=_blank><u><font color=#0000ff>A thank you goes out to Jeffrey Palermo for inspiring the above architectural diagram</font></u></a> </li>
</ul>
<h2><a name=AppendixB>Appendix B: MVP unit test code-snippet for Visual Studio 2005</a></h2>
<p>With a default VS 2005 install location, copy the following contents to "MVP Test Init.snippet" under "C:\Program Files\Microsoft Visual Studio 8\VC#\Snippets\1033\Visual C#". </p>
<div class=precollapse id=premain20 style="WIDTH: 100%"><img id=preimg20 style="CURSOR: pointer" height=9 src="http://www.codeproject.com/images/minus.gif" width=9 preid="20"><span id=precollapse20 style="MARGIN-BOTTOM: 0px; CURSOR: pointer" preid="20"> Collapse</span></div>
<pre lang=xml id=pre20 style="MARGIN-TOP: 0px">&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;CodeSnippets
xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"&gt;
&lt;CodeSnippet Format="1.0.0"&gt;
&lt;Header&gt;
&lt;Title&gt;MVP Test Init&lt;/Title&gt;
&lt;Shortcut&gt;mvpTestInit&lt;/Shortcut&gt;
&lt;Description&gt;Code snippet for creating an initial
unit test for a new MVP setup.&lt;/Description&gt;
&lt;Author&gt;Billy McCafferty&lt;/Author&gt;
&lt;SnippetTypes&gt;
&lt;SnippetType&gt;Expansion&lt;/SnippetType&gt;
&lt;/SnippetTypes&gt;
&lt;/Header&gt;
&lt;Snippet&gt;
&lt;Declarations&gt;
&lt;Literal&gt;
&lt;ID&gt;viewInterface&lt;/ID&gt;
&lt;ToolTip&gt;Name of the view interface&lt;/ToolTip&gt;
&lt;Default&gt;IView&lt;/Default&gt;
&lt;/Literal&gt;
&lt;Literal&gt;
&lt;ID&gt;presenter&lt;/ID&gt;
&lt;ToolTip&gt;Name of the presenter class&lt;/ToolTip&gt;
&lt;Default&gt;Presenter&lt;/Default&gt;
&lt;/Literal&gt;
&lt;Literal&gt;
&lt;ID&gt;mockView&lt;/ID&gt;
&lt;ToolTip&gt;Name of the mock view
used in the unit test&lt;/ToolTip&gt;
&lt;Default&gt;MockView&lt;/Default&gt;
&lt;/Literal&gt;
&lt;/Declarations&gt;
&lt;Code Language="csharp"&gt;
&lt;![CDATA[        [Test]
public void TestInitView()
{
$viewInterface$ view = new $mockView$();
$presenter$ presenter = new $presenter$(view);
view.AttachPresenter(presenter);
presenter.InitView();
}
private class $mockView$ : $viewInterface$
{
public void AttachPresenter($presenter$ presenter)
{
}
}
$end$]]&gt;
&lt;/Code&gt;
&lt;/Snippet&gt;
&lt;/CodeSnippet&gt;
&lt;/CodeSnippets&gt;</pre>
<h2>History</h2>
<ul>
    <li>2006.07.02 - Initial posting
    <li>2007.04.18 - Added lessons learned over the past year to top of article
    <li>2007.07.10 - Article edited and moved to the main CodeProject.com article base </li>
</ul>
<!-- Article Ends --><img src ="http://www.cnitblog.com/seeyeah/aggbug/36126.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-11-10 21:44 <a href="http://www.cnitblog.com/seeyeah/archive/2007/11/10/36126.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MVC vs. MVP</title><link>http://www.cnitblog.com/seeyeah/archive/2007/11/10/36124.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Sat, 10 Nov 2007 13:36:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/11/10/36124.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/36124.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/11/10/36124.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/36124.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/36124.html</trackback:ping><description><![CDATA[引用 <a href="http://www.darronschall.com/weblog/archives/000113.cfm">http://www.darronschall.com/weblog/archives/000113.cfm</a><br><br>
<div class=entry>
<p>By now you should've heard of the Model-View-Controller design pattern. If you've read <a href="http://www.amazon.com/exec/obidos/tg/detail/-/0735711836/qid=1086739697/sr=8-1/ref=sr_8_xs_ap_i1_xgl14/103-9901460-9578227?v=glance&amp;s=books&amp;n=507846" target=_blank><u><font color=#0000ff>OOP with ActionScript</font></u></a> by <a href="http://www.waxpraxis.org/" target=_blank><u><font color=#0000ff>Branden</font></u></a> and <a href="http://www.samuelwan.com/information/" target=_blank><u><font color=#0000ff>Sam</font></u></a> then you're also somewhat familiar with the Model-View-Presenter design pattern. So what's the difference?</p>
<p>MVC came first. With the MVC pattern it's possible to separate your presentation information from your behind the scenes business logic. Think along the lines of XHTML/CSS and separating your content from your presentation. A brilliant concept that works quite well, but is not without it's faults.</p>
<p>In MVC, the model stores the data, the view is a representation of that data, and the controller allows the user to change the data. When the data is changed, all views are notified of the change and they can update themselves as necessary (think EventDispatcher). </p>
<p>MVP is a derivative of MVC, mostly aimed at addressing the "Application Model" portion of MVC and focusing around the observer implementation in the MVC triad. Instead of a Controller, we now have a Presenter, but the basic idea remains the same - the model stores the data, the view is a representation of that data (not necessarily graphical), and the presenter coordinates the application.</p>
<p>In MVP the Presenter gets some extra power. It's purpose is to interpret events and perform any sort of logic necessary to map them to the proper commands to manipulate the model in the intended fashion. Most of the code dealing with how the user interface works is coded into the Presenter, making it much like the "Application Model" in the MVC approach. The Presenter is then directly linked to the View so the two can function together "mo' betta". </p>
<p>Basically, in MVP there is no Application Model middle-man since the Presenter assumes this functionality. Additionally, the View in MVP is responsible for handling the UI events (like mouseDown, keyDown, etc), which used to be the Controllers job, and the Model becomes strictly a Domain Model.</p>
<p>Clear as mud, right?</p>
<p>If you're interested in this topic there's quite a bit of useful information out there, but you'll need to take the time to digest it. Check out the following links: (most of these are Smalltalk based, but should still be understandable)</p>
<ul>
    <li><a href="http://c2.com/cgi/wiki?ModelViewControllerHistory" target=_blank><u><font color=#0000ff>Model View Controller History</font></u></a>
    <li><a href="http://www.object-arts.com/EducationCentre/Overviews/MVC.htm" target=_blank><u><font color=#0000ff>Model-View-Controller framework</font></u></a>
    <li><a href="http://c2.com/cgi/wiki?ModelViewController" target=_blank><u><font color=#0000ff>Model View Controller</font></u></a>
    <li><a href="http://ootips.org/mvc-pattern.html" target=_blank><u><font color=#0000ff>(ootips) - Model-View-Contoller</font></u></a>
    <li><a href="http://c2.com/cgi/wiki?ModelViewPresenter" target=_blank><u><font color=#0000ff>Model View Presenter</font></u></a>
    <li><a href="http://www.object-arts.com/EducationCentre/Overviews/ModelViewPresenter.htm" target=_blank><u><font color=#0000ff>Model-View-Presenter framework</font></u></a>
    <li><a href="http://www.object-arts.com/EducationCentre/Patterns/MVP.htm" target=_blank><u><font color=#0000ff>Pattern: Model-View-Presenter</font></u></a>
    <li><a href="http://web.archive.org/web/20010709040218/www-106.ibm.com/developerworks/library/mvp.html" target=_blank><u><font color=#0000ff>MVP: Model-View-Presenter (Java paper)</font></u></a>
    <li><a href="http://www.object-arts.com/Papers/TwistingTheTriad.PDF" target=_blank><u><font color=#0000ff>Twisting the Triad</font></u></a> </li>
</ul>
<p>Above all, remember that MVC and MVP are just patterns. They're more or less a set of guidelines to follow when building applications. In the end, the developer can implement the application however they see fit. </p>
<a name=more></a></div>
<p class=postmetadata>Posted by darron at June 9, 2004 12:44 PM </p>
<script type=text/javascript><!--
google_ad_client = "pub-5888341092613954";
google_ad_width = 336;
google_ad_height = 280;
google_ad_format = "336x280_as";
google_ad_type = "text_image";
google_ad_channel ="";
google_color_border = "CCCCCC";
google_color_bg = "FFFFFF";
google_color_link = "000000";
google_color_url = "666666";
google_color_text = "333333";
//--></script>
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type=text/javascript>
</script>
<img src ="http://www.cnitblog.com/seeyeah/aggbug/36124.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-11-10 21:36 <a href="http://www.cnitblog.com/seeyeah/archive/2007/11/10/36124.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>.Net中多语言版本的实现 [vs2003]</title><link>http://www.cnitblog.com/seeyeah/archive/2007/03/29/24894.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Thu, 29 Mar 2007 15:31:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/03/29/24894.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/24894.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/03/29/24894.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/24894.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/24894.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Copy From 陈迪臣.Net中两个比较重要的命名空间System.Globalization和System.Resources提供多语言版本的很好解决方案。在这个命名空间中你可以使用手工或者自动的方式来实现你的多语言版本。如果是手工的话，在工程中自己添加一个资源文件或者借助其它工作来生成资源文件，然后通过ResourceManager来读取就可以。在VS2003中，你不用写任何代码也能实...&nbsp;&nbsp;<a href='http://www.cnitblog.com/seeyeah/archive/2007/03/29/24894.html'>阅读全文</a><img src ="http://www.cnitblog.com/seeyeah/aggbug/24894.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-03-29 23:31 <a href="http://www.cnitblog.com/seeyeah/archive/2007/03/29/24894.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Generate a client-side proxy for a webservice using HTTP Handlers, Mootools and JSON</title><link>http://www.cnitblog.com/seeyeah/archive/2007/03/23/24545.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Fri, 23 Mar 2007 15:24:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/03/23/24545.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/24545.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/03/23/24545.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/24545.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/24545.html</trackback:ping><description><![CDATA[
		<div style="OVERFLOW: scroll; WIDTH: 100%; WORD-BREAK: break-all; HEIGHT: 500px">
				<div style="FONT-WEIGHT: bold; FONT-SIZE: 16pt">Generate a client-side proxy for a webservice using HTTP Handlers, Mootools and JSON</div>
				<b>By <a href="http://www.codeproject.com/script/Articles/list_articles.asp?userid=2118410">Bruno R. Figueiredo</a></b>. <br /><br /><div style="FONT-SIZE: 12px">We will create code that will generate all the javascript necessary to call a webservice, sending and recieving Json. This will allow us to choose wich javascript library (such as Mootools, prototype, scriptaculous, etc...) to use and still be able to performe this task.<br /><br /><ul class="download"><li><a href="http://www.codeproject.com/useritems/clientproxyfor_webservice/ClientProxyCP.zip"><font color="#002c99">Download ClientProxyCP.zip - 73.8 KB</font></a></li></ul><h2>Introduction </h2><p>With the release of the MSFT Ajax Extensions, calling a webservice from client-side is a kids task.</p><p>But what if you, like me, want to call a webservice but don't want to use the Ajax Extensions, using instead another library, like mootools? Well you could *just* create the soap body and send it to the webservice. That's seems easy, right?</p><p>Well, I like things that generate themselves.</p><p>In this post I will create a simple client-side proxy from a webservice, and if all ends well, we will be able to call it and get a response.</p><h2>Background info</h2><p>For understanding how this should be done, I went and "reflected" the MSFT Ajax Extensions assemblies to see how did they get this to work. So some of the code presented in this proof of concept is based on this. Again, the main ideia is to understand how to build a proxy similar to the used by the MSFT Ajax Extensions but without really using it.</p><h2>"Why don't you use the MSFT Ajax Extensions?"</h2><p>Well, first of all I wanted to learn how the whole process worked.</p><p>I also wanted to be able to call a webservice by sending and receiving Json without using the MSFT Ajax Extensions. Many small sized libraries make XHR calls. Why not used them.</p><p>Another issue, not covered here, is the usage of this code (with some slight changes) on the v1.1 of the .NET Framework.</p><h2>The first thing...</h2><p>... that we need to do is understand the life cycle of this:</p><p>Given a webservice (or a list of webservices), the application will validate if the webservice has the [AjaxRemoteProxy] attribute. If so, we will grab all the [WebMethod] methods that are public and generate the client-side proxy. When the client-proxy is called, on the server we need to get the correct method, invoke him, and return its results "json style". All of this server-side is done with some IHttpHandlers.</p><p>A HandlerFactory will do the work on finding out what is needed: The default webservice handler, a proxy handler, or a response handler.</p><p>The proxy file will be the asmx itself, but now we will add a "/js" to the end of the call, resulting in something like this:</p><pre lang="html">&lt;script src="http://www.codeproject.com/ClientProxyCP/teste.asmx/js" type="text/javascript"&gt;&lt;/script&gt;
</pre><p>When the call is made to this, a handler will now that a javascript is needed, and generate it.</p><h2>"Show me some code"</h2><p>The first thing we need to have is the <code>AjaxRemoteProxy</code> attribute. This attribute will allow us to both mark wich webservices and web methods we will be able to call on client-side: </p><div class="precollapse" id="premain1" style="WIDTH: 100%"><img id="preimg1" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="1" /><span id="precollapse1" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="1"> Collapse</span></div><pre lang="cs" id="pre1" style="MARGIN-TOP: 0px">using System;

namespace CreativeMinds.Web.Proxy
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
    public class AjaxRemoteProxyAttribute : Attribute
    {
<span class="cs-preprocessor">        #region Private Declarations</span>
        private bool  _ignore = false;
<span class="cs-preprocessor">        #endregion Private Declarations</span><span class="cs-preprocessor">        #region Properties</span><span class="cs-comment">/// &lt;summary&gt;</span><span class="cs-comment">/// Gets or sets a value indicating whether the target is ignored.</span><span class="cs-comment">/// &lt;/summary&gt;</span><span class="cs-comment">/// &lt;value&gt;&lt;c&gt;true&lt;/c&gt; if ignore; otherwise, &lt;c&gt;false&lt;/c&gt;.&lt;/value&gt;</span>
        public bool  Ignore
        {
            get { return _ignore; }
            set { _ignore = value; }
        }
<span class="cs-preprocessor">        #endregion Properties</span><span class="cs-preprocessor">        #region Constructor</span><span class="cs-comment">/// &lt;summary&gt;</span><span class="cs-comment">/// Initializes a new instance of the &lt;see cref="AjaxRemoteProxyAttribute"/&gt; class.</span><span class="cs-comment">/// &lt;/summary&gt;</span>
        public AjaxRemoteProxyAttribute()
        {
        }
        <span class="cs-comment">/// &lt;summary&gt;</span><span class="cs-comment">/// Initializes a new instance of the &lt;see cref="AjaxRemoteProxyAttribute"/&gt; class.</span><span class="cs-comment">/// &lt;/summary&gt;</span><span class="cs-comment">/// &lt;param name="_ignore"&gt;if set to &lt;c&gt;true&lt;/c&gt; if we wish to ignore this target.&lt;/param&gt;</span>
        public AjaxRemoteProxyAttribute(bool _ignore)
        {
            this._ignore = _ignore;
        }
<span class="cs-preprocessor">        #endregion Constructor</span>
    }
}
</pre><p>Now that we have our attribute, lets create a simple Webservice:</p><pre lang="cs">using System.Web.Services;
using CreativeMinds.Web.Proxy;

namespace CreativeMinds.Web.Services{
    [AjaxRemoteProxy()]
    [WebService(Namespace = <span class="cpp-string">"http://tempuri.org/"</span>)]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class MyWebService : WebService
    {
        [WebMethod]
        public string HelloWorld()
        {
            return <span class="cpp-string">"Hello World"</span>;
        }
        [WebMethod]
        public string HelloYou(string name)
        {
            return <span class="cpp-string">"Hello "</span> + name;
        }
    }
}
</pre><p>Notice that the webservice class is marked with our newly created attribute. </p><p>Now comes the cool code. The first thing we now need to do is to let the application know that the calls to *.asmx are now handled by us. So we need to do two things: First create the Handler and then change the web.config file. </p><strong>The WebServices Handler Factory</strong><p>As it was said before, the all *.asmx calls will be handled by us. Because we also want to maintain the normal functionality of the webservices, we need to create a handler factory. This factory will managed the return of the specific handler based on the following assumptions:</p><ol><li>If the context.Request.PathInfo ends with "/js", we need to generate the proxy; 
</li><li>If the context.Request.ContentType is "application/json;" or we have a context.Request.Headers["x-request"] with "JSON" value, we need to execute a method and return its value; 
</li><li>otherwise, we let the webservice run normally.</li></ol><p>So lets build our factory:</p><div class="precollapse" id="premain3" style="WIDTH: 100%"><img id="preimg3" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="3" /><span id="precollapse3" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="3"> Collapse</span></div><pre lang="cs" id="pre3" style="MARGIN-TOP: 0px">using System;
using System.Web;
using System.Web.Services.Protocols;

namespace CreativeMinds.Web.Proxy
{
    public class RestFactoryHandler:IHttpHandlerFactory
    {
<span class="cs-preprocessor">        #region IHttpHandlerFactory Members</span>

        public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
        {
            if (string.Equals(context.Request.PathInfo, <span class="cpp-string">"/js"</span>, StringComparison.OrdinalIgnoreCase))
            {
                return new RestClientProxyHandler();
            }
            else
            {
                if (context.Request.ContentType.StartsWith(<span class="cpp-string">"application/json;"</span>, StringComparison.OrdinalIgnoreCase) || 
                    (context.Request.Headers[<span class="cpp-string">"x-request"</span>] != null &amp;&amp; 
                    context.Request.Headers[<span class="cpp-string">"x-request"</span>].Equals(<span class="cpp-string">"json"</span>, StringComparison.OrdinalIgnoreCase)))
                {
                    return new RestClientResponseHandler();
                }
            }
            return new WebServiceHandlerFactory().GetHandler(context, requestType, url, pathTranslated);
        }

        public void ReleaseHandler(IHttpHandler handler)
        {
            
        }

<span class="cs-preprocessor">        #endregion</span>
    }
}
</pre><p>Then we also need to let the application know about our factory:</p><pre lang="xml">&lt;httpHandlers&gt;
    &lt;remove verb="*" path="*.asmx"/&gt;
    &lt;add verb="*" path="*.asmx" validate="false" type="CreativeMinds.Web.Proxy.RestFactoryHandler"/&gt;
&lt;/httpHandlers&gt;
</pre><strong>The client-side proxy generator handler</strong><p>When the context.Request.PathInfo equals "/js", we need to generate the client-side proxy. For this task the factory will return the <code>RestClientProxyHandler</code>. </p><div class="precollapse" id="premain5" style="WIDTH: 100%"><img id="preimg5" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="5" /><span id="precollapse5" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="5"> Collapse</span></div><pre lang="cs" id="pre5" style="MARGIN-TOP: 0px">using System.Web;

namespace CreativeMinds.Web.Proxy
{
    class RestClientProxyHandler : IHttpHandler
    {
        private bool isReusable = true;

<span class="cs-preprocessor">        #region IHttpHandler Members</span><span class="cs-comment">///&lt;summary&gt;</span><span class="cs-comment">///Enables processing of HTTP Web requests by a custom HttpHandler that implements the &lt;see cref="T:System.Web.IHttpHandler"&gt;&lt;/see&gt; interface.</span><span class="cs-comment">///&lt;/summary&gt;</span><span class="cs-comment">///</span><span class="cs-comment">///&lt;param name="context"&gt;An &lt;see cref="T:System.Web.HttpContext"&gt;&lt;/see&gt; object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests. &lt;/param&gt;</span>
        public void ProcessRequest(HttpContext context)
        {
            WebServiceData wsd = context.Cache[<span class="cpp-string">"WS_DATA:"</span> + context.Request.FilePath] as WebServiceData;
            if (wsd != null)
            {
                wsd.Render(context);
            }
        }

        <span class="cs-comment">///&lt;summary&gt;</span><span class="cs-comment">///Gets a value indicating whether another request can use the &lt;see cref="T:System.Web.IHttpHandler"&gt;&lt;/see&gt; instance.</span><span class="cs-comment">///&lt;/summary&gt;</span><span class="cs-comment">///</span><span class="cs-comment">///&lt;returns&gt;</span><span class="cs-comment">///true if the &lt;see cref="T:System.Web.IHttpHandler"&gt;&lt;/see&gt; instance is reusable; otherwise, false.</span><span class="cs-comment">///&lt;/returns&gt;</span><span class="cs-comment">///</span>
        public bool IsReusable
        {
            get { return isReusable; }
        }

<span class="cs-preprocessor">        #endregion</span>
    }
}
</pre><p>Notice two things:<br /></p><ol><li>the handler uses a <code>WebServiceData</code> object. This object contains the information about the webservice. So what we do where is get the WebServiceData object from the context.Cache and render it. 
</li><li>the <code>context.Cache[<span class="cpp-string">"WS_DATA:"</span> + ... ]</code> holds all the <code>WebServiceData</code> on all webservices that are proxified. This collection is filled also on the <code>WebServiceData</code> object. </li></ol><p><strong>WebServiceData object</strong></p><p>As said, the WebServiceData contains basic information about the webservice. It is also responsible for the render and execution of the webservice. </p><div class="precollapse" id="premain6" style="WIDTH: 100%"><img id="preimg6" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="6" /><span id="precollapse6" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="6"> Collapse</span></div><pre lang="cs" id="pre6" style="MARGIN-TOP: 0px">using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Security;
using System.Text;
using System.Web;
using System.Web.Compilation;
using System.Web.Hosting;
using System.Web.Services;
using System.Web.UI;
using Newtonsoft.Json;

namespace CreativeMinds.Web.Proxy
{
    internal class WebServiceData
    {
        
<span class="cs-preprocessor">        #region Private Declarations</span>

        private List&lt;MethodInfo&gt; _methods;
        private Type _type;
        private string _wsPath;
        private object _typeInstance;

<span class="cs-preprocessor">        #endregion Private Declarations</span><span class="cs-preprocessor">        #region Constructor</span>

        public WebServiceData(string wsPath)
        {
            _wsPath = wsPath;
            _methods = new List&lt;MethodInfo&gt;();
            Process();
        }

<span class="cs-preprocessor">        #endregion Constructor</span><span class="cs-preprocessor">        #region Process</span><span class="cs-comment">/// &lt;summary&gt;</span><span class="cs-comment">/// Processes this instance.</span><span class="cs-comment">/// &lt;/summary&gt;</span>
        private void Process()
        {
            <span class="cs-comment">//Verifies if the path to the webservice is valid</span>
            if (HostingEnvironment.VirtualPathProvider.FileExists(_wsPath))
            {
                Type type1 = null;
                try
                {
                    <span class="cs-comment">// Lets try and get the Type from the Virtual Path</span>
                    type1 = BuildManager.GetCompiledType(_wsPath);
                    if (type1 == null)
                    {
                        type1 = BuildManager.CreateInstanceFromVirtualPath(_wsPath, typeof (Page)).GetType();
                    }

                    if (type1 != null)
                    {
                        <span class="cs-comment">// Good. We have a Type. Now lets check if this is to Profixy.</span>
                        object[] objArray1 = type1.GetCustomAttributes(typeof (AjaxRemoteProxyAttribute), true);
                        if (objArray1.Length == 0)
                        {
                            throw new InvalidOperationException(<span class="cpp-string">"No AjaxRemoteProxyAttribute found on webservice"</span>);
                        }

                        <span class="cs-comment">// Well. So far so good.</span><span class="cs-comment">// Let's get all the methods.</span>
                        BindingFlags flags1 = BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance;
                        MethodInfo[] infoArray1 = type1.GetMethods(flags1);

                        foreach (MethodInfo info1 in infoArray1)
                        {
                            <span class="cs-comment">// we only need the WebMethods</span>
                            object[] metArray1 = info1.GetCustomAttributes(typeof (WebMethodAttribute), true);
                            if (metArray1.Length != 0)
                            {
                                _methods.Add(info1);
                            }
                        }
                        
                        <span class="cs-comment">// keep locally the Type</span>
                        _type = type1;

                        <span class="cs-comment">// Add this WS to the Cache, for later use.</span>
                        if (HttpContext.Current.Cache[<span class="cpp-string">"WS_DATA:"</span> + VirtualPathUtility.ToAbsolute(_wsPath)] == null)
                        {
                            HttpContext.Current.Cache[<span class="cpp-string">"WS_DATA:"</span> + VirtualPathUtility.ToAbsolute(_wsPath)] = this;
                        }
                    }
                    else
                    {
                        throw new ApplicationException(<span class="cpp-string">"Couldn't proxify webservice!!!!"</span>);
                    }
                }
                catch (SecurityException)
                {
                }
            }
        }

<span class="cs-preprocessor">        #endregion</span><span class="cs-preprocessor">        #region Render</span><span class="cs-comment">/// &lt;summary&gt;</span><span class="cs-comment">/// Renders the Proxy to the specified &lt;see cref="HttpContext"/&gt;.</span><span class="cs-comment">/// &lt;/summary&gt;</span><span class="cs-comment">/// &lt;param name="context"&gt;The &lt;see cref="HttpContext"/&gt;.&lt;/param&gt;</span>
        public void Render(HttpContext context)
        {
            <span class="cs-comment">// Set the ContentType to Javascript</span>
            context.Response.ContentType = <span class="cpp-string">"application/x-javascript"</span>;

            StringBuilder aux = new StringBuilder();
            if (_type == null) return;

            <span class="cs-comment">// Register the namespace</span>
            aux.AppendLine(string.Format(<span class="cpp-string">"RegisterNamespace(\"</span>{0}\<span class="cpp-string">");"</span>, _type.Namespace));

            <span class="cs-comment">// Create the Class for this Type</span>
            string nsClass = string.Format(<span class="cpp-string">"{0}.{1}"</span>, _type.Namespace, _type.Name);
            aux.AppendLine(string.Format(<span class="cpp-string">"{0} = function(){{}};"</span>, nsClass));

            _methods.ForEach(delegate (MethodInfo method)
                                 {
                                     <span class="cs-comment">// Create a static Method on the class</span>
                                     aux.AppendFormat(<span class="cpp-string">"{0}.{1} = function("</span>, nsClass, method.Name);

                                     <span class="cs-comment">// Set the method arguments</span>
                                     StringBuilder argumentsObject = new StringBuilder();
                                     foreach (ParameterInfo info2 in method.GetParameters())
                                     {
                                         aux.AppendFormat(<span class="cpp-string">"{0}, "</span>, info2.Name);
                                         argumentsObject.AppendFormat(<span class="cpp-string">"\"</span>{0}\<span class="cpp-string">":{0}, "</span>, info2.Name);
                                     }

                                     if (argumentsObject.Length &gt; 0)
                                     {
                                         argumentsObject = argumentsObject.Remove(argumentsObject.Length - 2, 2);
                                         argumentsObject.Insert(0, <span class="cpp-string">"{"</span>).Append(<span class="cpp-string">"}"</span>);
                                     }

                                     <span class="cs-comment">// Add the CompleteHandler argument in last</span>
                                     aux.Append(<span class="cpp-string">"onCompleteHandler){\n"</span>);

                                     <span class="cs-comment">// Render the method Body with the XHR call</span>
                                     aux.AppendLine(string.Format(<span class="cpp-string">"new Json.Remote(\"</span>{1}\<span class="cpp-string">", {{onComplete: onCompleteHandler, method:'post'}}).send({0});"</span>,                                      argumentsObject.ToString(), VirtualPathUtility.ToAbsolute(_wsPath + <span class="cpp-string">"/"</span> + method.Name)));
                                     aux.Append(<span class="cpp-string">"}\n"</span>);
                                 });
            context.Response.Write(aux.ToString());
        }

<span class="cs-preprocessor">        #endregion</span><span class="cs-preprocessor">        #region Invoke</span><span class="cs-comment">/// &lt;summary&gt;</span><span class="cs-comment">/// Invokes the requested WebMethod specified in the &lt;see cref="HttpContext"/&gt;.</span><span class="cs-comment">/// &lt;/summary&gt;</span><span class="cs-comment">/// &lt;param name="context"&gt;The &lt;see cref="HttpContext"/&gt;.&lt;/param&gt;</span>
        public void Invoke(HttpContext context)
        {
            <span class="cs-comment">// Method name to invoke</span>
            string methodName = context.Request.PathInfo.Substring(1);

            <span class="cs-comment">// We need an Instance of the Type</span>
            if (_typeInstance == null)
                _typeInstance = Activator.CreateInstance(_type);

            <span class="cs-comment">// Get the Posted arguments (format "json={JSON Object}")</span>
            string requestBody = new StreamReader(context.Request.InputStream).ReadToEnd();
            string[] param = requestBody.Split('=');
            <span class="cs-comment">// JSON Deserializer @ http://www.newtonsoft.com/products/json/</span>
            object a = JavaScriptConvert.DeserializeObject(param[1]);
            <span class="cs-comment">//object a = JavaScriptDeserializer.DeserializeFromJson&lt;object&gt;(param[1]);</span>
            Dictionary&lt;string, object&gt; dic = a as Dictionary&lt;string, object&gt;;
            int paramCount = 0;
            if (dic != null)
            {
                paramCount = dic.Count;
            }
            object[] parms = new object[paramCount];

            if (dic != null)
            {
                int count = 0;
                foreach (KeyValuePair&lt;string, object&gt; kvp in dic)
                {
                    Debug.WriteLine(string.Format(<span class="cpp-string">"Key = {0}, Value = {1}"</span>, kvp.Key, kvp.Value));
                    parms[count] = kvp.Value;
                    count++;
                }
            }

            <span class="cs-comment">// Get the method to invoke and invoke it</span>
            MethodInfo minfo = _type.GetMethod(methodName);
            object resp = minfo.Invoke(_typeInstance, parms);

            <span class="cs-comment">// Serialize the response</span><span class="cs-comment">// JSON Serializer @ http://www.newtonsoft.com/products/json/</span>
            string JSONResp = JavaScriptConvert.SerializeObject(new JsonResponse(resp));

            <span class="cs-comment">// Render the output to the context</span>
            context.Response.ContentType = <span class="cpp-string">"application/json"</span>;
            context.Response.AddHeader(<span class="cpp-string">"X-JSON"</span>, JSONResp);
            context.Response.Write(JSONResp);
        }

<span class="cs-preprocessor">        #endregion</span>
    }

    <span class="cs-comment">/// &lt;summary&gt;</span><span class="cs-comment">/// Wrapper for the JSON response</span><span class="cs-comment">/// &lt;/summary&gt;</span>
    public class JsonResponse
    {
        private object  _result = null;

        <span class="cs-comment">/// &lt;summary&gt;</span><span class="cs-comment">/// Gets or sets the result output.</span><span class="cs-comment">/// &lt;/summary&gt;</span><span class="cs-comment">/// &lt;value&gt;The result.&lt;/value&gt;</span>
        public object  Result
        {
            get { return _result; }
            set { _result = value; }
        }


        <span class="cs-comment">/// &lt;summary&gt;</span><span class="cs-comment">/// Initializes a new instance of the &lt;see cref="JsonResponse"/&gt; class.</span><span class="cs-comment">/// &lt;/summary&gt;</span><span class="cs-comment">/// &lt;param name="_result"&gt;The _result.&lt;/param&gt;</span>
        public JsonResponse(object _result)
        {
            this._result = _result;
        }
    }
}
</pre><p>When initialized, the WebServiceData object will try to get a <code>Type</code> from the webservice path. If successful, it will check if the webservice has the <code>AjaxRemoteProxyAttribute</code>, and if true, will extract the WebMethods list. </p><p>The <code>Invoke</code> method looks at the <code>context.Request.PathInfo</code> to see what method to execute. It also check if arguments are passed on the <code>context.Request.InputStream</code> and if so, adds them to the method call. In the end the response is serialized into a Json string and sent back to the client. </p><p>The Render method looks at all the WebMethods and creates the client-side code. </p><p>The <code>JsonResponse</code> class is used to simplify the serialization of the Json response.</p><p>With this we have completed the first big step: Build the necessary code to generate the proxy. </p><p>Now to help up "proxifing" the webservices, we will build a simple helper to use on the webforms: </p><div class="precollapse" id="premain7" style="WIDTH: 100%"><img id="preimg7" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="7" /><span id="precollapse7" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="7"> Collapse</span></div><pre lang="cs" id="pre7" style="MARGIN-TOP: 0px">using System.Collections.Generic;
using System.Web;
using System.Web.UI;

namespace CreativeMinds.Web.Proxy
{
    public static class ProxyBuilder
    {
<span class="cs-preprocessor">        #region Properties</span><span class="cs-comment">/// &lt;summary&gt;</span><span class="cs-comment">/// Gets or sets the get WS proxy list.</span><span class="cs-comment">/// &lt;/summary&gt;</span><span class="cs-comment">/// &lt;value&gt;The get WS proxy list.&lt;/value&gt;</span>
        public static List&lt;string&gt; WSProxyList
        {
            get
            {
                List&lt;string&gt; aux = HttpContext.Current.Cache[<span class="cpp-string">"WS_PROXIES_URL"</span>] as List&lt;string&gt;;
                HttpContext.Current.Cache[<span class="cpp-string">"WS_PROXIES_URL"</span>] = aux ?? new List&lt;string&gt;();
                return HttpContext.Current.Cache[<span class="cpp-string">"WS_PROXIES_URL"</span>] as List&lt;string&gt;;
            }
            set
            {
                HttpContext.Current.Cache[<span class="cpp-string">"WS_PROXIES_URL"</span>] = value;
            }
        }

<span class="cs-preprocessor">        #endregion Properties</span>


        public static void For(string wsPath)
        {
            if (!WSProxyList.Exists(delegate(string s) { return s == wsPath; }))
            {
                new WebServiceData(wsPath);
                WSProxyList.Add(wsPath);
            }
        }

        <span class="cs-comment">/// &lt;summary&gt;</span><span class="cs-comment">/// Renders all Webservice Proxies in the &lt;see cref="Page"/&gt;.</span><span class="cs-comment">/// &lt;/summary&gt;</span><span class="cs-comment">/// &lt;param name="page"&gt;The &lt;see cref="Page"/&gt; where the proxies will be generated and sused.&lt;/param&gt;</span>
        public static void RenderAllIn(Page page)
        {
            WSProxyList.ForEach(delegate(string virtualPath)
                                       {
                                           string FullPath = VirtualPathUtility.ToAbsolute(virtualPath + <span class="cpp-string">"/js"</span>);
                                           page.ClientScript.RegisterClientScriptInclude(string.Format(<span class="cpp-string">"WSPROXY:{0}"</span>, FullPath), FullPath);                               
                                       });
        }
    }
}
</pre><p>The <code>ProxyBuilder.For</code> method recieves a string with the virtual path to the webservice. With a valid path, this method will add a new <code>WebServiceData</code> object to the <code>WSProxyList</code> property. </p><p>When no more proxies are needed, the <code>ProxyBuilder.RenderAllIn</code> should be called. This will register all client script generated by our proxies. </p><pre lang="cs">protected void Page_Load(object sender, EventArgs e)
{
    ProxyBuilder.For(<span class="cpp-string">"~/teste.asmx"</span>);
    ProxyBuilder.RenderAllIn(this);
}
</pre><p>Browsing the page, we can now see the output for our webservice:</p><pre lang="jscript">RegisterNamespace(<span class="cpp-string">"CreativeMinds.Web.Services"</span>);
CreativeMinds.Web.Services.teste = function(){};
CreativeMinds.Web.Services.teste.HelloWorld = function(onCompleteHandler){
new Json.Remote(<span class="cpp-string">"/CreativeMindsWebSite/teste.asmx/HelloWorld"</span>, {onComplete: onCompleteHandler, method:'post'}).send();
}
CreativeMinds.Web.Services.teste.HelloYou = function(name, onCompleteHandler){
new Json.Remote(<span class="cpp-string">"/CreativeMindsWebSite/teste.asmx/HelloYou"</span>, {onComplete: onCompleteHandler, method:'post'}).send({<span class="cpp-string">"name"</span>:name});
}
</pre><p>Sweet! The generated javascript resembles our webservice class. We have the namespace <code>CreativeMinds.Web.Services</code> created, the class name <code>teste</code> its also there, and its webmethods. Notice that all method calls need a <code>onCompleteHandler</code>. This will handle all the successfully calls. </p><p>Only two step remaining: The Response Handler, and testing it all.</p><strong>Response Handler</strong><p>As you can see in the code generated by the proxy, the call to the webservice method doesn't change: </p><p><em>/CreativeMindsWebSite/teste.asmx/HelloWorld</em></p>So how can the know what to return - Json or XML?. Well, we will watch for the <code>context.Request.ContentType</code> and the <code>context.Request.Headers</code> on our <code>RestFactoryHandler</code> class. If one of thoose as Json on it we know what to do... :) 
<p>When a Json response is requested, the <code>RestFactoryHandler</code> will return the <code>RestClientResponseHandler</code>. </p><div class="precollapse" id="premain10" style="WIDTH: 100%"><img id="preimg10" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="10" /><span id="precollapse10" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="10"> Collapse</span></div><pre lang="cs" id="pre10" style="MARGIN-TOP: 0px">using System.Web;

namespace CreativeMinds.Web.Proxy
{
    public class RestClientResponseHandler : IHttpHandler
    {
<span class="cs-preprocessor">        #region IHttpHandler Members</span><span class="cs-comment">///&lt;summary&gt;</span><span class="cs-comment">///Enables processing of HTTP Web requests by a custom HttpHandler that implements the &lt;see cref="T:System.Web.IHttpHandler"&gt;&lt;/see&gt; interface.</span><span class="cs-comment">///&lt;/summary&gt;</span><span class="cs-comment">///</span><span class="cs-comment">///&lt;param name="context"&gt;An &lt;see cref="T:System.Web.HttpContext"&gt;&lt;/see&gt; object that         /// provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests. &lt;/param&gt;</span>
        public void ProcessRequest(HttpContext context)
        {
            WebServiceData wsd = context.Cache[<span class="cpp-string">"WS_DATA:"</span> + context.Request.FilePath] as WebServiceData;
            if (wsd != null)
            {
                wsd.Invoke(context);
            }
        }

        <span class="cs-comment">///&lt;summary&gt;</span><span class="cs-comment">///Gets a value indicating whether another request can use the &lt;see cref="T:System.Web.IHttpHandler"&gt;&lt;/see&gt; instance.</span><span class="cs-comment">///&lt;/summary&gt;</span><span class="cs-comment">///</span><span class="cs-comment">///&lt;returns&gt;</span><span class="cs-comment">///true if the &lt;see cref="T:System.Web.IHttpHandler"&gt;&lt;/see&gt; instance is reusable; otherwise, false.</span><span class="cs-comment">///&lt;/returns&gt;</span><span class="cs-comment">///</span>
        public bool IsReusable
        {
            get { return true; }
        }

<span class="cs-preprocessor">        #endregion</span>
    }
}
</pre><p>Again notice that it tries to get a <code>WebServiceData</code> object from the <code>context.Cache</code> and <code>Invoke</code> it passing the context as argument. The <code>Invoke</code> method of the <code>WebServiceData</code> will extract the method name form the PathInfo. Then it will create an instance from the Type, check for arguments passed on the post by checking the <code>Request.InputStream</code>. Using the Newtonsoft JavaScriptDeserializer we deserialize any arguments and add them to the object collection needed to invoke a method. Finally we invoke the method, serialize the response and send it back to the client. </p><div class="precollapse" id="premain11" style="WIDTH: 100%"><img id="preimg11" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="11" /><span id="precollapse11" style="MARGIN-BOTTOM: 0px; CURSOR: hand" preid="11"> Collapse</span></div><pre lang="cs" id="pre11" style="MARGIN-TOP: 0px">...

namespace CreativeMinds.Web.Proxy
{
    internal class WebServiceData
    {

...

        <span class="cs-comment">/// &lt;summary&gt;</span><span class="cs-comment">/// Invokes the requested WebMethod specified in the &lt;see cref="HttpContext"/&gt;.</span><span class="cs-comment">/// &lt;/summary&gt;</span><span class="cs-comment">/// &lt;param name="context"&gt;The &lt;see cref="HttpContext"/&gt;.&lt;/param&gt;</span>
        public void Invoke(HttpContext context)
        {
            <span class="cs-comment">// Method name to invoke</span>
            string methodName = context.Request.PathInfo.Substring(1);

            <span class="cs-comment">// We need an Instance of the Type</span>
            if (_typeInstance == null)
                _typeInstance = Activator.CreateInstance(_type);

            <span class="cs-comment">// Get the Posted arguments (format "json={JSON Object}")</span>
            string requestBody = new StreamReader(context.Request.InputStream).ReadToEnd();
            string[] param = requestBody.Split('=');
            <span class="cs-comment">// JSON Deserializer @ http://www.newtonsoft.com/products/json/</span>
            object a = JavaScriptConvert.DeserializeObject(param[1]);
            <span class="cs-comment">//object a = JavaScriptDeserializer.DeserializeFromJson&lt;object&gt;(param[1]);</span>
            Dictionary&lt;string, object&gt; dic = a as Dictionary&lt;string, object&gt;;
            int paramCount = 0;
            if (dic != null)
            {
                paramCount = dic.Count;
            }
            object[] parms = new object[paramCount];

            if (dic != null)
            {
                int count = 0;
                foreach (KeyValuePair&lt;string, object&gt; kvp in dic)
                {
                    Debug.WriteLine(string.Format(<span class="cpp-string">"Key = {0}, Value = {1}"</span>, kvp.Key, kvp.Value));
                    parms[count] = kvp.Value;
                    count++;
                }
            }

            <span class="cs-comment">// Get the method to invoke and invoke it</span>
            MethodInfo minfo = _type.GetMethod(methodName);
            object resp = minfo.Invoke(_typeInstance, parms);

            <span class="cs-comment">// Serialize the response</span><span class="cs-comment">// JSON Serializer @ http://www.newtonsoft.com/products/json/</span>
            string JSONResp = JavaScriptConvert.SerializeObject(new JsonResponse(resp));

            <span class="cs-comment">// Render the output to the context</span>
            context.Response.ContentType = <span class="cpp-string">"application/json"</span>;
            context.Response.AddHeader(<span class="cpp-string">"X-JSON"</span>, JSONResp);
            context.Response.Write(JSONResp);
        }
...
</pre><p>With this is are ready to test a call. So all we need to do is, first create the <code>onCompleteHandler</code> function to handle the response: </p><pre lang="jscript">    function completedHandler(json)
    {
        alert(json.Result);
    }
</pre>Then add a textbox to the page: <pre lang="html">    &lt;input type="textbox" id="txtName" /&gt;
</pre>Finally, a caller: <pre lang="html">    &lt;a href="#" onclick="CreativeMinds.Web.Services.teste.HelloYou($("textbox").value, complete)"&gt;call HelloYou&lt;/a&gt;
</pre><p>That's it. We have build a proxy generator. </p><p>Again this is a proof of concept, so its not tested for performance nor bug/error proof. </p><!-- Article Ends --><script src="http://www.codeproject.com/script/togglePre.js" type="text/javascript"></script></div></div>
<img src ="http://www.cnitblog.com/seeyeah/aggbug/24545.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-03-23 23:24 <a href="http://www.cnitblog.com/seeyeah/archive/2007/03/23/24545.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Applying Robustness Analysis on the Model–View–Controller (MVC) Architecture in ASP.NET Framework, using UML</title><link>http://www.cnitblog.com/seeyeah/archive/2007/03/20/24312.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Mon, 19 Mar 2007 17:09:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/03/20/24312.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/24312.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/03/20/24312.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/24312.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/24312.html</trackback:ping><description><![CDATA[
		<div style="FONT-WEIGHT: bold; FONT-SIZE: 16pt">
				<a href="/Files/seeyeah/ModelViewController_demo_src.zip">
						<font color="#002c99">ModelViewController_demo_src.zip</font>
				</a>
				<br />
				<hr />
				<br />Applying Robustness Analysis on the Model–View–Controller (MVC) Architecture in ASP.NET Framework, using UML</div>
		<b>By <a href="http://www.codeproject.com/script/Articles/list_articles.asp?userid=90779">Shams Mukhtar</a></b>. <br /><br /><div style="FONT-SIZE: 12px">This article will enhance your vision on the usage of Robustness Analysis in conjunction with Model View Controller, using UML with application in ASP.NET. This article is a sequel to my articles on Architecture and Design with ASP.NET.<br /><h2>Introduction</h2><p nd="1">Recently, I wrote <a href="http://www.codeproject.com/aspnet/Frame_Work_Design.asp"><font color="#002c99">an article</font></a> on the development of a master-page framework in ASP.NET. Master Page framework was an extension of the ASP.NET <code nd="2">UI.Page</code> framework, and most of it is related to the static appearance of the Page. Then, I started receiving emails about the dynamic behavior of pages, and controlling the interactions between user-controls on a page, and communication between different pages etc. So, I thought there is a need of discussion on this aspect of the .NET Framework, and this would not be possible without mentioning the Model-View-Controller Design Pattern, that is embedded everywhere in the ASP.NET Framework. This article is composed of two parts. In the first part, we will discuss in detail about the Model-View-Controller Design pattern in context to ASP.NET, using UML, and finish our discussion with writing an example <a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/aspnet/ModelViewController.asp#" target="_blank" itxtdid="3265760">application</a> as a bonus for its implementation. In the next part of the article, we’ll wrap up our discussion with dynamic aspects of pages by explaining different types of fine-grained controllers with their design and implementation, and we’ll discuss about the details of the HTTP pipeline too and what is the best approach to use it; so stay tuned, lot to come :).</p><h3>Model-View-Controller Design Pattern:</h3><p nd="3">This is the classical and most famous of all design patterns especially when we talk about User Interfaces. A user interface is basically a presentation layer of some sort of model-data. If the <a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/aspnet/ModelViewController.asp#" target="_blank" itxtdid="3265785">data</a> changes, the view is updated accordingly, or vice-versa. In this particular scenario, if you look closely, you can observe that there are two objects involved for performing this task, i.e., a <b>View</b> and a <b>Model</b>. There might be more than one view of a data model. Like, one view could be a Grid presentation, and other could be a Graphical Plot of some sort, like Bar chart or Pie chart, or so. Here is how it would look like:</p><p><a href="http://www.codeproject.com/aspnet/ModelViewController/image001_big.jpg" target="_blank"><img height="267" alt="image001.jpg" src="http://www.codeproject.com/aspnet/ModelViewController/image001.jpg" width="600" /></a></p><p nd="4">I know, you should have these questions now, like how the view object would talk with the model object and vice versa, and how we want to look at the model, which part of the model will go to which view and which part of the view and how. Now, we are talking about one other object, the controller object; this controller object acts as glue between model and view. Controller object is basically a business object, provides the answer how we want to view the data, what rules should be applied for its presentation or so, and what will happen if some one changes some parameters in the view, how to react to some key-board or mouse events if this is an interactive application, and some times you need to update the model and send messages to other views that the data is changed, so that they can postback a request for updating their own contents. You will get all these answers shortly!</p><h3>Robustness Analysis:</h3><p nd="5">This Model-View-Controller can be best described visually, using robustness analysis, first introduced by Ivar Jacobson, in his award winning book on Object Oriented Software Engineering (see reference below), and was further explained by Doug Rosenberg et al in his book, Use Case Driven Object Modeling with UML. Robustness Analysis involves analyzing the narrative text of use cases, identifying a first-guess set of objects that will participate in those use cases, and classifying these objects based on the roles they play, and it helps you partition objects within a Model-View-Controller paradigm. Robustness analysis enables the ongoing discovery of objects, and helps you ensure that you've identified most of the major domain classes before starting any additional design or development. Ivar has classified them as:</p><ol><li nd="6">«entity» objects depict long lived objects, deals mostly with the persisted states. 
</li><li nd="7">«boundary» objects depict links between the system and environment, communicating. 
</li><li nd="8">«control» objects depict use-case-specific behavior. </li></ol><p nd="9"><b>Entity objects</b> are no more than the information or data your boundary objects are looking for. These might be <a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/aspnet/ModelViewController.asp#" target="_blank" itxtdid="3265794">database</a> tables, Excel files, or might be "transient" session or cached data or so.</p><p nd="10"><b>Boundary objects</b> are the objects with which the actors (for instance, the users) will be communicating in your software system. These might be any windows, screens, dialogs and menus, or other UIs in your system. You can easily identify them while analyzing your use cases.</p><p nd="11"><b>Control objects</b> (controllers) are the business objects or your business web-services. This is where you capture your business rules that are used to filter out the data to be presented to the user he has requested for or something. So, the controllers are actually controlling the businesses needs or the business itself.</p><p nd="12">Here are the visual icons of these three UML stereotypes:</p><p><img height="200" alt="image003.jpg" src="http://www.codeproject.com/aspnet/ModelViewController/image003.jpg" width="534" /></p><h3>Rules of the game:</h3><p nd="13">Here are the four basic rules you should enforce when extracting these objects while analyzing your use cases and making interaction diagrams among these objects:</p><ul><li nd="14">Actors can only talk to the boundary objects. 
</li><li nd="15">Boundary objects can only talk to controllers and actors. 
</li><li nd="16">Entity objects can only talk to controllers. 
</li><li nd="17">Controllers can talk to boundary objects and entity objects, and to other controllers, but not to actors. </li></ul><p><img height="360" alt="image005.jpg" src="http://www.codeproject.com/aspnet/ModelViewController/image005.jpg" width="537" /></p><p nd="18">If you look at the above figure, you can see the only access point for an actor to reach any object is through the Boundary objects. Boundary objects can’t talk with each other. While Control objects can talk freely with all of the objects except that it has no access to the actor or the user of the system. And the Entity objects can’t talk with each other too; they can access each other but through the Control object. Also, an Entity object can only reach Boundary object through the Control object. So the Control object is really a media of communication between each layer, and that’s why it is suited for business rules.</p><h3>Robustness Analysis and Model-View-Controller:</h3><p nd="19">Now, we have all the rules in place, let’s see how these objects are related to Model-View-Controller paradigm. Well, good news is this, that they have one-to-one mapping with the objects, derived from this analysis:</p><ul><li nd="20">Entity object maps to Model object, 
</li><li nd="21">Boundary object maps to View object, and 
</li><li nd="22">Controller is same in both. </li></ul><p nd="23">And the other good news is that the same rules are applied to these objects too. It means, when we are doing Robustness Analysis, we can use Model-View-Controller objects in place of Entity-Boundary-Controller objects. Here is how our objects would look like now:</p><p><img height="200" alt="image007.jpg" src="http://www.codeproject.com/aspnet/ModelViewController/image007.jpg" width="503" /></p><p nd="24">And here is the simplistic and hypothetical sequence diagram for MVC. What you see in this diagram, a web-user initiated a query and an event is generated that is handled by the controller and gets information that is needed from the model, validates the information and passes back the result set to the view.</p><p><img height="484" alt="image009.jpg" src="http://www.codeproject.com/aspnet/ModelViewController/image009.jpg" width="600" /></p><p nd="25">Model-View-Controller is an approach for segregating different layers, in such a manner that it can be easy to maintain for a moderate to very complex application. It would be best suitable when you are making something for the web, where the business rules change frequently with time. Also, it’s the best way of writing applications in a service oriented architecture, where you write/encapsulate your business rules in a separate web-service than the Data-Access Layer, where the service is only dealing with Model/Data related stuff. Since the user interfaces are also separate, you can build powerful Custom Controls, and put them in the User-Controls for deploying as Pagelets. And finally, what is left, you need just to build proper interactions for them, enforcing rules mentioned above, while designing any solution.</p><h3>Examples:</h3><p nd="26">So, let’s see where we stand now: we now know what these model-view-controller objects are, how they look like, and what are the rules we should enforce when we do analysis of a particular use case scenario and making interaction diagrams out of them. Let’s take an example scenario and explain it using this technique!</p><h4>Use case (UC.1.001):</h4><p nd="27"><u nd="27">User shall be able to logon to the web-application.</u></p><p nd="28">Here is the UML diagram for this use case; it shows an actor (web-user) and logon use-case relationship, it would more be elaborated when we go into the analysis phase of it. I am taking a very simple case, of login and deferring the registration use-case for our example, for making it simple and understandable at the first go.</p><p><img height="216" alt="image011.jpg" src="http://www.codeproject.com/aspnet/ModelViewController/image011.jpg" width="379" /></p><p nd="29">Before going into any analysis, it would be better, or I call it, it’s a must, that you should have the visual prototype in front of you. Here is how our prototype would look like:</p><p><img height="150" alt="image013.jpg" src="http://www.codeproject.com/aspnet/ModelViewController/image013.jpg" width="282" /></p><p nd="30">What it means, that the user is presented with a logon screen, where he can enter his identity and press the Login button for access to the system. The user can also turn on the check-mark on the check box for remembering him; in that case, a cookie is needed on his system, to be enabled. Also, when he logs in, a new session is created on the server side for keeping his activities alive while using the web-application. With all these, let’s elaborate this scenario using the Robustness Analysis diagrams:</p><p><a href="http://www.codeproject.com/aspnet/ModelViewController/image015_big.jpg" target="_blank"><img height="371" alt="image015.jpg" src="http://www.codeproject.com/aspnet/ModelViewController/image015.jpg" width="600" /></a></p><p nd="31">Let’s take a closer look at the above diagram. User interacts with Logon Screen (the view) that has one submit button for login (has-a relationship in UML is shown with a diamond shaped line at one end), and has a checkbox for enabling the cookie storage or so. What happens when the user clicks on any of these two buttons, the request goes to the <code nd="32">MainController</code> object, which then decides what to do with these actions, since it has all the logic built for handling those events, and then does some validations from security control object that in turn takes data from the Data Access object? <code nd="33">MainController</code> then creates a session for this user, it also stores user's info like encrypted passwords, in somewhere in the cookie store or so. With this analysis, we are now very much clear that, apart from the logon screen, there would be four other objects involved in this scenario; <code nd="34">MainController</code>, <code nd="35">SessionState</code> object, the <code nd="36">CookieStore</code> object and the Database Access objects. It does have given you an idea what are the objects required to do a particular job, but it does not tell you what functionality is needed for accomplishing this task. For that, we need a sequence diagram, here is how the sequence diagram should look like:</p><p><a href="http://www.codeproject.com/aspnet/ModelViewController/image017_big.jpg" target="_blank"><img height="468" alt="image017.jpg" src="http://www.codeproject.com/aspnet/ModelViewController/image017.jpg" width="600" /></a></p><p nd="37">After careful study of the above sequence diagram, we can safely classify our objects as there should be a <code nd="38">MainController</code>, that takes care the business logic for the page, and a <code nd="39">BusinessService</code> object that takes care of all the validation, Session Management and cookie setup stuff. There would be a <code nd="40">DataAccessGateWay</code> (model) object that encapsulates all the details of talking with the back end database. Here is how the class diagram looks like:</p><p><a href="http://www.codeproject.com/aspnet/ModelViewController/image019_big.jpg" target="_blank"><img height="302" alt="image019.jpg" src="http://www.codeproject.com/aspnet/ModelViewController/image019.jpg" width="600" /></a></p><p nd="41">Isn’t it beautiful that all of this robustness analysis leads us to a design where we can define our first set of detailed class diagrams? I know you’ll love it.</p><p nd="42">The most important of all of the objects above, are the business objects that are the controller objects. Now, let’s have a closer look at how ASP.NET framework facilitates us in defining these Controller classes.</p><h3>ASP.NET and Model-View-Controller (MVC)</h3><p nd="43">Basic idea of MVC is to segregate the presentation layer from the business layer and the Data Access Layer, or the model. Let’s see how it is done in ASP.NET.</p><p><img height="263" alt="image021.jpg" src="http://www.codeproject.com/aspnet/ModelViewController/image021.jpg" width="413" /></p><p nd="44">Let’s have a closer look at this picture, <code nd="45">Controller</code> is bound to some model, and updates the <code nd="46">View</code> when the data changes or vice-versa. So, the view depends on the model, passively. We achieve this goal, with the code-behind feature of ASP.NET, the page/user-controls logic is separate from that of the view, that is presented either in “*.aspx” files or in “*.ascx” files. And the controls you add to the page-controller, i.e. <code nd="47">System.UI.Page</code>, you try to make them data-bounded as much as possible for consistency and synchronization. And also, you embed your event handler’s logic in the page controller using the code-behind feature. And if your architecture is service-oriented architecture, then you can introduce another controller as a business service (might be security service) that talks to the back-end model as needed and returns secured data to the Page Controller. Here is how it would look like:</p><p><img height="600" alt="image023.jpg" src="http://www.codeproject.com/aspnet/ModelViewController/image023.jpg" width="597" /></p><h3>It's Showtime (bonus++):</h3><p nd="48">As a bonus, we would be developing a full fledged master-details web application that will give you enough details about model-view-controller (MVC) and its inner working, as well as a way of communication between different user-controls without even knowing each other. Also, with it you’ll get a reusable library that is composed of classes <code nd="49">OleDataLinkAdapter</code> and <code nd="50">MsSqlDataLinkAdapter</code>. These objects are wrappers around ADO.NET Connection and Command objects.</p><h4>Master-Details ASP.NET Application</h4><p nd="51">The application is a web-application that has a master-page composed of a company logo, a banner, main contents, and footer. Main contents should display a parent-child relationship in the form of grid-view. There shall be two grids, the top one would be the master grid and the bottom would be the details one. When the user clicks on any item in the master view, the details/child view changes accordingly and should display the related child data.</p><p nd="52">Use-Case001: User shall be able to view/manipulate the master-details for Northwind Customer/Orders tables.</p><p><img height="240" alt="image025.png" src="http://www.codeproject.com/aspnet/ModelViewController/image025.png" width="351" /></p><p nd="53">We would be applying the same technique as in my previous article, that is Pattern Oriented Architecture and Design (POAD). For this, we need the prototypic pattern of our system we are planning to design. And here is how the prototype would look like:</p><p><a href="http://www.codeproject.com/aspnet/ModelViewController/image027_big.png" target="_blank"><img height="431" alt="image027.png" src="http://www.codeproject.com/aspnet/ModelViewController/image027.png" width="600" /></a></p><p nd="54">So, what we see is what we get, that is, our application would be composed of a <code nd="55">MasterPage</code>, an HTML frame container, then HTML Table, and this table would contain further the two controls, Master-User-Control and the Details-User-Control. These user controls in turn would contain the <code nd="56">MasterDataGrid</code> and <code nd="57">DetailsDataGrid</code> Views. Here is the static inventory of the objects, needed to fulfill this job:</p><p><img height="288" alt="image029.png" src="http://www.codeproject.com/aspnet/ModelViewController/image029.png" width="549" /></p><h4>Robustness Analysis of the Use-Case001</h4><p nd="58">We now have some understandings of the objects needed to fulfill this job, but before going into the detailed design, we need to analyze the use cases, to find-out the proper interactions and relationships among them. This would be done using the Robustness Analysis, while keeping into our mind the MVC concept. Let's draw the conceptual Robustness Diagram for this scenario:</p><p><a href="http://www.codeproject.com/aspnet/ModelViewController/image031_big.png" target="_blank"><img height="332" alt="image031.png" src="http://www.codeproject.com/aspnet/ModelViewController/image031.png" width="600" /></a></p><p nd="59">What it means, that the user is presented with a master-details view. The upper Master-Grid-view is used to select the master table items. So, whenever the user selects master item, the child view is updated accordingly. Let's see how we achieve these interactions between objects. Look at the above diagram closely and you will see that when the user selects an item from the master-grid-view, the <code nd="60">MasterUserControl</code> would be notified, and that in turn notifies this event to the listener, that is the <code nd="61">MainController</code> object. <code nd="62">MainController</code> object would notify about this event to the other listener, the <code nd="63">DetailsUserControl</code>.</p><p nd="64">These user-controls have direct access to the Model (<code nd="65">DataAccessGateway</code>) that uses <code nd="66">MsSqlDataLinkAdapter</code> to establish MS-SQL database connections (for OLEDB connections, use the other adapter, <code nd="67">OleDataLinkAdapter</code>). This is a classic example of the MVC and its inner working. Here is the object interaction diagram for this particular scenario:</p><p><img height="432" alt="image033.png" src="http://www.codeproject.com/aspnet/ModelViewController/image033.png" width="600" /></p><p nd="68">The <code nd="69">MasterController</code> and the <code nd="70">DetailsController</code> are differential Controllers, that they are responsible of controlling one User-Control at a time and fine tune them while the <code nd="71">MainController</code> object acts as an integrator, or a channel for these two controllers to pass information to and fro between them. Let's see how we are planned for them to work. For an object being a Notifying object, needs to implement the <code nd="72">INotifier</code> interface, while a Listener object needs to implement the <code nd="73">IListner</code> interface. Also, a listener object needs to attach itself with the relevant notifier in-order for being notified. In our case, we have the Master-Controller and, the Details-Controller is the listener. <code nd="74">MainController</code> is both listener and notifier, and acts as glue between them.</p><p nd="75">Now, at this point, we have all the information about the objects, responsible for doing the job, how they would look like, and about their behavior and functionality. Let's now design them and develop the complete class design. And here is the detailed class diagram for this application:</p><p><a href="http://www.codeproject.com/aspnet/ModelViewController/image035_big.png" target="_blank"><img height="455" alt="image035.png" src="http://www.codeproject.com/aspnet/ModelViewController/image035.png" width="600" /></a></p><h2>Implementation using C# and ASP.NET</h2><p nd="76">Code hierarchy is shown in the following figure, where you will find DataAccess adapters in <i>Shams.Data.dll</i>, updated MasterPage Framework library in <i>Shams.Web.UI.MasterPages.dll</i>, and I have collected some libraries from different sources on the web, you’ll find them under <i>Shams.Web.UI.WebControls.dll</i>. The sample application is in <code nd="77">Shams.MVC.WebApp</code> namespace. All you need is to open <i>Shams.MVC.WebApp</i> solution and you are ready to go. And verify the virtual path in the file “<i>Shams.MVC.WebApp.csproj.webinfo</i>” to be created in the IIS. Here is the one for it:</p><pre lang="text" nd="78">&lt;VisualStudioUNCWeb&gt;

&lt;Web URLPath = "http://localhost/shams/MVC/WebApp/Shams.MVC.WebApp.csproj" /&gt;

&lt;/VisualStudioUNCWeb&gt;</pre><p><img height="595" alt="image037.png" src="http://www.codeproject.com/aspnet/ModelViewController/image037.png" width="530" /></p><p nd="79">Here are some code snippets, from the application, starting with the <i>web.config</i>. The <code nd="80">AppSettings</code> section in the <i>web.config</i> file would contain two things for this application, the connection string for the NorthWind <code nd="81">DataTable</code>, and the path to the <code nd="82">MasterPageUserControl</code>.</p><p nd="83">Here is how it does look like:</p><pre lang="xml" nd="84">&lt;appSettings&gt;
  &lt;add key="MasterPageUserControl" value="MasterPageUserControl.ascx"/&gt;
  &lt;add key="ConnectString001" 
       value="server=localhost;database=northwind;Integrated Security=true;"/&gt;
  &lt;add key="ConnectString002" 
       value="Provider=SQLOLEDB;server=localhost;database=northwind;
                                        Integrated Security=true;"/&gt;
&lt;/appSettings&gt;</pre><p nd="85">The model is the <code nd="86">DataAccessGateway</code> class and it returns the <code nd="87">DataSet</code> that’s been used in the Master-Details data grid controls. Here is how it looks like:</p><p><img height="597" alt="image039.png" src="http://www.codeproject.com/aspnet/ModelViewController/image039.png" width="600" /></p><p nd="88">It uses the <code nd="89">MsSqlDataLinkAdapter</code> that is a wrapper around <b>ADO.NET</b> connection and command objects. This is a singleton class, with all of its functions being static. Here is the skeleton of this class:</p><p><a href="http://www.codeproject.com/aspnet/ModelViewController/image041_big.png" target="_blank"><img height="500" alt="image041.png" src="http://www.codeproject.com/aspnet/ModelViewController/image041.png" width="600" /></a></p><p nd="90">In our particular scenario, <code nd="91">DataAccessGateway</code> uses this class, to connect to the MS-SQL NorthWind database, by calling the <code nd="92">Connect(.)</code> function of it. Once you have this connection object, you call another function <code nd="93">FilldataSet(.)</code> to return you a brand-new dataset being used for data-binding. Once it is done, you call <code nd="94">Disconnect(.)</code> to close the connection object.</p><p nd="95">Other notable classes are the controller classes, <code nd="96">UcMasterDetails</code> (Main-Controller) and two other classes, <code nd="97">UcMainContentsMaster</code> (Master Controller) and <code nd="98">UcMainContentsDetails</code> (Details Controller). Here is how we connected these two classes with each other using <code nd="99">UcMasterDetails</code> as a mediator.</p><p><a href="http://www.codeproject.com/aspnet/ModelViewController/image043_big.png" target="_blank"><img height="443" alt="image043.png" src="http://www.codeproject.com/aspnet/ModelViewController/image043.png" width="600" /></a></p><p nd="100">And finally! Here is how the application would look like when you run it. :)</p><p><a href="http://www.codeproject.com/aspnet/ModelViewController/image045_big.png" target="_blank"><img height="347" alt="image045.png" src="http://www.codeproject.com/aspnet/ModelViewController/image045.png" width="600" /></a></p><p nd="101">The MVC pattern I used in the application is the minimal use of it, but it’s the best pattern being used in all the navigations between pages and between controls. It also depends on how you plot a plan for your application in making a prototype application. So folks, that’s it for now, I have tried to un-cover everything about the MVC design pattern, with some useful applications for its implementation. This is one of the most commonly used and misunderstood pattern. I tried to make it simple and give you all the details it carries, along with the robustness analysis, that’s the key in analyzing such applications.</p><h4>Improvement:</h4><p nd="102">There is always a point of improvement in any system, and the same here is applicable too. I haven’t talked about any efficiency related stuff, because that itself needs a full article. So, I am putting this responsibility on you to go through the documentation for improving efficiency of ASP.NET applications, like Caching, View-State Management and Session Management, and How to avoid round-trips to the server, like post-backs etc. And I would appreciate if you involve me in your findings, thank you very much.</p><h2>Question-Answers Session:</h2></div><img src ="http://www.cnitblog.com/seeyeah/aggbug/24312.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-03-20 01:09 <a href="http://www.cnitblog.com/seeyeah/archive/2007/03/20/24312.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>NHibernate Best Practices with ASP.NET, Generics, and Unit Tests</title><link>http://www.cnitblog.com/seeyeah/archive/2007/03/20/24310.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Mon, 19 Mar 2007 17:04:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/03/20/24310.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/24310.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/03/20/24310.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/24310.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/24310.html</trackback:ping><description><![CDATA[
		<div style="FONT-WEIGHT: bold; FONT-SIZE: 16pt">
				<a href="/Files/seeyeah/NHibernateBestPractices_src.zip">
						<font color="#002c99">NHibernateBestPractices_src.zip</font>
				</a>
				<br />
				<hr />
				<br />NHibernate Best Practices with ASP.NET, Generics, and Unit Tests</div>
		<b>By <a href="http://www.codeproject.com/script/Articles/list_articles.asp?userid=2797038">Billy McCafferty</a></b>. <br /><br /><div style="FONT-SIZE: 12px">This article describes best practices for leveraging the benefits of NHibernate, ASP.NET, Generics, and unit testing together.<br /><br /><h2>Introduction</h2><p nd="1">NHibernate, like other ORM tools, has alleviated the maintenance of thousands of lines of code and stored procedures, thus allowing developers to focus more attention on the core of a project: its domain model and business logic. Even if you automatically generate your ADO.NET data-access layer using a tool such as CodeSmith or LLBLGen Pro (both great tools), NHibernate provides the flexibility in decoupling your business model from your relational model. Your database should be an implementation detail that is defined to support your domain model, not the other way around. As forums quickly fill with heated debates concerning these points, this article will not focus on proving the benefits of using an ORM tool over ADO.NET, or NHibernate over another ORM tool, but on describing best practices for integrating NHibernate into ASP.NET using well established design patterns and "lessons learned from the field".</p><p nd="2">This article assumes a good understanding of C# and NHibernate, experience with the <a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/aspnet/NHibernateBestPractices.asp#" target="_blank" itxtdid="3265785">Data</a> Access Object pattern, and at least a basic familiarity with Generics. If you're just getting acquainted with NHibernate, I'd recommend reading these two great introductions at TheServerSide.net: <a href="http://www.theserverside.net/articles/showarticle.tss?id=NHibernate" target="_blank">Part 1</a> and <a href="http://www.theserverside.net/articles/showarticle.tss?id=NHibernateP2" target="_blank">Part 2</a>. For an extensive overview of the <a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/aspnet/NHibernateBestPractices.asp#" target="_blank" itxtdid="3265786">Data Access</a> Object (DAO) design pattern, go to <a href="http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html" target="_blank">J2EE's BluePrints catalog</a>.</p><p nd="3">In building solid data integration within an ASP.NET 2.0 <a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="http://www.codeproject.com/aspnet/NHibernateBestPractices.asp#" target="_blank" itxtdid="3265760">application</a>, we aim to achieve the following objectives:</p><ul><li nd="4">Presentation and business logic layers should be in blissful ignorance of how they communicate with the database. You should be able to modify your means of data communication with minimal modification to these layers. 
</li><li nd="5">Business logic should be easily testable without depending on a live database. 
</li><li nd="6">NHibernate features, such as lazy-loading, should be available throughout the entire page life-cycle. 
</li><li nd="7">.NET 2.0 Generics should be leveraged to alleviate duplicated code. </li></ul><p nd="8">A sample application has been included that demonstrates the merging of NHibernate, ASP.NET, and .NET Generics while meeting the above objectives. What follows is a description of how each of the aforementioned objectives has been tackled in the application. But before getting into the implementation details, let's skip right to the chase and get the sample up and running.</p><h2>The Sample Application</h2><p nd="9">The sample application, at the risk of being terribly cliché, utilizes the Northwind database within SQL Server 2000 to view and update a listing of Northwind customers. To demonstrate the use of lazy-loading, the application also displays the orders that each customer has made. All you need to run the sample locally is IIS with the .NET 2.0 Framework installed, and SQL Server 2000 containing the Northwind database.</p><p nd="10">To get the application up and running:</p><ol><li nd="11">Unzip the sample application to the folder of your choice. 
</li><li nd="12">Open <i>NHibernateSample.Web/web.config</i> and <i>NHibernateSample.Tests/App.config</i> to modify the database connection strings to connect to a Northwind database on SQL Server 2000. 
</li><li nd="13">Create a new virtual directory within IIS. The alias should be <i>NHibernateSample</i>, and the directory should point to the <i>NHibernateSample.Web</i> folder created after unzipping the application. 
</li><li nd="14">Open your web browser to <i>http://localhost/NHibernateSample/ViewCustomers.aspx</i>, and you're off and running! </li></ol><p nd="15">Now that you're able to follow along with the example in front of you, we'll examine how the application was developed to meet our design objectives...</p><h2>Meeting the Design Objectives</h2><p nd="16">When writing an ASP.NET application, my primary goals are:</p><ul><li nd="17">Write code once and only once. (Hey, doesn't that sound like XP?) 
</li><li nd="18">Focus on simplicity and readability. 
</li><li nd="19">Keep coupling and dependencies to a minimum. </li></ul><p nd="20">To assist with keeping logical tiers loosely coupled, the included sample application is split into four projects: Web, Core, Data, and Tests. In peeling the layers of the onion, let's begin with the simple presentation/controller layer and work our way down.</p><h3>NHibernateSample.Web</h3><p nd="21">As expected, the <i>NHibernateSample.Web</i> project contains application configuration and web pages. In this sample, the code-behind pages act as controllers, communicating with the business and data access layers accordingly. Arguably, this is not best-practice MVC separation, but it's simple, and serves well for the demonstration. (I'll leave the thoroughly debated MVC-and-ASP.NET discussion for another day.)</p><p nd="22">Here's a closer look at some of the more interesting bits...</p><h4>Open Session in View</h4><p nd="23">If you want to leverage NHibernate's lazy-loading (which you most definitely will), then the <a href="http://www.hibernate.org/43.html" target="_blank">Open-Session-in-View</a> pattern is the way to go. ("Session" in this context is the NHibernate session...not the ASP.NET "<code nd="24">Session</code>" object.) Essentially, this pattern suggests that one NHibernate session be opened per HTTP request. Although session management within the ASP.NET page life-cycle is clear in theory, it varies widely in available implementation approaches. The approach I've taken is to create a dedicated HTTPModule to handle the details of the pattern. Aside from centralizing session management responsibilities, this approach provides the additional benefit that we may implement the Open-Session-in-View pattern without putting any session management code into our code-behind pages.</p><p nd="25">To see how this has been implemented, take a look at the class <code nd="26">NHibernateSessionModule</code> under the <i>App_Code</i> directory. The following section is included in the <i>web.config</i> to activate the HTTPModule:</p><pre lang="xml" nd="27">&lt;httpModules&gt;
  &lt;add name="NHibernateSessionModule" 
       type="NHibernateSample.Web.NHibernateSessionModule" /&gt;
&lt;/httpModules&gt;</pre><p nd="28">The HTTPModule included in the sample application begins a transaction at the beginning of the web request, and commits/closes it at the end of the request. There's very little overhead associated with this as NHibernate doesn't actually open a database connection until needed. As we'll see, you may switch out this strategy with others exposed by the session manager. Other strategies that you may want to consider are opening a session <i>not</i> associated with a transaction and/or registering an <code nd="29">IInterceptor</code> with the NHibernate session. (Using an <code nd="30">IInterceptor</code> is great for auditing purposes. See <a href="http://www.amazon.com/gp/product/193239415X/102-1170069-1054529?v=glance&amp;n=283155" target="_blank">Hibernate in Action</a>, section 8.3.2 - Audit Logging, for more details.)</p><h4>NHibernate Settings Within web.config</h4><p nd="31">There are two key settings within <i>web.config</i> to optimize NHibernate: <code nd="32">hibernate.connection.isolation</code> and <code nd="33">hibernate.default_schema</code>. By default, NHibernate uses <code nd="34">IsolationLevel.Unspecified</code> as its database isolation level. In other words, NHibernate leaves it up to the ADO.NET provider to determine what the isolation level is by default. If the provider you're using has a default isolation level of <code nd="35">Serializable</code>, this is a very strict level of isolation that can be overkill for most application scenarios. A more reasonable setting to start with is <code nd="36">ReadCommitted</code>. With this setting, "reading transactions" don't block other transactions from accessing a row. However, an uncommitted "writing transaction" blocks all other transactions from accessing the row. Other provider defaults include (note that they are subject to change by version):</p><ul><li nd="37">SQL Server 2000 - Read Committed 
</li><li nd="38">SQL Server 2005 - Read Committed 
</li><li nd="39">Firebird - Read Committed 
</li><li nd="40">MySQL's InnoDB - Repeatable Read 
</li><li nd="41">PostgreSQL - Read Committed 
</li><li nd="42">Oracle - Read Committed </li></ul><p nd="43">The other optional setting not to be ignored, <code nd="44">hibernate.default_schema</code>, is easily overlooked, but can have a significant impact on the querying performance. By default, table names within prepared NHibernate queries - such as those from <code nd="45">CreateCriteria</code> - are not fully qualified; e.g., <i>Customers</i> vs. <i>Northwind.dbo.Customers</i>. The crux of the problem is that <i>sp_execsql</i>, the stored procedure used to execute NHibernate queries, does not efficiently optimize queries unless the table names are fully qualified. Although this is a small syntactic difference, it can slow query speeds by as much as an order of magnitude in some cases. Explicitly setting <code nd="46">hibernate.default_schema</code> can provide as much as a 33% overall performance gain on data intensive pages. The following is an example of declaring these settings in <i>web.config</i>:</p><pre lang="xml" nd="47">&lt;add key="hibernate.connection.isolation" value="ReadCommitted" /&gt;
&lt;add key="hibernate.default_schema" value="Northwind.dbo" /&gt;</pre><p nd="48">In order to decouple the implementation details of data access from the <i>NHibernateSample.Web</i> project, NHibernate session management has been completely separated into the <i>NHibernateSample.Data</i> project. To inform NHibernate where the <i>embedded</i> HBM mapping files reside, define a <i>web.config</i> setting named <code nd="49">HBM_ASSEMBLY</code>. NHibernate will review this assembly for embedded HBM files to load object mappings. (This is not a setting used directly by NHibernate, but by a custom class we'll be reviewing soon.)</p><h4>A Simple List and Update Form</h4><p nd="50">The web project contains two web pages: <i>ViewCustomers.aspx</i> and <i>EditCustomer.aspx</i>. (I'll give you three guesses to figure out what they do.) The important thing to note is that the code-behind pages work with a DAO factory to talk to the database; i.e., the code isn't bound to a concrete implementation of a data access object. This makes it much easier to swap DAO implementations and unit test your code without depending on a live database. With everything in place, the following is an example of how easy it is to retrieve all the customers in the database:</p><pre lang="cs" nd="51">IDaoFactory daoFactory = <span class="cs-keyword" nd="52">new</span> NHibernateDaoFactory();
ICustomerDao customerDao = daoFactory.GetCustomerDao();
IList&lt;Customer&gt; allCustomers = customerDao.GetAll();</pre><p nd="53">In the above example, a concrete reference to <code nd="54">NHibernateDaoFactory</code> is retrieved via <code lang="cs"><span class="cs-keyword" nd="55">new</span></code>. In production code, this reference may (should?) be "injected" at runtime using a technique called Inversion of Control (IoC), or "Dependency Injection". Martin Fowler has written <a href="http://www.martinfowler.com/articles/injection.html" target="_blank">a great introduction to this pattern</a>. Think of it as code decoupling on steroids. With IoC, it's possible to remove many direct instantiations of concrete objects along with the inflexibility that comes along with these dependencies. The many benefits of IoC include a flexible framework, an emphasis on coding-to-interface, and extreme ease of unit testing. The drawback is another layer of complexity within the application. Here are two great tools for putting IoC into practice:</p><ul><li nd="56"><b><a href="http://www.springframework.net/" target="_blank">Spring .NET</a></b>: This framework provides IoC declared via XML configuration files. Spring .NET also has a number of other powerful modules, making it an attractive option if you're looking for more than just IoC. This is the framework I'm currently using for IoC, but it's not the only good choice out there. 
</li><li nd="57"><b><a href="http://www.castleproject.org/index.php/Windsor_Container" target="_blank">Castle Windsor</a></b>: The Castle Windsor Container project provides good IoC support with a combination of configuration and strongly typed declarations. Some of the advantages Castle Windsor brings to the table are less XML and more compile-time error catching. Like Spring .NET, the Castle project also provides a wide assortment of additional development utilities. </li></ul><p nd="58">While it is acceptable to use the <code lang="cs"><span class="cs-keyword" nd="59">new</span></code> keyword to create <code nd="60">NHiberanteDaoFactory</code> within your code-behind, or controller, your business objects should never create this dependency directly. Instead, their DAO dependency should be provided via a public setter or via a constructor. (IoC can help here as well.) This greatly enhances your ability to unit test with "mock" DAO classes. For example, the following code, found within <code nd="61">NHibernateSample.Tests.Data.CustomerDaoTests</code>, retrieves a customer and gives it its DAO dependency:</p><pre lang="cs" nd="62">IDaoFactory daoFactory = <span class="cs-keyword" nd="63">new</span> NHibernateDaoFactory();
ICustomerDao customerDao = daoFactory.GetCustomerDao();

Customer customer = customerDao.GetById(<span class="cpp-string" nd="64">"CHOPS"</span>, <span class="cs-keyword" nd="65">false</span>);
<span class="cs-comment" nd="66">// Give the customer its DAO dependency via a public setter</span>
customer.OrderDao = daoFactory.GetOrderDao();</pre><p nd="67">Using this technique, the business layer never needs to depend directly on the data layer. Instead, it depends on interfaces defined within the same layer, as we'll see next within the <i>NHibernateSample.Core</i> project.</p><h3>NHibernateSample.Core</h3><p nd="68">The <i>NHibernateSample.Core</i> project contains the domain model and NHibernate HBM files. This project also contains interfaces to data access objects in the <code nd="69">NHibernateSample.Core.Data</code> namespace. (Arguably, the HBM files belong in the <i>NHibernateSample.Data</i> project, but the maintenance convenience of having the HBM files physically close to the domain objects they describe far outweighs this break in encapsulation.)</p><h4>Data Dependency Inversion</h4><p nd="70">You'll notice that the <i>NHibernateSample.Core</i> project does not contain implementation details of data access objects, only interfaces describing the services it needs. The concrete DAO classes which implement these interfaces are found within <i>NHibernateSample.Data</i>. This is a technique called <a href="http://www.martinfowler.com/eaaCatalog/separatedInterface.html" target="_blank">Separated Interface</a> by Martin Fowler or "Dependency Inversion" by Robert Martin in <a href="http://www.amazon.com/gp/product/0135974445/ref=pd_lpo_k2a_1_img/102-1170069-1054529?%5Fencoding=UTF8" target="_blank">Agile Software Development</a>. Considering <i>NHibernateSample.Core</i> as an "upper-level layer" and <i>NHibernateSample.Data</i> as a "lower-level layer", then, as Martin describes, "each of the upper-level layers declares an abstract interface for the services that it needs. The lower-level layers are then realized from these abstract interfaces. ... Thus, the upper layers do not depend on the lower layers. Instead, the lower layers depend on abstract service interfaces <i>declared</i> in the upper layers". Dependency inversion is the perfect technique for breaking a bi-directional dependency between domain and data layers.</p><p nd="71">To see this in action, the data interfaces are described in <code nd="72">NHibernateSample.Core.DataInterfaces</code>. <code nd="73">IGenericDao</code> is a generic interface for providing typical data access functionality. <code nd="74">IDaoFactory</code> then acts as an interface for one or more DAO factory classes. Coding to the <code nd="75">IDaoFactory</code> interface allows you to create one concrete DAO factory for production code, and another concrete DAO factory for returning "mock" DAO objects for unit testing purposes. (This is described by the <a href="http://www.dofactory.com/Patterns/PatternAbstract.aspx" target="_blank">abstract factory pattern</a>.) Leveraging <a href="http://www.connextra.com/aboutUs/mockobjects.pdf" target="_blank">mock objects in unit tests</a> provides a means for testing a single responsibility at a time.</p><h4>Generics with NHibernate</h4><p nd="76">By far, one of the greatest benefits that C# 2.0 has brought to the table is the inclusion of generics. With generics, more code reuse can be effectively realized while still enforcing strongly typed coding "contracts". But while the benefits are great, NHibernate has not yet been upgraded to take advantage of this new language feature. (I know they're busy at work doing just that, though.) In the meantime, a solution can be found <a href="http://www.ayende.com/projects/nhibernate-query-analyzer/generics.aspx" target="_blank">here</a> called - quick, take a guess - <i>NHibernate.Generics</i>. <i>NHibernate.Generics</i> provides generic-typed wrappers for the <code nd="77">IList</code> and <code nd="78">ISet</code> collections that NHibernate currently binds to. I won't spend much time describing the approach, as <a href="http://www.ayende.com/projects/nhibernate-query-analyzer/generics.aspx" target="_blank">Oren Eini's website</a> will do a much more thorough job of this, but there is one particular note of interest in the code. Within the constructors of the <code nd="79">NHibernateSample.Core.Domain</code> objects, <code nd="80">Customer</code> and <code nd="81">Order</code> are "wire up" code for handling relational scaffolding. The collection wrappers accept two anonymous methods for handling add/remove events from either end of the relationship. So if you remove a child from a parent, the parent automatically gets removed from the child, and vice versa. (Look at <i>NHibernateSample.Tests.CustomerTests</i> to see it work.)</p><pre lang="cs" nd="83"><span class="cs-keyword" nd="82">public</span> Customer() {
    <span class="cs-comment" nd="84">// Implement parent/child relationship add/remove scaffolding</span>
    _orders = <span class="cs-keyword" nd="85">new</span> EntityList&lt;Order&gt;(
        <span class="cs-keyword" nd="86">delegate</span>(Order order) { order.ParentCustomer = <span class="cs-keyword" nd="87">this</span>; },
        <span class="cs-keyword" nd="88">delegate</span>(Order order) { order.ParentCustomer = <span class="cs-keyword" nd="89">null</span>; }
        );
}</pre><p nd="90">In the above example, note that private members encapsulating these relationships must be in the following format: <code nd="91">_camelCase</code>. There are a couple other caveats on the <i>NHiberante.Generics</i> website, but, overall, they're a small price to pay for having support for NHibernate generics. But be sure to keep an eye on <a href="http://www.hibernate.org/343.html" target="_blank">NHibernate's website</a> as an upgrade supporting generics is in the works.</p><h3>NHibernateSample.Data</h3><p nd="92">The <i>NHibernateSample.Data</i> project contains the implementation details for communicating with the database and managing NHibernate sessions.</p><h4>The DAO Factory and Generic DAO</h4><p nd="93">The DAO factory and generic DAO objects I've implemented as <code nd="94">NHibernateDaoFactory</code> and <code nd="95">GenericNHibernateDAO</code>, respectively, are C# ports of the Java versions <a href="http://www.hibernate.org/328.html" target="_blank">described in detail at NHibernate's website</a>. I highly recommend reviewing this article in detail. The most impressive thing to note is that it takes just a few lines of code to create a full-blown DAO object ready for use:</p><ol><li nd="96">Add a new inline implementation and retrieval method for the DAO, to <code nd="97">NHibernateSample.Data.NHibernateDaoFactory</code>. 
</li><li nd="98">Add a new inline interface and retrieval method to <code nd="99">NHibernateSample.Core.DataInterfaces.IDaoFactory</code>. </li></ol><p nd="100">Looking at the <code nd="101">ICustomerDao</code> example already in the sample application, that took about five lines of code...not too bad.</p><h4>Handling the NHibernate Session</h4><p nd="102">Finally, the only remaining question is how are NHibernate sessions managed? Details answering this question may be found in the class <code nd="103">NHibernateSessionManager</code>. Within this class, the basic flow for retrieving a session is as follows:</p><ol><li nd="104">The client code calls <code nd="105">NHibernateSessionManager.Instance.GetSession()</code>. 
</li><li nd="106">If not already instantiated, this singleton object builds the session factory, loading HBM mapping files from the <code nd="107">HBM_ASSEMBLY</code> declared in <i>web.config</i>. (<code nd="108">NHibernateSessionManager</code> is a singleton since building the session factory is very expensive.) 
</li><li nd="110"><code nd="109">GetSession</code> looks to see if a session is already bound to <code nd="111">System.Runtime.Remoting.Messaging.CallContext[<span class="cpp-string" nd="112">"THREAD_SESSION"</span>]</code>. 
</li><li nd="113">If an open NHibernate session is not found, then a new one is opened (and bound to an optional <code nd="114">IInterceptor</code>) and then set to <code nd="115">CallContext[<span class="cpp-string" nd="116">"THREAD_SESSION"</span>]</code>. 
</li><li nd="118"><code nd="117">GetSession</code> then returns the session assigned to <code nd="119">CallContext[<span class="cpp-string" nd="120">"THREAD_SESSION"</span>]</code>. </li></ol><p nd="121">This flow, as well as the rest of <code nd="122">NHibernateSessionManager</code>, follows that closely described in <a href="http://www.amazon.com/gp/product/193239415X/102-1170069-1054529?v=glance&amp;n=283155" target="_blank">Hibernate in Action</a>, chapter 8 - Writing Hibernate Applications.</p><p nd="123">The HTTPModule described in the <i>NHibernateSample.Web</i> project opens a transaction at the beginning of a web request, and commits/closes it at the end of the request. The following is an example of modifying the HTTPModule so that an <code nd="124">IInterceptor</code> gets bound to the session as well as being contained within a transaction:</p><pre lang="cs" nd="127"><span class="cs-keyword" nd="125">public</span><span class="cs-keyword" nd="126">void</span> Init(HttpApplication context) {
    context.BeginRequest += 
          <span class="cs-keyword" nd="128">new</span> EventHandler(InitNHibernateSession);
    ...
}

<span class="cs-keyword" nd="129">private</span><span class="cs-keyword" nd="130">void</span> InitNHibernateSession(<span class="cs-keyword" nd="131">object</span> sender, EventArgs e) {
    IInterceptor myNHibernateInterceptor = ...

    <span class="cs-comment" nd="132">// Bind the interceptor to the session.</span><span class="cs-comment" nd="133">// Using open-session-in-view, an interceptor </span><span class="cs-comment" nd="134">// cannot be bound to an already opened session,</span><span class="cs-comment" nd="135">// so this must be our very first step.</span>
    NHibernateSessionManager.Instance.RegisterInterceptor(myNHibernateInterceptor);

    <span class="cs-comment" nd="136">// Encapsulate the already opened session within a transaction</span>
    NHibernateSessionManager.Instance.BeginTransaction();
}</pre><h3>NHibernateSample.Tests</h3><p>I'll assume you can probably guess what this project is for.</p><h4>Unit Test Performance</h4><p>It's imperative for unit tests to be blazing fast. If a suite of unit tests takes too long to run, developers stop running them - and we want them running unit tests all the time! In fact, if a test takes more than 0.1 second to run, the test is probably too slow. Now, if you've done unit testing in the past, you know that any unit test requiring access to a live database takes much longer than this to run. With <a href="http://www.nunit.org/" target="_blank">NUnit</a>, you can put tests into categories, making it easy to run different groups of tests at a time, thus excluding the tests that connect to a database most of the time. Here's a quick example:</p><pre lang="cs">[TestFixture]
[Category(<span class="cpp-string">"NHibernate Tests"</span>)]
<span class="cs-keyword">public</span><span class="cs-keyword">class</span> SomeTests
{
    [Test]
    <span class="cs-keyword">public</span><span class="cs-keyword">void</span> TestSomethingThatDependsOnDb() { ... }
}</pre><h4>Testing with NHibernate</h4><p>In a previous version of this article, the <code>ISession</code> was stored and retrieved via <code>HttpContext.Current.Items.</code> The problem with this is that it forced you to simulate an HTTP context when running unit tests. It also prevented the framework from being easily ported to a Windows application. Instead, it is recommended that the <code>ISession</code> be stored and retrieved via <code>System.Runtime.Remoting.Messaging.CallContext.</code> (Thanks to Tadeu Camargo da Silva, on the NHibernate forums, and the feedback below for recommending this solution.) Using <code>CallContext</code> provides proper storage of the <code>ISession</code> for Web apps, Windows apps, and unit tests, alike. Take a look at <code>NHibernateSample.Tests.Data.CustomerDaoTests</code> for a unit test that is now "HTTP agnostic." Additionally, as you'll see in the unit test, unless you want data changes made within your tests to be committed to the database, it's a good idea to rollback the transaction.</p><h4>Testing with NHibernate "Mocked"</h4><p>Unless you're specifically testing DAO classes, you usually don't want to run unit tests that are dependent on a live database. They're slow and volatile by nature; i.e., if the data changes, the tests break. When testing business logic, unit tests shouldn't break if data changes. But the major obstacle is that business objects themselves may depend on DAOs. Using the abstract factory pattern that we've put into place, we can inject mock DAOs into the business objects, thereby simulating communications with the database. An example is included in <i>NHibernateSample.Tests.Domain.CustomerTests</i>. The following snippet creates the mock DAO and gives it to the <code>Customer</code> object via a public setter. Since the setter only expects an object implementing the <code>IOrderDao</code> interface, the mock object can easily replace all of the live-database behavior.</p><pre lang="cs">Customer customer = <span class="cs-keyword">new</span> Customer(<span class="cpp-string">"Acme Anvils"</span>);
customer.ID = <span class="cpp-string">"ACME"</span>;
customer.OrderDao = <span class="cs-keyword">new</span> MockOrderDao();</pre><p>Unfortunately, more often than not, you're maintaining legacy code that has no semblance of the ideal "code-to-interface" that allows for such flexibility. Usually, there are many explicit dependencies to concrete objects, and it is difficult to replace data-access objects with mock objects to simulate database communications. In these situations, your options are to either refactor the legacy code to fit within a test harness, or to use an object mocking tool such as <a href="http://www.typemock.com/" target="_blank">TypeMock</a>. With TypeMock, it is even possible to mock sealed and singleton classes - an impressive feat to pull off without such a tool. Albeit powerful, TypeMock is best left alone unless absolutely needed; using TypeMock prematurely makes it tempting to not always code to interface. The more appropriate course to take when working with legacy code - time and budget permitting - is to refactor the code for greater flexibility. <a href="http://www.amazon.com/gp/product/0131177052/102-1170069-1054529?v=glance&amp;n=283155" target="_blank">Working Effectively with Legacy Code</a> by Michael Feathers is full of great ideas for refactoring legacy code into a test harness.</p><h2>Putting it into Practice</h2><p>The sample application provides a strong data-layer foundation for building scalable web applications up to the enterprise level. (Almost all of the techniques also fit well for building Windows Forms applications.) But before using it within your own environment, I would recommend the following:</p><ul><li>Place <code>NHibernateSample.Core.DataInterfaces.IGenericDAO</code>, <code>NHibernateSample.Data.GenericNHibernateDAO</code>, and <code>NHibernateSample.Data.NHibernateSessionManager</code> into a reusable class library as they would be transferable between applications with no code modifications whatsoever. 
</li><li>Select an IoC provider to inject DAO dependencies into your page controllers. 
</li><li>Finally, I would ignore how I've setup my ASPX code-behind logic - it can quickly create monolithic code-behind pages; instead, I would consider using patterns such as <a href="http://www.martinfowler.com/eaaDev/ModelViewPresenter.html" target="_blank">Model-View-Presenter</a> (my personal favorite), <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpatterns/html/DesPageController.asp" target="_blank">Page Controller</a>, <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpatterns/html/DesFrontController.asp" target="_blank">Front Controller</a>, or other MVC-like approaches. But again, that's for a completely different discussion altogether! </li></ul><p>I hope this article has helped with putting best practices into place for leveraging the benefits of ASP.NET, NHibernate, and Generics. I'm currently having great success with this approach on my projects, and look forward to hearing your experiences as well. Please let me know if you have any questions or suggestions.<br /><br /></p></div><img src ="http://www.cnitblog.com/seeyeah/aggbug/24310.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-03-20 01:04 <a href="http://www.cnitblog.com/seeyeah/archive/2007/03/20/24310.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ASP.NET 2.0 本地化技术之研究（二）</title><link>http://www.cnitblog.com/seeyeah/archive/2007/03/17/24186.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Sat, 17 Mar 2007 10:32:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/03/17/24186.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/24186.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/03/17/24186.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/24186.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/24186.html</trackback:ping><description><![CDATA[Copy From  <a class="headermaintitle" id="Header1_HeaderTitle" href="http://www.cnblogs.com/reonlyrun/"><strong><font color="#000000" size="4">Clark's Life</font></strong></a><br /><hr /><br />在<a class="postTitle2" id="_1f91e4e64c42_HomePageDays_DaysList_ctl00_DayItem_DayList_ctl00_TitleUrl" href="http://www.cnblogs.com/reonlyrun/archive/2007/03/14/673649.html"><font color="#1a8bc8">ASP.NET 2.0 本地化技术之研究</font></a>的回复中提到了以下两点：<br /><br />1.这只是单个页面的切换，如何做整个站点的切换呢？（ <a id="AjaxHolder_Comments_CommentList_ctl15_NameLink" target="_blank">hjh</a> ）<br />2.关于如何将资源直接显示……既然控件能够将嵌入dll的资源直接显示，不知道网站能否也将嵌入资源直接调用WebResource显示呢？（<a id="AjaxHolder_Comments_CommentList_ctl11_NameLink" href="http://cathsfz.cnblogs.com/" target="_blank"><font color="#1a8bc8">Cat Chen</font></a> ）<br /><br />由于不是一两句可以说清，所以再开一篇仔细讲一下。<br /><br /><br />内容列表：<br /><br />1.整站本地化资源的切换<br />2.使用ProFile保存用户选择的语言<br />3.关于WebResource的使用<br />4.代码下载<br /><br /><span style="FONT-WEIGHT: bold">1.整站本地化资源的切换</span><br /><br />在上一篇里我们讲到，可以通过重载页面的InitializeCulture函数，在其中切换当前线程的CurrentUICulture和CurrentCulture来实现本页的资源切换。那么整站呢？总不能在每个页面里都写上这几句吧。。。<br /><br />首先，我想到的是使用MasterPage，如果在MasterPage里加上资源切换的代码，那么所有使用该母板的页面都具备这种能力了吧，呵呵（想得不错）。但如意算盘打破了，MasterPage是使用@Master来声明的，根本和Page是两个继承路线，所以MasterPage里没有InitializeCulture这个虚函数！<br /><br />没办法，想到了另一个解决方案，创建一个从System.Web.UI.Page继承下来的基类，在其中实现资源切换，而站内所有页面的实现类都从该类继承。OK，就这么办！<br /><br />打开上一篇完成的网站，选中网站，右键在弹出菜单中点击[添加ASP.NET文件夹]-[App_Code]。<br />选中该文件夹，右键点击[添加新项]，在弹出式窗口中选择“类”，命名为LocalizedPage.cs，点击[添加]完成，如图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/LocalizedPage.jpg" /><br /><br />编辑LocalizedPage.cs，代码如下：<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Data;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Configuration;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.Security;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.UI;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.UI.WebControls;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.UI.WebControls.WebParts;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.UI.HtmlControls;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Threading;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Globalization;<br /><br /></span><span style="COLOR: #808080">///</span><span style="COLOR: #008000"> </span><span style="COLOR: #808080">&lt;summary&gt;</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #808080">///</span><span style="COLOR: #008000"> 所有需要本地化资源切换的页面基类<br /></span><span style="COLOR: #808080">///</span><span style="COLOR: #008000"> </span><span style="COLOR: #808080">&lt;/summary&gt;</span><span style="COLOR: #808080"><br /></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> LocalizedPage : System.Web.UI.Page <br />{<br />    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">override</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> InitializeCulture()<br />    {<br />        String s </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> Request.QueryString[</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">currentculture</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">];<br />        </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> (</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">String.IsNullOrEmpty(s))<br />        {<br />            </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">UICulture - 决定了采用哪一种本地化资源，也就是使用哪种语言<br />            </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">Culture - 决定各种数据类型是如何组织，如数字与日期</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">            Thread.CurrentThread.CurrentUICulture </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> CultureInfo(s);<br />            Thread.CurrentThread.CurrentCulture </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> CultureInfo.CreateSpecificCulture(s);<br />        }<br />    }<br />}<br /></span></div><br />编辑Image.aspx.cs，去除其重载的InitialzeCulture()函数，将其基类改为LocalizedPage，代码如下：<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Data;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Configuration;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Collections;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.Security;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.UI;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.UI.WebControls;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.UI.WebControls.WebParts;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.UI.HtmlControls;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Threading;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Globalization;<br /><br /></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> partial </span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> Image : LocalizedPage<br />{<br />    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> Page_Load(</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000"> sender, EventArgs e)<br />    {<br />        System.Drawing.Bitmap img </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> (System.Drawing.Bitmap)GetGlobalResourceObject(<br />            </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">LocalizedText</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,<br />            CultureInfo.CurrentCulture.Name.ToLower().Replace(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">_</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">) </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">_flag</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><br />        System.IO.MemoryStream ms </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> System.IO.MemoryStream();<br />        img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);<br /><br />        Response.ClearContent();<br />        Response.ContentType </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">image/jpeg</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">;<br />        Response.BinaryWrite(ms.ToArray());<br /><br />        img.Dispose();<br />        ms.Dispose();<br />        ms.Flush();<br />    }<br />}<br /></span></div><br />运行网站，可以看到由Image页面负责输出的图片，可以按选中的语言正常切换。<br /><br />再编辑Default.aspx.cs，调整代码如下：<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Data;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Configuration;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.Security;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.UI;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.UI.WebControls;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.UI.WebControls.WebParts;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.UI.HtmlControls;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Globalization;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Threading;<br /><br /><br /></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> partial </span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> _Default : LocalizedPage <br />{<br />    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> Page_Load(</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000"> sender, EventArgs e)<br />    {<br />        String s </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> Request.QueryString[</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">currentculture</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">];<br />        Image1.ImageUrl </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">~/Image.aspx?currentculture=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> s;<br />    }<br /><br />    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> Button1_Click(</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000"> sender, EventArgs e)<br />    {<br />        Localize1.Text </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> (String)GetLocalResourceObject(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Label1Resource1.Text</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">) </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"><br />            (String)GetGlobalResourceObject(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">LocalizedText</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Msg1</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br />    }<br />}<br /></span></div><br />运行程序，一切正常。<img src="http://www.cnblogs.com/CuteSoft_Client/CuteEditor/images/emsmile.gif" align="absMiddle" border="0" /><br /><br />总结：利用这种方式，新建页面时只需修改其继承的基类为LocalizedPage即可。对于已经建好的站点，同理，也可以很方便的加入资源切换的支持。<br /><br /><span style="FONT-WEIGHT: bold">2.使用ProFile保存用户选择的语言 <br /><br /></span>前面我们是通过URL传参的方式将用户选择的语言传递到各个页面，感觉不爽。那么使用Session呢？听上去不错，但是你没听过ProFile吗？这可是ASP.NET 2.0的新特性之一呀！与Session一样ProFile是针对一个特定用户的，但ProFile更好用，因为它有以下特点：<br />  1）可存储，默认是保存在SQL Server Express中，但通过实现Provider可以将它存储到任何地方<br />  2）支持匿名使用，在用户认证后还可以迁移到认证用户中（具体实现方法据说是非常的“巧妙”）<br />  3）支持生成和管理报告<br />不错吧，那么我们就用ProFile来保存用户选择的语言信息吧。<br /><br /><span style="COLOR: #ff0000">注意：由于ProFile默认是由SQL Server Express来存储的，所以要保证你的VS2005已经安装该模块。</span><br /><br />编辑Web.Config，在system.web节点下增加以下配置<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">configuration</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />    </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">system</span><span style="COLOR: #ff0000">.web</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />        </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">anonymousIdentification </span><span style="COLOR: #ff0000">enabled</span><span style="COLOR: #0000ff">="true"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br />        </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">profile</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />            </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">properties</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />                </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">add </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="LanguagePreference"</span><span style="COLOR: #ff0000"> type</span><span style="COLOR: #0000ff">="string"</span><span style="COLOR: #ff0000"> <br />                    defaultValue</span><span style="COLOR: #0000ff">="Auto"</span><span style="COLOR: #ff0000"> allowAnonymous</span><span style="COLOR: #0000ff">="true"</span><span style="COLOR: #ff0000"> </span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br />            </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">properties</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />        </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">profile</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />    </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">system.web</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /></span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">configuration</span><span style="COLOR: #0000ff">&gt;</span></div><br />同时加入的anonymousIdentification节，是为了让系统自动为匿名用户生成唯一标识。另外的allowAnonymous="true"表明LanguagePreference属性可以被匿名用户访问。<br /><br />编辑Default.aspx，切换到[设计]视图，删除原来用于切换语言的两个链接“中文(中国)”和“English(USA)”。从工具箱中拖一个DropDownList控件到页面上，设置其AutoPostBack属性为True（<span style="COLOR: #ff0000">切记！</span>），然后编辑它的Items属性，如图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Items.jpg" /><br /><br />中文（中国）的Value为zh-cn，英文（美国）的Value为en-us。<br /><br />编辑Default.aspx.cs，为DropDownList编写SelectedIndexChanged事件的实现，代码如下：<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #000000">    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> DropDownList1_SelectedIndexChanged(</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000"> sender, EventArgs e)<br />    {<br />        Profile.LanguagePreference </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> DropDownList1.SelectedValue;<br />        Response.Redirect(Request.Url.AbsolutePath);<br />    }</span></div><br />修改Page_Load的实现，代码如下：<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #000000">    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> Page_Load(</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000"> sender, EventArgs e)<br />    {<br />        String s </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> Profile.LanguagePreference;<br />        Image1.ImageUrl </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">~/Image.aspx</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">;<br />    }</span></div><br />然后再编辑LocalizedPage.cs，代码如下：<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #000000">    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">override</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> InitializeCulture()<br />    {<br />        String s </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> (String)Context.Profile.GetPropertyValue(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">LanguagePreference</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br />        </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> (</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">String.IsNullOrEmpty(s) </span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000"> (s </span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Auto</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)) <br />        {<br />            </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">UICulture - 决定了采用哪一种本地化资源，也就是使用哪种语言<br />            </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">Culture - 决定各种数据类型是如何组织，如数字与日期</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">            Thread.CurrentThread.CurrentUICulture </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> CultureInfo(s);<br />            Thread.CurrentThread.CurrentCulture </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> CultureInfo.CreateSpecificCulture(s);<br />        }<br />    }</span></div><br /><span style="COLOR: #ff0000">注意：我们在Default.aspx.cs之所以可以直接使用Profile来访问用户个人信息，是因为ASP.NET在页面运行时自动为我们生成了一个继承自System.Web.Profile.ProfileBase的ProfileCommon类。而在App_Code目录的代码开始执行时，ProfileCommon还没有生成，更别提Profile了。所幸的是，我们可以通过上面代码的方式访问到用户个人信息（真的研究了好长时间。。。</span><img style="COLOR: #ff0000" src="http://www.cnblogs.com/CuteSoft_Client/CuteEditor/images/emcry.gif" align="absMiddle" border="0" /><span style="COLOR: #ff0000">）</span><br /><span style="FONT-WEIGHT: bold"><br /></span>运行程序，切换语言，运行效果如图所示：<br />中文（中国）<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Example17.jpg" /><br /><br />英文（美国）<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Example18.jpg" /><br /><br />注意哟，退出程序后，再次运行，所有页面将按你上次设置的语言显示，Profile真的很不错。<br /><span style="FONT-WEIGHT: bold"><br />3.关于WebResource的使用</span><br /><br />ASP.NET是在运行时将全局资源和本地资源进行编译，象.aspx文件一样，所以我们只需要将.resx文件xcopy到正在运行的WEB服务器上，即可为新语言提供本地化的支持。但如果我们开发了一个WEB控件，其中使用到了一些资源（如图片），那就要求我们必须将DLL和资源文件一起部署到WEB服务器上，比较麻烦。<br /><br />ASP.NET开发团队考虑到了这一点，现在我们可以在网站里使用资源DLL，这样在发布DLL时资源也同时被分配了。该技术是通过在控件代码里调用GetWebResourceUrl方法，这个方法指向一个名为WebResource.axd的内置HTTP处理程序的URL。通过加载一个名为AssemblyResourceLoader的HttpHandler类，ASP.NET运行时响应WebResource.axd的请求，返回指定资源的URL。<br /><br />该技术有以下缺点：<br />  1）只能在面向 ASP.NET 2.0 网站的 DLL 项目内使用该技术，而无法网站内直接使用该技术<br />  2）该技术实际上并不支持任何形式的本地化（说到这，感觉把这家伙写到本随笔里不太合适。。。管它呢，先写完再说！<img src="http://www.cnblogs.com/CuteSoft_Client/CuteEditor/images/emsmilep.gif" align="absMiddle" border="0" />）<br /><br />选中网解决方案，右键在弹出式菜单里点击[添加]-&gt;[新建项目]，在弹出窗口选中Visual C#项目下的类库，并设好保存路径，如图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/ClassLibary.jpg" /><br /><br />点击确定，删除Class1.cs。选中ClassLibrary1项目，右键在弹出菜单里点击[添加]-&gt;[新建项]，在弹出窗口选择“WEB 自定义控件”，如图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/WebCustomControl.jpg" /><br /><br />点击[添加]，现在解决方案里已经包含两个项目了，如图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/ThirdSolution.jpg" /><br /><br />右键ClassLibrary1项目，选择[添加]-&gt;[现有项]，随便找一张图片（我使的是园子的logo，嘿嘿），如图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/AddLogo.jpg" /><br /><br />点击[添加]，右键刚添加的图片点击[属性]，将“生成操作”设为“嵌入的资源”，如图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/BindResProperty.jpg" /><br /><br />编辑WebCustomControl1.cs，代码如下：<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Collections.Generic;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.ComponentModel;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Text;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.UI;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Web.UI.WebControls;<br /><br />[assembly: WebResource(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">ClassLibrary1.logo.gif</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">image/gif</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)]<br /><br /></span><span style="COLOR: #0000ff">namespace</span><span style="COLOR: #000000"> ClassLibrary1<br />{<br />    [ToolboxData(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&lt;{0}:WebCustomControl1 runat=server&gt;&lt;/{0}:WebCustomControl1&gt;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)]<br />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> WebCustomControl1 : WebControl<br />    {<br />        </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">override</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> RenderContents(HtmlTextWriter output)<br />        {<br />            output.WriteBeginTag(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">image</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br />            String url </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> Page.ClientScript.GetWebResourceUrl(GetType(), </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">ClassLibrary1.logo.gif</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br />            output.WriteAttribute(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">src</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, url);<br />            output.WriteEndTag(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">image</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br />        }<br />    }<br />}<br /></span></div><br />编辑网站的Default.aspx文件，切换到[设计]视图，将工具箱的ClassLibrary1面板里的WebCustomControl1控件拖到页面上，运行程序，效果如图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Example19.jpg" /><br /><br /><br /><span style="FONT-WEIGHT: bold">4.代码下载<br /><br /></span>下载地址：<a href="http://www.cnblogs.com/Files/reonlyrun/WebLocalizationTaste2.rar"><font color="#1a8bc8">http://www.cnblogs.com/Files/reonlyrun/WebLocalizationTaste2.rar</font></a><img src ="http://www.cnitblog.com/seeyeah/aggbug/24186.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-03-17 18:32 <a href="http://www.cnitblog.com/seeyeah/archive/2007/03/17/24186.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>vs2005 team suite简体中文版本，对应的sp1下载地址</title><link>http://www.cnitblog.com/seeyeah/archive/2007/03/17/24183.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Sat, 17 Mar 2007 10:07:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/03/17/24183.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/24183.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/03/17/24183.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/24183.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/24183.html</trackback:ping><description><![CDATA[首先是下载：我用的版本是vs2005 team suite简体中文版本，对应的sp1下载地址：<a href="http://www.microsoft.com/downloads/details.aspx?displaylang=zh-cn&amp;FamilyID=BB4A75AB-E2D4-4C96-B39D-37BAF6B5B1DC" target="_blank"><font color="#000080">点这里</font></a><img src ="http://www.cnitblog.com/seeyeah/aggbug/24183.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-03-17 18:07 <a href="http://www.cnitblog.com/seeyeah/archive/2007/03/17/24183.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ASP.NET 2.0 本地化技术之研究</title><link>http://www.cnitblog.com/seeyeah/archive/2007/03/14/24053.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Wed, 14 Mar 2007 15:54:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/03/14/24053.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/24053.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/03/14/24053.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/24053.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/24053.html</trackback:ping><description><![CDATA[Copy From <a class="headermaintitle" id="Header1_HeaderTitle" href="http://www.cnblogs.com/reonlyrun/"><strong><font color="#000000" size="4">Clark's Life</font></strong></a><br /> 
<hr /><br />众所周知ASP.NET 2.0里对本地化（Localization）做了很多工作，大大简化了开发过程。今天终于能抽出时间研究一下这个技术了，资料很多，但大多带着一股咬文嚼字的翻译味道，So......自己写一篇。<br /><br />1.使用工具自动生成本地化资源（LocalResources）<br />2.原来Localizable AttributeProperty是这么回事呀<br />3.手工添加本地化资源<br />4.显示使用本地化资源<br />5.全局资源的使用（GlobalResources）<br />6.如何在后台编程时使用这两种资源<br />7.编程切换语言设置<br />8.使用图片资源<br />9.代码下载<br /><br /><br /><span style="FONT-WEIGHT: bold">1.使用工具自动生成本地化资源（LocalResources）<br /><br /></span>首先当然是建立一个WEB工程啦，如图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/CreateWeb.jpg" /><br /><br />双击Default.aspx，切换到[设计]视图，从工具箱里拖一个Button进来。点击菜单中的[工具]-&gt;[生成本地资源]，如图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/CreateLocalResources.jpg" /><br /><br />这时发现在解决方案方案视图里自动增加了一份本地资源，如下图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/FirstSolution.jpg" /><br /><br />切换到属性视图，可以看到Text和ToolTip都被加上了一个红色符号，提示“属性是用表达形式隐式绑定的”，如下图所示：（<span style="COLOR: red">注意：只有在视图状态下选中控件才能看到这两个属性哟</span>）<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/RedPropertyView.jpg" /><br /><br />为了增加对多语言的支持，我们现在右键Default.aspx.resx，点击复制，再选中App_LocalResources目录，点击粘贴，将“副本 Default.aspx.resx”重命名为Default.aspx.en-us.resx，如图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/SecondSolution.jpg" /><br /><br />分别编辑Default.aspx.resx和Default.aspx.en-s.resx 的内容，如下图示：<br />Default.aspx.resx<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/FirstDefaultResx.jpg" /><br /><br />Default.aspx.en-s.resx<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/FirstDefaultEnResx.jpg" /><br /><br />运行网站，效果如下图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Example01.jpg" /><br /><br />点击IE菜单的[工具]-&gt;[Internet 选项]，在弹出的对话框中点[常规]选项卡的[语言]按钮，弹出[语言首选项]对话框，如下图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Language01.jpg" /><br /><br />点击[添加]按钮，选择英语(美国)，并上移到第一位，如下图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Language02.jpg" /><br /><br />一路[确定]点下来，刷新页面，可以看到Button的标题变成“Hello World”啦，如下图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Example02.jpg" /><br /><br />综上所述，使用工具自动生成本地化资源，并隐式的绑定到控件上，来实现页面的本地化是非常简单的，但存在以下几项问题：<br /><br />    1）必须为每个页生成多份资源文件；<br />    2）只有控件属性中被标识为Localizable(true)才可能被工具扫描到；<br />    3）由于是对页面包含的控件进行扫描，所以最好是在页面布局基本确定后再使用该工具，并且在编辑完    资源文件后不要再使用该工具，如果不幸你这样做了。。。会发现默认的资源文件被清空了<br /><br /><span style="FONT-WEIGHT: bold">2.原来Localizable AttributeProperty是这么回事呀<br /><br /></span>在我前天写的随笔<a class="postTitle2" id="viewpost1_TitleUrl" href="http://www.cnblogs.com/reonlyrun/archive/2007/03/12/672295.html"><font color="#1a8bc8">开发和使用自定义服务器控件</font></a>中，一直对属性为什么要使用Localizable(true)不清楚。<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #000000">        [<br />        Bindable(</span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000">),<br />        Category(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Appearance</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">),<br />        DefaultValue(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Hello</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">),<br />        Description(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">The welcome message text.</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">),<br />        Localizable(</span><span style="COLOR: #0000ff">true</span><span style="COLOR: #000000">)<br />        ]<br />        </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000"> Text<br />        {<br />            </span><span style="COLOR: #0000ff">get</span><span style="COLOR: #000000"><br />            {<br />                </span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000"> s </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> (</span><span style="COLOR: #0000ff">string</span><span style="COLOR: #000000">)ViewState[</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Text</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">];<br />                </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> (s </span><span style="COLOR: #000000">==</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">null</span><span style="COLOR: #000000">) </span><span style="COLOR: #000000">?</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Hello</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> : s;<br />            }<br />            </span><span style="COLOR: #0000ff">set</span><span style="COLOR: #000000"><br />            {<br />                ViewState[</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Text</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">] </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> value;<br />            }<br />        }<br /></span></div><br />查了许多关于WEB控件开发的资料，其中都对这个AttributeProperty做了忽略，原来只有当控件的属性声明为Localizable(true)时，VS的生成本地资源工具才会扫描到。 <img src="http://www.cnblogs.com/CuteSoft_Client/CuteEditor/images/emteeth.gif" align="absMiddle" border="0" /><span style="FONT-WEIGHT: bold"><br /></span><br /><span style="FONT-WEIGHT: bold">3.手工添加本地化资源<br /><br /></span>第一点中提到过生成本地资源工具只能扫描到页面已经包含的控件，那么如果后面由于开发需要又增加了一个新的控件要怎么办呢？虽然大部人都能自己想到，但还是写一下吧。<br /><br />从工具箱再拖一个Label控件到页面上，切换到源视图，修改代码如下：<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">asp:Label </span><span style="COLOR: #ff0000">ID</span><span style="COLOR: #0000ff">="Label1"</span><span style="COLOR: #ff0000"> runat</span><span style="COLOR: #0000ff">="server"</span><span style="COLOR: #ff0000"> meta:resourcekey</span><span style="COLOR: #0000ff">="Label1Resource1"</span><span style="COLOR: #ff0000"> Text</span><span style="COLOR: #0000ff">="Label"</span><span style="COLOR: #0000ff">&gt;&lt;/</span><span style="COLOR: #800000">asp:Label</span><span style="COLOR: #0000ff">&gt;</span></div><br />再修改两份本地资源文件，为Label1添加Label1Resource1键值的相关资源，如下图所示：<br />Default.aspx.resx<br /><img height="172" alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Language03.jpg" width="692" /><br /><br />Default.aspx.en-s.resx<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Language04.jpg" /><br /><br />再运行程序，切换语言设置，可以看到Label1的效果和Button1是一样的，如图所示：<br />中文(中国)<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Example03.jpg" /><br /><br />英语（美国）<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Example04.jpg" /><br /><span style="FONT-WEIGHT: bold"></span><br /><span style="FONT-WEIGHT: bold">4.显示使用本地化资源<br /><br /></span>OK，前面提到的都是隐式使用本地化资源，那么如何显示的使用本地化资源呢？相对于隐式来讲，显示更加灵活，功能也更强大。<br /><br />只需要将刚才的Label1的代码，<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">asp:Label </span><span style="COLOR: #ff0000">ID</span><span style="COLOR: #0000ff">="Label1"</span><span style="COLOR: #ff0000"> runat</span><span style="COLOR: #0000ff">="server"</span><span style="COLOR: #ff0000"> meta:resourcekey</span><span style="COLOR: #0000ff">="Label1Resource1"</span><span style="COLOR: #ff0000"> Text</span><span style="COLOR: #0000ff">="Label"</span><span style="COLOR: #0000ff">&gt;&lt;/</span><span style="COLOR: #800000">asp:Label</span><span style="COLOR: #0000ff">&gt;</span></div><br />修改为，即可。<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">asp:Label </span><span style="COLOR: #ff0000">ID</span><span style="COLOR: #0000ff">="Label1"</span><span style="COLOR: #ff0000"> runat</span><span style="COLOR: #0000ff">="server"</span><span style="COLOR: #ff0000"> <br />    Text</span><span style="COLOR: #0000ff">="&lt;%$ Resources:Label1Resource1.Text %&gt;"</span><span style="COLOR: #ff0000"> <br />    ToolTip</span><span style="COLOR: #0000ff">="&lt;%$ Resources:Label1Resource1.ToolTip %&gt;"</span><span style="COLOR: #ff0000"> </span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /></span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">asp:Label</span><span style="COLOR: #0000ff">&gt;</span></div><span style="FONT-WEIGHT: bold"></span><br />这时切换到[设计]视图，选中Label1控件，查看属性视图，会发现Text和ToolTip被加上的是蓝色的符号，提示“属性绑定了表达式”。跟上面提到的红色符号比较，少了“隐式”二字，这也就是所谓的显式和隐定的来源吧，如下图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/BluePropertyView.jpg" /><br /><br /><span style="FONT-WEIGHT: bold">5.全局资源的使用（GlobalResources）<br /><br /></span>前面提到过，本地资源需要为每个页面分别生成多个资源文件，虽然这样看起来分门别类的挺清楚，但在实际应用过程中，我们有许多资源是可以共享的，总不能不停的重复写来写去吧。其实这种情况我们可以用全局资源（GlobalResources）来解决。<br /><br />在资源方案视图中选中网站，右键，点击[添加ASP.NET 文件夹]-&gt;[App_GlobalResources]，如图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/AddGlobalResources.jpg" /><br /><br />再选中App_GlobalResources文件夹，右键，点击[添加新项]，在弹出的对话框中选中“资源文件”，命名为“LocalizedText.resx”，点击[添加]，如图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/AddResourcesFile.jpg" /><br /><br />双击LocalizedText.resx进行编辑，添加一条新的字符串资源，如图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Language05.jpg" /><br /><br />复制LocalizedText.resx，粘贴到App_GlobalResources目录，重命名为LocalizedText.en-us.resx，双击进行编辑，添加一条新的字符串资源，如图所示：<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Language06.jpg" /><br /><br />打开Default.aspx，切换到[设计]视图，从工具箱拖一个TextBox控件到页面上。切换到[源]视图，修改代码：<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">asp:TextBox </span><span style="COLOR: #ff0000">ID</span><span style="COLOR: #0000ff">="TextBox1"</span><span style="COLOR: #ff0000"> runat</span><span style="COLOR: #0000ff">="server"</span><span style="COLOR: #ff0000"> Text</span><span style="COLOR: #0000ff">="&lt;%$ Resources:LocalizedText, Msg1 %&gt;"</span><span style="COLOR: #0000ff">&gt;&lt;/</span><span style="COLOR: #800000">asp:TextBox</span><span style="COLOR: #0000ff">&gt;</span></div><br />运行程序，切换语言设置，可以看到全局资源的使用效果了，如图所示：<br />中文（中国）<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Example05.jpg" /><br /><br />英语（美国）<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Example06.jpg" /><br /><br /><span style="COLOR: red">注意：全局资源不能使用隐式声明。</span><br /><br /><span style="FONT-WEIGHT: bold"></span><span style="FONT-WEIGHT: bold">6.如何在后台编程时使用这两种资源</span><br /><br />前面提到的都是在网页中使用这两种资源，那么如何在后台使用这两种资源呢？<br /><br />打开Default.aspx，切换到[设计]视图，从工具箱拖一个Localize控件（Literal 控件与 Label 控件类似，但 Literal 控件不允许对所显示的文本应用样式。可以通过设置 Text 属性，以编程方式控制在控件中显示的文本。）到页面上。双击Button1，添加以下实现<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #000000">    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> Button1_Click(</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000"> sender, EventArgs e)<br />    {<br />        Localize1.Text </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> (String)GetLocalResourceObject(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Label1Resource1.Text</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">) </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"><br />            (String)GetGlobalResourceObject(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">LocalizedText</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Msg1</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br />    }</span></div><br />记得添上这个引用<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Globalization;</span></div><br />运行程序，切换语言设置，可以看到和前面使用表达式调用资源的效果是一样的，如图所示：<br />中文（中国）<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Example07.jpg" /><br /><br />英语（美国）<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Example08.jpg" /><br /><br /><span style="FONT-WEIGHT: bold">7.动态切换语言设置</span><br /><br />以上的介绍都是通过IE浏览器获取语言设置，其实我们可以自己设置使用哪种语言。<br /><br />1)通过在每个页面里的Page节指定<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #000000; BACKGROUND-COLOR: #ffff00">&lt;%</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">@ Page Culture</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">=</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">en-us</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> UICulture</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">=</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">en-us</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> </span><span style="COLOR: #000000; BACKGROUND-COLOR: #ffff00">%&gt;</span></div><br />如上所设，该页将使用en-us的语言设置。<br /><br /><span style="COLOR: #fe0000">注意：这只是个概要式写法，实际的页面中的Page一般都包含更多的属性。</span><br /><br />2）通过在Web.Config里的globalization节指定<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">system</span><span style="COLOR: #ff0000">.web</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />    </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">globalization </span><span style="COLOR: #ff0000">Culture</span><span style="COLOR: #0000ff">="en-us"</span><span style="COLOR: #ff0000"> UICulture</span><span style="COLOR: #0000ff">="en-us"</span><span style="COLOR: #ff0000"> </span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br /></span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">system.web</span><span style="COLOR: #0000ff">&gt;</span></div><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><br />3）当然还有一种就是通过编程动态切换语言设置啦，这也是实际项目中经常用到的方式<br /><br />打开Default.aspx，切换到[源]视图，添加如下代码<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #000000">        </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">a </span><span style="COLOR: #ff0000">href</span><span style="COLOR: #0000ff">="?currentculture=zh-cn"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">中文(中国)</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">a</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br />        </span><span style="COLOR: #ff0000">&amp;nbsp;</span><span style="COLOR: #000000"><br />        </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">a </span><span style="COLOR: #ff0000">href</span><span style="COLOR: #0000ff">="?currentculture=en-us"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">English(USA)</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">a</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"> </span></div><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><br />打开Default.aspx.cs，添加如下代码<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #000000">    String s;<br /><br />    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">override</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> InitializeCulture()<br />    {<br />        s </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> Request.QueryString[</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">currentculture</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">];<br />        </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> (</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">String.IsNullOrEmpty(s))<br />        {<br />            </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">UICulture - 决定了采用哪一种本地化资源，也就是使用哪种语言<br />            </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">Culture - 决定各种数据类型是如何组织，如数字与日期</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">            Thread.CurrentThread.CurrentUICulture </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> CultureInfo(s);<br />            Thread.CurrentThread.CurrentCulture </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> CultureInfo.CreateSpecificCulture(s);<br />        }<br />    }</span></div><br /><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->记得添上这个引用<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Threading;</span></div><br />运行程序，分别点击新增加的两个链接，效果如图所示：<br />中文（中国）<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Example13.jpg" /><br /><br />Enlish(USA)<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Example14.jpg" /><br /><br /><br /><span style="FONT-WEIGHT: bold">8.使用图片资源</span><br /><br />分别编辑LocalizedText.resx和LocalizedText.en-su.resx，添加图片资源，如图所示：<br />LocalizedText.resx <br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Language07.jpg" /><br /><br />LocalizedText.en-su.resx <br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Language08.jpg" /><br /><br /><span style="COLOR: red">注意：当图形文件添加到图形资源里时，系统会自动去除扩展名并把“-”替换成“_”，如上图，我的文件名为“en-us-flag.png”添加到资源里变成了“en_us_flag”。</span><br /><br />打开Default.aspx，切换到[设计]视图，从工具箱拖一个Image控件到页面上。打开Default.aspx.cs，添加如下代码<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #000000">    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> Page_Load(</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000"> sender, EventArgs e)<br />    {<br />        Image1.ImageUrl </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">~/Image.aspx?currentculture=</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> s;<br />    }<br /></span></div><br />选中网站右键，创建一个新的WEB窗体，命名为Image.aspx，编辑该页面，在Page节增加如下代码<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #000000; BACKGROUND-COLOR: #ffff00">&lt;%</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">@ Page Language</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">=</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">C#</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> AutoEventWireup</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">=</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">true</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> CodeFile</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">=</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">Image.aspx.cs</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> Inherits</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">=</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">Image</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> Culture</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">=</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">auto</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5"> UICulture</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">=</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">auto</span><span style="COLOR: #000000; BACKGROUND-COLOR: #f5f5f5">"</span><span style="COLOR: #000000; BACKGROUND-COLOR: #ffff00">%&gt;</span></div><br /><span style="COLOR: red">注意：默认创建的WEB窗体的Page节，不包括Culture和UICulture这两个属性，一定要手工添上，切记！<br /><br /><span style="COLOR: #000000">打开Image.aspx.cs，添加如下代码<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #000000">    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">override</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> InitializeCulture()<br />    {<br />        String s </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> Request.QueryString[</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">currentculture</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">];<br />        </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> (</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">String.IsNullOrEmpty(s))<br />        {<br />            </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">UICulture - 决定了采用哪一种本地化资源，也就是使用哪种语言<br />            </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">Culture - 决定各种数据类型是如何组织，如数字与日期</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">            Thread.CurrentThread.CurrentUICulture </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> CultureInfo(s);<br />            Thread.CurrentThread.CurrentCulture </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> CultureInfo.CreateSpecificCulture(s);<br />        }<br />    }<br /><br />    </span><span style="COLOR: #0000ff">protected</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> Page_Load(</span><span style="COLOR: #0000ff">object</span><span style="COLOR: #000000"> sender, EventArgs e)<br />    {<br />        System.Drawing.Bitmap img </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> (System.Drawing.Bitmap)GetGlobalResourceObject(<br />            </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">LocalizedText</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,<br />            CultureInfo.CurrentCulture.Name.ToLower().Replace(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">_</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">) </span><span style="COLOR: #000000">+</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">_flag</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><br />        System.IO.MemoryStream ms </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> System.IO.MemoryStream();<br />        img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);<br /><br />        Response.ClearContent();<br />        Response.ContentType </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">image/jpeg</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">;<br />        Response.BinaryWrite(ms.ToArray());<br /><br />        img.Dispose();<br />        ms.Dispose();<br />        ms.Flush();<br />    }</span></div><br />当然，别忘了添上这两个引用 
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Threading;<br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> System.Globalization;</span></div><br /></span></span><font color="#4b4b4b">运行程序，分别点击两个链接，效果如图所示： <br />中文（中国）<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Example15.jpg" /><br /><br />English（USA）<br /><img alt="" src="http://www.cnblogs.com/images/cnblogs_com/reonlyrun/blog/Example16.jpg" /><br /><br /><span style="FONT-WEIGHT: bold"></span></font><span style="COLOR: red"><span style="COLOR: #000000"><span style="FONT-WEIGHT: bold">9.代码下载<br /><br /></span>下载地址：<a href="http://www.cnblogs.com/Files/reonlyrun/WebLocalizationTaste.rar"><font color="#1a8bc8">http://www.cnblogs.com/Files/reonlyrun/WebLocalizationTaste.rar</font></a></span></span><img src ="http://www.cnitblog.com/seeyeah/aggbug/24053.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-03-14 23:54 <a href="http://www.cnitblog.com/seeyeah/archive/2007/03/14/24053.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Treeview改动htc路径</title><link>http://www.cnitblog.com/seeyeah/archive/2007/01/24/22282.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Wed, 24 Jan 2007 12:55:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/01/24/22282.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/22282.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/01/24/22282.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/22282.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/22282.html</trackback:ping><description><![CDATA[
		<p>在web.config里 添加</p>
		<p>&lt;configSections&gt;</p>
		<p>&lt;section name="MicrosoftWebControls" type="System.Configuration.NameValueSectionHandler, System, System.Configuration.NameValueSectionHandler, System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /&gt; </p>
		<p>&lt;/configSections&gt; </p>
		<p>&lt;MicrosoftWebControls&gt; </p>
		<p>&lt;add key="CommonFiles" value="/webctrl_client/1_0" /&gt;</p>
		<p>&lt;/MicrosoftWebControls&gt;</p>
		<p>可以修改调用树的htc的位置</p>
		<p>把以上配置中的<br />/webctrl_client/1_0<br />改为<br />网址全称<br /><a href="http://localhost/webctrl_client/1_0">http://localhost/webctrl_client/1_0</a><br /></p>
<img src ="http://www.cnitblog.com/seeyeah/aggbug/22282.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-01-24 20:55 <a href="http://www.cnitblog.com/seeyeah/archive/2007/01/24/22282.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在 ASP.NET 中执行 URL 重写</title><link>http://www.cnitblog.com/seeyeah/archive/2007/01/20/22090.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Fri, 19 Jan 2007 17:02:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/01/20/22090.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/22090.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/01/20/22090.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/22090.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/22090.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 发布日期： 8/23/2004 | 更新日期： 8/23/2004						Scott Mitchell				4GuysFromRolla.com				适用范围：				Microsoft® ASP.NET										摘要：介绍如何使用 Microsoft ASP.NET 执行动态 URL 重写。URL 重写是截取传入 Web 请求并自动将请求重定向到其他...&nbsp;&nbsp;<a href='http://www.cnitblog.com/seeyeah/archive/2007/01/20/22090.html'>阅读全文</a><img src ="http://www.cnitblog.com/seeyeah/aggbug/22090.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-01-20 01:02 <a href="http://www.cnitblog.com/seeyeah/archive/2007/01/20/22090.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> web.config中的session配置详解 </title><link>http://www.cnitblog.com/seeyeah/archive/2007/01/20/22089.html</link><dc:creator>KiMoGiGi</dc:creator><author>KiMoGiGi</author><pubDate>Fri, 19 Jan 2007 16:34:00 GMT</pubDate><guid>http://www.cnitblog.com/seeyeah/archive/2007/01/20/22089.html</guid><wfw:comment>http://www.cnitblog.com/seeyeah/comments/22089.html</wfw:comment><comments>http://www.cnitblog.com/seeyeah/archive/2007/01/20/22089.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cnitblog.com/seeyeah/comments/commentRss/22089.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/seeyeah/services/trackbacks/22089.html</trackback:ping><description><![CDATA[打开某个应用程序的配置文件Web.config后，我们会发现以下这段： <br /><br /><div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid"><div><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  sessionState <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　</span><span style="COLOR: #ff0000">mode</span><span style="COLOR: #0000ff">="InProc"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　stateConnectionString</span><span style="COLOR: #0000ff">="tcpip=127.0.0.1:42424"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　sqlConnectionString</span><span style="COLOR: #0000ff">="data source=127.0.0.1;Trusted_Connection=yes"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　cookieless</span><span style="COLOR: #0000ff">="false"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　timeout</span><span style="COLOR: #0000ff">="20"</span><span style="COLOR: #ff0000">  <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">/&gt;</span></div></div>  <br />　　这一段就是配置应用程序是如何存储Session信息的了。我们以下的各种操作主要是针对这一段配置展开。让我们先看看这一段配置中所包含的内容的意思。sessionState节点的语法是这样的： <br /><br /><div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid"><div><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  sessionState </span><span style="COLOR: #ff0000">mode</span><span style="COLOR: #0000ff">="Off|InProc|StateServer|SQLServer"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />              cookieless</span><span style="COLOR: #0000ff">="true|false"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />              timeout</span><span style="COLOR: #0000ff">="number of minutes"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />              stateConnectionString</span><span style="COLOR: #0000ff">="tcpip=server:port"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />              sqlConnectionString</span><span style="COLOR: #0000ff">="sql connection string"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />              stateNetworkTimeout</span><span style="COLOR: #0000ff">="number of seconds"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"> </span></div></div>  <br /><br />必须有的属性是 <br /><br />属性 选项 描述  <br />mode  设置将Session信息存储到哪里  <br /> Off 设置为不使用Session功能  <br /> InProc 设置为将Session存储在进程内，就是ASP中的存储方式，这是默认值。  <br /> StateServer 设置为将Session存储在独立的状态服务中。  <br /> SQLServer 设置将Session存储在SQL Server中。  <br /><br />可选的属性是： <br /><br />属性 选项 描述  <br />cookieless  设置客户端的Session信息存储到哪里  <br /> ture 使用Cookieless模式  <br /> false 使用Cookie模式，这是默认值。  <br />timeout  设置经过多少分钟后服务器自动放弃Session信息。默认为20分钟  <br />stateConnectionString  设置将Session信息存储在状态服务中时使用的服务器名称和端口号，例如："tcpip=127.0.0.1:42424”。当mode的值是StateServer是，这个属性是必需的。  <br />sqlConnectionString  设置与SQL Server连接时的连接字符串。例如"data source=localhost;Integrated Security=SSPI;Initial Catalog=northwind"。当mode的值是SQLServer时，这个属性是必需的。  <br />stateNetworkTimeout  设置当使用StateServer模式存储Session状态时，经过多少秒空闲后，断开Web服务器与存储状态信息的服务器的TCP/IP连接的。默认值是10秒钟。  <br /><br />ASP.NET中客户端Session状态的存储 <br />　　在我们上面的Session模型简介中，大家可以发现Session状态应该存储在两个地方，分别是客户端和服务器端。客户端只负责保存相应网站的SessionID，而其他的Session信息则保存在服务器端。在ASP中，客户端的SessionID实际是以Cookie的形式存储的。如果用户在浏览器的设置中选择了禁用Cookie，那末他也就无法享受Session的便利之处了，甚至造成不能访问某些网站。为了解决以上问题，在ASP.NET中客户端的Session信息存储方式分为：Cookie和Cookieless两种。 <br /><br />　　ASP.NET中，默认状态下，在客户端还是使用Cookie存储Session信息的。如果我们想在客户端使用Cookieless的方式存储Session信息的方法如下： <br /><br />　　找到当前Web应用程序的根目录，打开Web.Config文件，找到如下段落： <br /><br /><div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid"><div><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  sessionState <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　</span><span style="COLOR: #ff0000">mode</span><span style="COLOR: #0000ff">="InProc"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　stateConnectionString</span><span style="COLOR: #0000ff">="tcpip=127.0.0.1:42424"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　sqlConnectionString</span><span style="COLOR: #0000ff">="data source=127.0.0.1;Trusted_Connection=yes"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　cookieless</span><span style="COLOR: #0000ff">="false"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　timeout</span><span style="COLOR: #0000ff">="20"</span><span style="COLOR: #ff0000">  <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000">  <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /></span></div></div><br />　　这段话中的cookieless="false"改为：cookieless="true"，这样，客户端的Session信息就不再使用Cookie存储了，而是将其通过URL存储。关闭当前的IE，打开一个新IE，重新访问刚才的Web应用程序，就会看到类似下面的样子： <br /><br /><br /><br />　　其中，http://localhost/MyTestApplication/(ulqsek45heu3ic2a5zgdl245)/default.aspx中黑体标出的就是客户端的Session ID。注意，这段信息是由IIS自动加上的，不会影响以前正常的连接。 <br /><br />ASP.NET中服务器端Session状态的存储 <br />准备工作 <br /><br />　　为了您能更好的体验到实验现象，您可以建立一个叫做SessionState.aspx的页面，然后把以下这些代码添加到&lt;  body&gt;&lt;  /body&gt;中。 <br /><br /><br /><div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid"><div><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  scriptrunat</span><span style="COLOR: #0000ff">="server"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />Sub Session_Add(sender As Object, e As EventArgs) <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　  Session("MySession") = text1.Value <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　  span1.InnerHtml = "Session data updated! </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  P</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">Your session contains: </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  font </span><span style="COLOR: #ff0000">color</span><span style="COLOR: #0000ff">=red</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">" &amp; 　　　　　　　　　　  Session("MySession").ToString() &amp; "</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  /font</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">" <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />End Sub <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /><br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />Sub CheckSession(sender As Object, eAs EventArgs) <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　  If (Session("MySession")Is Nothing) Then <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　　 span1.InnerHtml = "NOTHING, SESSION DATA LOST!" <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　  Else <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　　 span1.InnerHtml = "Your session contains: </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  font </span><span style="COLOR: #ff0000">color</span><span style="COLOR: #0000ff">=red</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">" &amp; 　　　　　　　　　　　　 Session("MySession").ToString() &amp; "</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  /font</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">" <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />End If <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />End Sub <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  /script</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  formrunat</span><span style="COLOR: #0000ff">="server"</span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="Form2"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　  </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  inputid</span><span style="COLOR: #0000ff">="text1"</span><span style="COLOR: #ff0000">type</span><span style="COLOR: #0000ff">="text"</span><span style="COLOR: #ff0000">runat</span><span style="COLOR: #0000ff">="server"</span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="text1"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　  </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  inputtype</span><span style="COLOR: #0000ff">="submit"</span><span style="COLOR: #ff0000">runat</span><span style="COLOR: #0000ff">="server"</span><span style="COLOR: #ff0000">OnServerClick</span><span style="COLOR: #0000ff">="Session_Add"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　　　　 value</span><span style="COLOR: #0000ff">="Add to Session State"</span><span style="COLOR: #ff0000"> id</span><span style="COLOR: #0000ff">="Submit1"</span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="Submit1"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　  </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  inputtype</span><span style="COLOR: #0000ff">="submit"</span><span style="COLOR: #ff0000">runat</span><span style="COLOR: #0000ff">="server"</span><span style="COLOR: #ff0000">OnServerClick</span><span style="COLOR: #0000ff">="CheckSession"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　　　　 value</span><span style="COLOR: #0000ff">="View Session State"</span><span style="COLOR: #ff0000"> id</span><span style="COLOR: #0000ff">="Submit2"</span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="Submit2"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  /form</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  hrsize</span><span style="COLOR: #0000ff">="1"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  fontsize</span><span style="COLOR: #0000ff">="6"</span><span style="COLOR: #0000ff">&gt;&lt;</span><span style="COLOR: #800000">  spanid</span><span style="COLOR: #0000ff">="span1"</span><span style="COLOR: #ff0000">runat</span><span style="COLOR: #0000ff">="server"</span><span style="COLOR: #ff0000"> </span><span style="COLOR: #0000ff">/&gt;&lt;</span><span style="COLOR: #800000">  /font</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">  <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /></span></div></div><br />　　这个SessionState.aspx的页面可以用来测试在当前的服务器上是否丢失了Session信息。 <br /><br />将服务器Session信息存储在进程中 <br />　　让我们来回到Web.config文件的刚才那段段落中： <br /><br /><div style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 95%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid"><div><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">  sessionState <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　</span><span style="COLOR: #ff0000">mode</span><span style="COLOR: #0000ff">="InProc"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　stateConnectionString</span><span style="COLOR: #0000ff">="tcpip=127.0.0.1:42424"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　sqlConnectionString</span><span style="COLOR: #0000ff">="data source=127.0.0.1;Trusted_Connection=yes"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　cookieless</span><span style="COLOR: #0000ff">="false"</span><span style="COLOR: #ff0000"> <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" />　　timeout</span><span style="COLOR: #0000ff">="20"</span><span style="COLOR: #ff0000">  <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000">  <br /><img alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndicators/None.gif" align="top" /></span></div></div>　　当mode的值是InProc时，说明服务器正在使用这种模式。 <br /><br />　　这种方式和以前ASP中的模式一样，就是服务器将Session信息存储在IIS进程中。当IIS关闭、重起后，这些信息都会丢失。但是这种模式也有自己最大好处，就是性能最高。应为所有的Session信息都存储在了IIS的进程中，所以IIS能够很快的访问到这些信息，这种模式的性能比进程外存储Session信息或是在SQL Server中存储Session信息都要快上很多。这种模式也是ASP.NET的默认方式。 <br /><br />　　好了，现在让我们做个试验。打开刚才的SessionState.aspx页面，随便输入一些字符，使其存储在Session中。然后，让我们让IIS重起。注意，并不是使当前的站点停止再开始，而是在IIS中本机的机器名的节点上点击鼠标右键，选择重新启动IIS。(想当初使用NT4时，重新启动IIS必须要重新启动计算机才行，微软真是@#$%^&amp;)返回到SessionState.aspx页面中，检查刚才的Session信息，发现信息已经丢失了。 <br /><br />将服务器Session信息存储在进程外 <br />　　首先，让我们来打开管理工具-&gt;服务，找到名为：ASP.NET State Service的服务，启动它。实际上，这个服务就是启动一个要保存Session信息的进程。启动这个服务后，你可以从Windows任务管理器-&gt;进程中看到一个名为aspnet_state.exe的进程，这个就是我们保存Session信息的进程。 <br /><br />　　然后，回到Web.config文件中上述的段落中，将mode的值改为StateServer。保存文件后的重新打开一个IE，打开SessionState.aspx页面，保存一些信息到Session中。这时，让我们重起IIS，再回到SessionState.aspx页面中查看刚才的Session信息，发现没有丢失。 <br /><br />　　实际上，这种将Session信息存储在进程外的方式不光指可以将信息存储在本机的进程外，还可以将Session信息存储在其他的服务器的进程中。这时，不光需要将mode的值改为StateServer，还需要在stateConnectionString中配置相应的参数。例如你的计算你是192.168.0.1，你想把Session存储在IP为192.168.0.2的计算机的进程中，就需要设置成这样：stateConnectionString="tcpip=192.168.0.2:42424"。当然，不要忘记在192.168.0.2的计算机中装上.NET Framework，并且启动ASP.NET State Services服务。 <br /><br />将服务器Session信息存储在SQL Server中 <br />　　首先，还是让我们来做一些准备工作。启动SQL Server和SQL Server代理服务。在SQL Server中执行一个叫做InstallSqlState.sql的脚本文件。这个脚本文件将在SQL Server中创建一个用来专门存储Session信息的数据库，及一个维护Session信息数据库的SQL Server代理作业。我们可以在以下路径中找到那个文件： <br /><br />[system drive]\winnt\Microsoft.NET\Framework\[version]\  <br />　　然后打开查询分析器，连接到SQL Server服务器，打开刚才的那个文件并且执行。稍等片刻，数据库及作业就建立好了。这时，你可以打开企业管理器，看到新增了一个叫ASPState的数据库。但是这个数据库中只是些存储过程，没有用户表。实际上Session信息是存储在了tempdb数据库的ASPStateTempSessions表中的，另外一个ASPStateTempApplications表存储了ASP中Application对象信息。这两个表也是刚才的那个脚本建立的。另外查看管理-&gt;SQL Server代理-&gt;作业，发现也多了一个叫做ASPState_Job_DeleteExpiredSessions的作业，这个作业实际上就是每分钟去ASPStateTempSessions表中删除过期的Session信息的。 <br /><br />　　接着，我们返回到Web.config文件，修改mode的值改为SQLServer。注意，还要同时修改sqlConnectionString的值，格式为： <br /><br />sqlConnectionString="data source=localhost; Integrated Security=SSPI;" <br />　　其中data source是指SQL Server服务器的IP地址，如果SQL Server与IIS是一台机子，写127.0.0.1就行了。Integrated Security=SSPI的意思是使用Windows集成身份验证，这样，访问数据库将以ASP.NET的身份进行，通过如此配置，能够获得比使用userid=sa;password=口令的SQL Server验证方式更好的安全性。当然，如果SQL Server运行于另一台计算机上，你可能会需要通过Active Directory域的方式来维护两边验证的一致性。 <br /><br />　　同样，让我们做个试验。向SessionState.aspx中添加Session信息，这时发现Session信息已经存在SQL Server中了，即使你重起计算机，刚才的Session信息也不会丢失。现在，你已经完全看见了Session信息到底是什么样子的了，而且又是存储在SQL Server中的，能干什么就看你的发挥了，哈哈。 <br /><br />总结 <br />　　通过这篇文章，你可以看到在Session的管理和维护上，ASP.NET比ASP有了很大的进步，我们可以更加随意的挑选适合的方法了。对于企业级的应用来说，这无疑对于服务器的同步、服务器的稳定性、可靠性都是有利的。相信在强大的微软支持下，新一代的电子商务平台将会搭建的更好！ <br /><br />　　同时，大家也会发现，在这个整个技术中包括了操作系统、Web服务及数据库多种技术的整合。我相信，也许Windows没有Unix稳定，IIS没有Apache稳定，SQL Server也没有Oracle强大，但是，谁可以将他们如此完美的联动到一起呢？所以说，虽然微软每一方面都不是太强，但是如果把微软的东西都整合到一起，谁敢说他不强大呢？微软就是微软！<img src ="http://www.cnitblog.com/seeyeah/aggbug/22089.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/seeyeah/" target="_blank">KiMoGiGi</a> 2007-01-20 00:34 <a href="http://www.cnitblog.com/seeyeah/archive/2007/01/20/22089.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>