<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_阿不</title><subtitle type="text">在腾讯微博和新浪微博 @hjf1223 ，最新的技术观点在那</subtitle><id>http://feed.cnblogs.com/blog/u/13109/rss</id><updated>2012-01-06T03:27:31Z</updated><author><name>阿不</name><uri>http://www.cnblogs.com/hjf1223/</uri></author><generator>feed.cnblogs.com</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hjf1223/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/13109/rss"/><entry><id>http://www.cnblogs.com/hjf1223/archive/2011/09/28/windows_phone7_finance_calculator.html</id><title type="text">Windows phone7 软件发布：理财计算器（包括wp7房贷计算器，wp7个税计算器，wp7存款利息计算器）</title><summary type="text">前一段时间，需要经常用到贷款计算器的功能，这样有利于我们做出更好的决策。但是我们只能通过银行的工作人员的计算器来计算，给我带来了极大的不便和损失，由此便萌生了开发一个Windows phone7版的贷款计算器的功能。 此计算器目前的最新版本为：1.1.0.0，具有房贷计算器，2011个税计算器，存款利息计算器的功能: 房贷计算器：根据不同的贷款类型（商业，公积金，组合贷款），选择不同的还款方式（等额本息，等额本金），计算不同的贷款金额以年限的条件下，需要支出的月供情况情况 ，包括每期的月供明细。以供购房者选择合适的贷款方式提供参考。 个税计算器：根据2011年新版的个人所得税条例，计算公民..</summary><published>2011-09-28T04:30:00Z</published><updated>2011-09-28T04:30:00Z</updated><author><name>阿不</name><uri>http://www.cnblogs.com/hjf1223/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hjf1223/archive/2011/09/28/windows_phone7_finance_calculator.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hjf1223/archive/2011/09/28/windows_phone7_finance_calculator.html"/><content type="html">&lt;p&gt;前一段时间，需要经常用到贷款计算器的功能，这样有利于我们做出更好的决策。但是我们只能通过银行的工作人员的计算器来计算，给我带来了极大的不便和损失，由此便萌生了开发一个Windows phone7版的贷款计算器的功能。&lt;/p&gt; &lt;p&gt;此计算器目前的最新版本为：1.1.0.0，具有房贷计算器，2011个税计算器，存款利息计算器的功能:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;房贷计算器：根据不同的贷款类型（商业，公积金，组合贷款），选择不同的还款方式（等额本息，等额本金），计算不同的贷款金额以年限的条件下，需要支出的月供情况情况 ，包括每期的月供明细。以供购房者选择合适的贷款方式提供参考。  &lt;li&gt;个税计算器：根据2011年新版的个人所得税条例，计算公民的工资需缴交的所得税情况。  &lt;li&gt;存款利息计算器：根据不同的存款方式，存款期限，利率和存款金额计算应得的利息。&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;此外，值得一提的是。这个软件仍然是以开源的形式发布源码，项目源码地址：&lt;a href="http://financingcalculator.codeplex.com" target="_blank"&gt;http://financingcalculator.codeplex.com&lt;/a&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;目前，正在申请将此软件发布到Maketplace，在发布到maketplace，目前大家只能通过.XAP文件来部署软件，XAP文件下载地址：&lt;a href="http://download.codeplex.com/Download?ProjectName=financingcalculator&amp;amp;DownloadId=286964&amp;amp;FileTime=129617346894200000&amp;amp;Build=18207"&gt;Calcualtor_1.1.0.0.xap&lt;/a&gt;&lt;/p&gt; &lt;p&gt;最后，庆祝一下 Windows phone7 mango的正式推送吧，不知道你升级了没？反正我已经升级了。&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/hjf1223/201109/201109301020279102.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="480x800" border="0" alt="480x800" src="http://images.cnblogs.com/cnblogs_com/hjf1223/201109/201109301020272657.png" width="484" height="804"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;font color="#ff0000"&gt;&lt;font size="3"&gt;此款软件已经在Marketplace上架了，地址是：&lt;/font&gt;&lt;a href="http://www.windowsphone.com/en-US/apps/371c3263-51d0-4f51-aad2-f83580a13e3e"&gt;&lt;font size="3"&gt;http://www.windowsphone.com/en-US/apps/371c3263-51d0-4f51-aad2-f83580a13e3e&lt;/font&gt;&lt;/a&gt;&lt;font size="3"&gt; ，下载地址： zune://navigate/?phoneappID=371c3263-51d0-4f51-aad2-f83580a13e3e&lt;/font&gt; &lt;/font&gt;&lt;img src='http://t3.qpic.cn/mblogpic/b9acee8ee6d80bed1a70/460' alt="下载地址"/&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hjf1223/aggbug/2194049.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hjf1223/archive/2011/09/28/windows_phone7_finance_calculator.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hjf1223/archive/2011/06/27/2091052.html</id><title type="text">从.NET到Mono－记Kooboo CMS对Mono的兼容历程：三、平台的兼容性</title><summary type="text">从基于.NET Framework迁移到Mono平台，除了经常会遇到大小写敏感的问题之外，还会经常遇到一些平台不兼容性的问题，这也是我的经历当中，遇到的另一个比较麻烦的问题。其实关于Mono平台的兼容性，在官方的文档中已经释放了比较明确的信息，他们就明确的一句话来描述Mono的兼容性：“Everything in .NET 4.0 except WPF, EntityFramework and W...</summary><published>2011-06-26T23:32:00Z</published><updated>2011-06-26T23:32:00Z</updated><author><name>阿不</name><uri>http://www.cnblogs.com/hjf1223/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hjf1223/archive/2011/06/27/2091052.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hjf1223/archive/2011/06/27/2091052.html"/><content type="html">&lt;p&gt;从基于.NET Framework迁移到Mono平台，除了经常会遇到大小写敏感的问题之外，还会经常遇到一些平台不兼容性的问题，这也是我的经历当中，遇到的另一个比较麻烦的问题。其实关于Mono平台的兼容性，在&lt;a href="http://www.mono-project.com/Compatibility" target="_blank"&gt;官方的文档&lt;/a&gt;中已经释放了比较明确的信息，他们就明确的一句话来描述Mono的兼容性：“&lt;b&gt;Everything in .NET 4.0 except WPF, EntityFramework and WF, limited WCF.”&lt;/b&gt;。但是实际上并不是这么简单的！在Kooboo CMS中，目前没有用过WPF、EF、WF、WCF，但是仍然遇到了几个由于平台的兼容性而导致的问题。这些不兼容，本身就是Mono设计的意图，出于操作系统平台的差异性，Mono可能会选择对部分功能的不兼容；我还遇到了一些在.NET Framework中编译正常的代码，但是在Mono中无法编译通过，这种可能是对编译器严谨性设计上的不同；同时，对于已支持的API，虽然功能确实是支持，但是我们还是会得到一些与.NET Framework不大一样的结果而造成不兼容的情况出现。&lt;/p&gt; &lt;p&gt;对于程序集出现不兼容的情况时，不像.NET Framework在启动时经常出现的各种程序集无法载入而让整个站点无法启动，MONO的策略是比较好的。我们完全可以把在.NET中编译好的Web站点，直接放到Mono运行时去执行。这时候，假定你程序中使用了MONO不支持的某些程序集或API的时候，它并不会马上就告诉你程序无法启动，而是只要你还没有用到这些功能，程序就还可以正常运行。所以我们会经常发现，我们发布好的站点，可能可以直接在MONO中跑起来，但是当我们把源码用MonoDevelop打开时，却无法编译通过，出现不少的编译错误。我很喜欢这点设计。&lt;/p&gt; &lt;p&gt;尽管如此，要让ASP.NET站点可以完全兼容MONO，调试是必不可少的。所以我们必须保证我们的源码可以在MonoDevelop中正常打开，并且完全编译通过。下面的几点不兼容或是在编译时就发现，或是程序出错了错误后，通过MonoDevelop的调试发现并且解决。让我们一起来看一下吧：&lt;/p&gt; &lt;ol&gt; &lt;li&gt;ASP.NET HealthMonitoring的不兼容。在源码用MonoDevelop打开之后，一编译就发现好多好多的错误，其中大部分的错误是出现在所重写的几个HealthMonitoring的类上面。经常一段时间的研究发现，Mono在System.Web.Management的空间下，只有少数的几个类，而且这些类的大部分都没有具体的实现。对于这点不兼容，自然没什么好说的，也没有好的解决办法，再找一个好的替代方案。不过，我相信这个功能应该是可以实现的，除了Eventlog有系统平台特性之外，应该没有更多的平台依赖。  &lt;li&gt;在编译错误中，有一行代码，在.NET中运行正常，在MONO中却出错，提示我们需要做一下类型转换。为了更好的说明这个编译错误，我把代码原型提炼出来：public interface IReadonly&lt;br/&gt;{&lt;br/&gt;    string Name{get;}&lt;br/&gt;}&lt;br/&gt;public class BaseClass&lt;br/&gt;{&lt;br/&gt;    public string Name {&lt;br/&gt;        get;&lt;br/&gt;        set;&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;public class Class1:BaseClass,IReadonly&lt;br/&gt;{&lt;br/&gt;}&lt;br/&gt;class MainClass&lt;br/&gt;{    &lt;br/&gt;    public static void SetName&amp;lt;T&amp;gt;(T o)&lt;br/&gt;        where T:BaseClass,IReadonly&lt;br/&gt;    {        &lt;br/&gt;        o.Name= "";&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;&lt;p&gt;这段代码中，o.Name=“”这行在Mono中会提示我们“Ambiguity between BaseClass.Name and IReadonly.Name”。对于这个问题，解决方案很简单，只需要做对o作一下类型转换，转成BaseClass的类型再赋值就可以通过了。我本身对编译器的行为了解不是特别多，所以讲不出个所以然出来。只能说，这是两种编译器在类型处理严谨性上面的不同吧。&lt;/p&gt;&lt;li&gt;对于Mono支持的API，我们仍然也需要经过运行测试之后才能保证它是否工作正常。相同的一个API，不同的平台，产生不同的结果，这个是很容易理解的。我这边就遇到两个这样的问题。 &lt;ul&gt;&lt;li&gt;在使用KeyedHashAlgorithm对字符串进去HASH的结果不同。因为我们使用了KeyedHashAlgorithm对用户密码进行一次加密，但是相同的用户数据和输入密码在MONO运行时，总是提示密码不正确。经过调试发现，是由于.NET和MONO所使用的密钥长度不同，还好只是使用了密钥长度。我们只需要让它们使用一样的长度就可以了，加上这行代码：algorithm.Key = new byte[64]; &lt;li&gt;我使用了DataContractSerializer来序列化对象。在序列化普通对象的时候，它们的格式都是一致的。但是在序列化Dictionary对象的时候，.NET和Mono就会有一些差异，这些差异主要集中在XML名称空间上。在.NET中，序列化的根结点的名称空间是http://schemas.microsoft.com/2003/10/Serialization/Arrays，而MONO序列化后的根结点名称空间为：&lt;a href="http://schemas.datacontract.org/2004/07/System.Collections.Generic"&gt;http://schemas.datacontract.org/2004/07/System.Collections.Generic&lt;/a&gt;。这就造成了平台之间的数据切换的不兼容。不过，经过一段时间的研究还是找到的解决办法，只需要在实例化DataContractSerializer的时候指定使用.NET的根结点名称空间就可以解决问题：DataContractSerializer ser = new DataContractSerializer(typeof(List&amp;lt;Dictionary&amp;lt;string,object&amp;gt;&amp;gt;),"ArrayOfArrayOfKeyValueOfstringanyType","http://schemas.microsoft.com/2003/10/Serialization/Arrays",new Type[]{typeof(Dictionary&amp;lt;string,object&amp;gt;)})&lt;br/&gt;&lt;p&gt;对于序列化的不兼容，这边还有另一个问题，就是对IDictionary的处理手法上Mono和.NET还是有一些不同的。在.NET中，对象的属性中包含IDictionary对象是可以正常序列化和反序列化，但是在MONO运行时就会提示“'this' type cannot be an interface itself”。解决这个错误的办法就是把IDictionary改成为Dictionary，用具体的类型，而不是用接口。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;以上的这些问题都是我目前能记起来的所遇到的平台的不兼容问题。大家可以看到，除了MONO本身不支持的功能和API之外，其它的兼容性问题经过一段时间的调试和研究之后都还是可以顺利解决的。由此我相信，要让一个普通的ASP.NET站点兼容MONO，应该不仅仅只存在于理论上的可能了。&lt;/p&gt;&lt;p&gt;本来计划，本篇是作为一个结束篇来写的。但是在写的过程中发现，还有一些其它方面的心得还漏掉很多，比如使用MonoDevelop的经验，调试ASP.NET MVC程序的经验等等。这些都与Windows/.NET平台还是有一些区别的。所以下篇准备分享和记录一下这方面的经验。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hjf1223/aggbug/2091052.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hjf1223/archive/2011/06/27/2091052.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hjf1223/archive/2011/06/15/2082220.html</id><title type="text">从.NET到Mono－记Kooboo CMS对Mono的兼容历程：二、大小写敏感问题，到处都是地雷</title><summary type="text">在Linux/Unix系统中，对任何文件路径，文件名，URL地址的处理都是大小写敏感。对于这点设计，让我们长期从事基于Windows平台开发和工作的开发人员情何以堪啊。尽管可能这样设计是有它的特殊目的和出发点，但我直到现在仍然认为这点是一个非常蛋疼的设计。我试图找到一些理由来解释Linux为什么处理这些字符串使用大小写敏感，但是得到的答案都是一些无关痛痒的理由： 大小写敏感后，可以用更短的文件名来表示更多的文件。比如a.txt和A.txt它们是不是一样的文件，但是大小写不敏感后，它们只能表示同一个文件。 大小写敏感，让字符串（文件名）更容易排序。 Linux是用C语言写的，在C语言里面，字符串</summary><published>2011-06-15T15:27:00Z</published><updated>2011-06-15T15:27:00Z</updated><author><name>阿不</name><uri>http://www.cnblogs.com/hjf1223/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hjf1223/archive/2011/06/15/2082220.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hjf1223/archive/2011/06/15/2082220.html"/><content type="html">&lt;p&gt;在Linux/Unix系统中，对任何文件路径，文件名，URL地址的处理都是大小写敏感。对于这点设计，让我们长期从事基于Windows平台开发和工作的开发人员情何以堪啊。尽管可能这样设计是有它的特殊目的和出发点，但我直到现在仍然认为这点是一个非常蛋疼的设计。我试图找到一些理由来解释Linux为什么处理这些字符串使用大小写敏感，但是得到的答案都是一些无关痛痒的理由：&lt;/p&gt; &lt;ul&gt; &lt;li&gt;大小写敏感后，可以用更短的文件名来表示更多的文件。比如a.txt和A.txt它们是不是一样的文件，但是大小写不敏感后，它们只能表示同一个文件。  &lt;li&gt;大小写敏感，让字符串（文件名）更容易排序。  &lt;li&gt;Linux是用C语言写的，在C语言里面，字符串是大小写敏感的。难道C++,C#不是大小写敏感？  &lt;li&gt;大写和小写字符，本身它们的ASCII值就不一样，本来就应该认为它们是不一样的字符？&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;上面的这些讨论，难道有一个理由可以成为大小写敏感后所带来的不便的替罪羊（请允许我以一个Windows用户的角度来评论这个设计）？在我这段时间的跨平台研究经验中，这个问题已经不再是方不方便的问题了，已经上升到软件兼容层面上的很大问题。你可以说，这是我们自己写程序的的随意性造成的。但是对于长期基于Windows平台开发的开发人员，又有多少程序员会注意到文件路径和文件名的大小写统一问题。在我所遇到的问题中，至少80%以上是由于文件名和路径大小写的不一致所导致的，而这些问题，有时候也是相当长的时间来进行调试后才会察觉出来。下面就来看看，我到目前为止，发现的由大小写敏感所带来的软件的兼容问题吧：&lt;/p&gt; &lt;ul&gt; &lt;li&gt;首页一打开，你会发现出现一个404错误。如果运气好的话，那你检查一下是不是把Index.aspx写成index.aspx。&lt;img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-embarrassedsmile" alt="尴尬" src="http://images.cnblogs.com/cnblogs_com/hjf1223/201106/201106152327147256.png"&gt;  &lt;li&gt;当你把页面打开之后，你发现怎么页面的样式都乱七八糟的，根本就不是我们正常的显示效果。那我们可能要借助一下Firebug或是其它的HTTP网络请求查看工具来看一下是不是有哪些样式的请求出错了，是不是把L写成l了。&lt;img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-cryingface" alt="哭泣的脸" src="http://images.cnblogs.com/cnblogs_com/hjf1223/201106/201106152327142273.png"&gt;  &lt;li&gt;当你把样式的问题也解决了以后，你可能还会有很多图片，logo没有办法正常显示。如果在Windows下面访问正常的话，那你的第一个反应也是应该检查一下大小写问题。  &lt;li&gt;同样的问题，还会出现在请求脚本文件中。  &lt;li&gt;如果你的程序中，有做文件读写操作的话，80%是可能会运行不正常的，同样是因为你的文件名（路径）大小写不一致引起的，要重点检查。  &lt;li&gt;我们使用MVC，最经常的都是URL，controller，View的名称会是以一种约定的形式存在，并且VS提供了一个很好的脚手架，让我们可以确保Controller和View的大小写基本都能保持一致，但这样就够了吗？在这种请求中，你也许会经常收到各种View无法找到的异常，第一个反应，你就是应该要检查一下文件和路径的大小问题了。但是要解决这个问题，却不是通过修改程序就能解决的。假设有AccountController这个View，在Windows平台下，我们通过account或是Account都可以正常请求，但在Linux下，你通过account是不能正常请求的。不能正常请求，不是因为Controller无法正常处理，因为MVC中，本身对查找controller已经是大小写不敏感了。但是由于你是用account来请求的，那么它此时认为的controllerName是account，它会以account这个字符串去找相应的View，假定我们原本的View是Account，那此时它一定是会找错的。这个问题，基本没有什么好办法，要么从底层的IOMAP来解决，要么就是强制要求URL也大小写敏感。  &lt;li&gt;同样在MVC中，有一个Metadata，叫做UIHit，它可以用来指定某个字段对应的编辑模板的。在这里，我们假设使用了UIHit("password")，但是在EditorTemplates中，存在Password.cshtml，同样的让我们的页面产生一些CSS兼容性问题的错觉。&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;还好，CSS，Javascript的语法都大小写敏感的，要不然痛苦还会多很多。&lt;/p&gt; &lt;p&gt;以上提到的这些大小写的问题，Mono团队也很早就意识到如果不解决，可能会是Mono更好发展的一个障碍。因此他们提供了一项叫做&lt;a href="http://www.mono-project.com/IOMap" target="_blank"&gt;IOMap&lt;/a&gt;的底层映射来屏蔽由文件路径大小不同所带来了兼容性问题。但是，在介绍中，只是提及对基于Apache的配置例子，也许也有针对XSP的支持例子：&lt;/p&gt; &lt;p&gt;MONO_OPTIONS="--debug --profile=iomap" xsp2&lt;/p&gt; &lt;p&gt;但是由于我需要在MonoDevelop中调试更多的其它问题，因此我也需要在MonoDevelop用XSP启动站点调试时开启IOMap的选项，可惜，至今我还没有设置的办法。今晚，我看到了&lt;a href="http://www.mono-project.com/Release_Notes_MonoTools_2.0_Beta_1" target="_blank"&gt;Mono Tools for Visual Studio 2.0 Beta 1&lt;/a&gt;调试可以配置这个选项，但为何这个Mono_Tools也可以跨平台呢？莫非它不仅仅是一个Visual Studio的插件？也许奥秘就在这里。希望过几天我就可以解决调试时使用IOMap的问题。这样真的可以减少太多的代码改动了&lt;img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-openmouthedsmile" alt="大笑" src="http://images.cnblogs.com/cnblogs_com/hjf1223/201106/201106152327179571.png"&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;本文结束&lt;/p&gt;&lt;p&gt;关于MonoDevelop的调试大小写问题的官方邮件列表讨论：&lt;a href='http://go-mono.com/forums/#nabble-td3606760' target="_blank"&gt;http://go-mono.com/forums/#nabble-td3606760&lt;/a&gt; &lt;/p&gt;&lt;img src="http://www.cnblogs.com/hjf1223/aggbug/2082220.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hjf1223/archive/2011/06/15/2082220.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hjf1223/archive/2011/05/31/2065160.html</id><title type="text">从.NET到Mono－记Kooboo CMS对Mono的兼容历程：一、Mono以及移平台的基本注意要点</title><summary type="text">前言 话说Mono是一个非常有意思的平台，它提供了对.NET程序的跨平台迁移的可能性，它是一种看上去可以粉碎诟病者对.NET平台软件不具跨平台性质疑的技术。我非常喜欢.NET平台，同时我也非常希望它具...</summary><published>2011-05-31T13:13:00Z</published><updated>2011-05-31T13:13:00Z</updated><author><name>阿不</name><uri>http://www.cnblogs.com/hjf1223/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hjf1223/archive/2011/05/31/2065160.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hjf1223/archive/2011/05/31/2065160.html"/><content type="html">&lt;p&gt;&lt;strong&gt;&lt;font style="font-weight: bold"&gt;前言&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;话说Mono是一个非常有意思的平台，它提供了对.NET程序的跨平台迁移的可能性，它是一种看上去可以粉碎诟病者对.NET平台软件不具跨平台性质疑的技术。我非常喜欢.NET平台，同时我也非常希望它具备满足各种需要的特性，包括跨平台特性，因此我也对Mono技术非常感兴趣。由于我这两年以来一直在从事CMS开源项目的相关开发，在工作便利和技术研究的兴趣的驱动下，我一直非常希望能让Kooboo CMS支持Mono平台下的运行，进而也能进一步的让更多人了解Mono和Kooboo CMS。这是我近年来少有的一个明确的技术目标。&lt;/p&gt; &lt;p&gt;Kooboo CMS3.0版本，在去年完全重写了所有的代码，包括架构。在这次重写的过程中，增加了很多基于更小权限要求，更少依赖系统资源，更少的第三方程序集依赖等更具跨平台可能的考虑。&lt;/p&gt; &lt;ul&gt; &lt;li&gt;更小的权限要求：以前我在写代码的时候，都不会考虑代码的运行安全问题，不管使用什么技术手段，能让程序正常运行起来就是唯一的目标。所以我经常会用一些需要具备比较高代码信任等级的代码。比如：emit；系统文件操作；无限制的反射代码；记录到系统事件日志等等很多需要Full trust level的代码运行权限。而这种的代码，会让程序在主机空间受到很大限制，甚至于运行不起来，从而造成这个项目无法被大量用户使用。另一方面，我认为，需要更高信任等级的代码，可能会在跨平台的迁移中带来更大的麻烦。因此，尽量把代码限制在尽可能小的拖管范畴之内会更有助于对Mono平台的支持，毕竟Mono也是没有完全实现.NET的所有功能。&lt;/li&gt; &lt;li&gt;更少的依赖系统资源：这个就更加显而易见了。当我们使用了具有系统平台特征的功能和API时，要做迁移的时候就有可能因为某个功能和API的缺失而让我们不得不放弃。另一个例子，我们平常习惯了Windows平台下的开发，在做文件路径处理的时候我们很多人都会直接用”\”来拆分或合并，但是当你的代码中有大量这种潜规则的时候，就会给我们的迁移工作带来更大的麻烦。&lt;/li&gt; &lt;li&gt;更少的依赖第三方程序集：这点不管是否需要具备迁移性，对于我们普通的开发也是非常重要的。当你依赖了很多第三方程序集的时候，当有其中一个不具备跨平台特性的话，那就非常麻烦了。&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;尽管在设计之初做了充分的考虑，也有一定的规划。但是最近一段时间以来的实践经验也告诉我，从.NET到Mono绝对不是一件很容易和轻松的事情，这之间你会遇到各种各样不可预知的问题。一方面，可能是因为Mono平台还不具备大规模的成功案例和充分测试使用，可能我们每个使用者在使用中都可能会发现其他人从来没有遇到过的问题和Bug；另一方面，不同系统平台的特点，让Mono不得不能出于兼容的考虑，或者其它方面的考虑，使用与普通的.NET环境不一样的配置环境，从而让我们的程序运行出错。尽管如此，我还是磕磕绊绊的走完了一半的路程，到目前为止，我已经可以成功的在Mono+Linux平台下让Kooboo CMS运行起来，并且可以做大部分的基础的事情。目前还有一些仍然绕不过去的问题等着我去研究解决，但可能还需要花费比较长的时间。&lt;/p&gt; &lt;p&gt;这个记录过程是以微博的形式简单记录，但是我认为，要让知识得到更好的总结和传播，博客的效应绝对会比微博来的方便和系统一些。所以我准备不定期的把我的迁移经验总结一下，希望这个总结的过程也能让我更具动力的去研究和解决在mono中遇到的各种问题（目前处于时断时续的状态）。今天先简单的总结一下Mono平台的.NET开发需要注意的一些基本注意事项。（另外，需要说明的是，这边只关注ASP.NET的Mono迁移，并不涉及Winform，Sivlerligh，WPF的Mono支持。）&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&lt;font style="font-weight: bold"&gt;基本注意事项&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;在Mono官网的这篇 &lt;a href="http://www.mono-project.com/Guidelines:Application_Portability"&gt;Guidelines:Application Portability&lt;/a&gt;大体的介绍了下，从Windows平台的开发到Linux平台的迁移需要注意的一些要点。这些要点，我们上面都大致有过简单的介绍：&lt;/p&gt; &lt;ul&gt; &lt;li&gt;大小写敏感问题。这是第一个，也是最经常出错的问题。虽然我不止一次的提醒自己在Liunx下面，任何字符串都是大小写敏感，包括文件路径，但我还是不止的在这个问题上在栽跟头。经常是一个样式或脚本怎么请求都无效，我还在那边检查是不是哪些的解析有问题了。但过了几十分钟之后发现，你的路径中某一个字符大小写不一致了。我认为，Linux的路径大小写问题真的很蛋疼，不管它有什么用处，Windows大小写不敏感不也用的好好的？？&lt;/li&gt; &lt;li&gt;路径分隔符，如上我所提到的在代码中直接使用“\”来拆分或合并路径。在这里，这种情况应该要被绝对杜绝。System.IO.Path这个类里面提供了很多API来帮我们处理到这些问题，请尽量养成用这个类的习惯吧。&lt;/li&gt; &lt;li&gt;绝对路径问题，在linux下面，绝对路径的表达方式与Windows会有所不同。因此，还是尽量会一些系统的静态API来帮我们解决这些问题吧。&lt;/li&gt; &lt;li&gt;P/Invoke，这种操作也是绝对不允许的。因为你所调用的Win32 API，它所依赖的运行平台没有办法做到跨平台调用。&lt;/li&gt; &lt;li&gt;注册表操作，同理，不应该出现这种情况 。&lt;/li&gt; &lt;li&gt;底层的计算机指令的控制，这个我们写.NET程序应该几乎不会出现，略过。（IL代码不晓得是否可以跨平台？应该是不行吧。）&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;总之，它所有的要求就是遵从所有平台所共同具有的特性，尽量避免使用具备明显的特定平台特征的代码。除了，现有的Mono的官方文档所指出的明显不支持的那些功能之外，我们还会遇到各种它所没有提及的没有实现或来不及实现的一些技术细节，我遇到了一些问题包括：加密/解密类库的密钥长度与.NET不一样导致的，加密/解密失败（已经解决）；不支持HealthMornitoring功能；对DataContractSerializer序列化反序化的格式不一致（目前仍然没有解决）；对某些语言特性的编译不一致造成的编译失败。&lt;/p&gt; &lt;p&gt;关于Mono平台的兼容性，我们可以通过&lt;a href="http://mono-project.com/Compatibility"&gt;Compatibility&lt;/a&gt;文档了解一下，但是某些功能的兼容性在新版本已经有所变化。比如在2.10.2中，已经完全解决了MVC3的兼容问题。对于API的兼容性，我们可以通过这个链接：&lt;a href="http://www.go-mono.com/status/"&gt;http://www.go-mono.com/status/&lt;/a&gt; 来具备了解一下。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&lt;font style="font-weight: bold"&gt;关于调试&lt;/font&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;如果我们直接把用VS编译好的程序拿到Mono/Linux下来运行的时候，出现任何问题，我们都无法简单的通过它的异常提示消息来得到很准备的问题原因。相同的一个功能，如果出现在Windows下运行正常，而Mono/linux下出错的话。除了调试，我们基本也没有更好的手段来排错。因此，我们做好使用在Mono/Linux下调试程序的准备。可能目前唯一的选择的是在Linux下，通过MonoDevelop运行程序来调试，这是目前最为简单的方法了吧。在这里，我们要知道，一个在VS编译好的程序，可以直接在Mono/Linux下运行，没有问题。但是这个程序的代码如果要用MonoDevelop，使用Mono运行时来编译的话，你可能就没有那么轻松过关了&lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="眨眼" src="http://images.cnblogs.com/cnblogs_com/hjf1223/201105/201105312113201760.png"&gt;。不过，不用担心，只要我们把那些编译错误解决之后，使用MonoDevelop+XSP来调试的话，用户体验还是不错的。&lt;/p&gt; &lt;p&gt;本文结束&lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-openmouthedsmile" alt="大笑" src="http://images.cnblogs.com/cnblogs_com/hjf1223/201105/201105312113208139.png"&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hjf1223/aggbug/2065160.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hjf1223/archive/2011/05/31/2065160.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hjf1223/archive/2011/02/20/mono_windows.html</id><title type="text">ASP.NET MVC3 on Mono的折腾（一）：Windows下的部署</title><summary type="text">Mono 2.10支持MVC3和Razor的消息着实让我兴奋了好一会儿，因为支持MVC3后，我就有可能做Kooboo CMS3兼容Mono的相关测试工作。可是没一会儿，我就发现，离我的目标还是有一点距离。不晓得是Mono 2.10本身存在的bug，还是我的原因，我总是无法很完美的在Mono运行起MVC3的站点，即使是用默认的MVC3站点模板创建的非常简单的站点也是一样。长期生活在微软Windows/.NET平台的滋润环境中，没有Linux平台的使用经验和基于配置文件的Web部署习惯，让我花了不少的时间来解决原本很简单的问题的。从使用XSP失败，到折腾Windows下使用Nginx+FastCG</summary><published>2011-02-20T08:45:00Z</published><updated>2011-02-20T08:45:00Z</updated><author><name>阿不</name><uri>http://www.cnblogs.com/hjf1223/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hjf1223/archive/2011/02/20/mono_windows.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hjf1223/archive/2011/02/20/mono_windows.html"/><content type="html">&lt;p&gt;Mono 2.10支持MVC3和Razor的消息着实让我兴奋了好一会儿，因为支持MVC3后，我就有可能做Kooboo CMS3兼容Mono的相关测试工作。可是没一会儿，我就发现，离我的目标还是有一点距离。不晓得是Mono 2.10本身存在的bug，还是我的原因，我总是无法很完美的在Mono运行起MVC3的站点，即使是用默认的MVC3站点模板创建的非常简单的站点也是一样。长期生活在微软Windows/.NET平台的滋润环境中，没有Linux平台的使用经验和基于配置文件的Web部署习惯，让我花了不少的时间来解决原本很简单的问题的。从使用XSP失败，到折腾Windows下使用Nginx+FastCGI来Host ASP.NET MVC3站点，之后再花了半个周末如痴如醉的研究openSUSE下的Mono部署。虽然没有解决MVC3的运行问题，但是总算还是搞清楚了如何在Mono部署ASP.NET站点，以及简单的linux平台(openSUSE)操作。下面就简单的记录一下这些艰难历程：&lt;/p&gt; &lt;p&gt;&lt;strong&gt;环境准备&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;在VS2010，用默认的MVC3站点模板创建一个简单MVC站点，有简单的首页和登录页面，并且在Bin下面要私有部署MVC3的相关依赖程序集除了Microsoft.Web.Infrastructure.dll。&lt;a href="http://files.cnblogs.com/hjf1223/Mono_MVC3.zip"&gt;站点下载&lt;/a&gt;。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;1.使用Mono 2.10的XSP服务器失败&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Mono支持跨平台，当然包括Windows平台，而且在Windows平台下面，它也提供了一种非常简单的方式来让我们方便的通过Mono启动Web站点，就这是XSP。在Mono安装成功之后，它会在目录的右键菜单中增加一个菜单项“XSP 2 Web Server Here 2.10”，而我们只需要简单的选择这个菜单项就可以将目录作为Web站点用Mono运行起来。而这里的XSP2，表示，我们使用的是ASP.NET 2.0，如果你需要用ASP.NET 4.0，那你可以通过修改注册表的方式再增加一个类似的菜单项，只是把执行的批处理由xsp2.bat改为xsp4.bat就行了。这原本是最为简单的测试方案，而且我使用Mono2.8来启动MVC2站点也是没有任何问题。结果在Mono 2.10中，XSP的站点死活就是不响应，那个请求让他运行一万年，它也是在那边loading，也不timeout。问题通过各种努力，最终无力解决。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;2.在Windows平台下使用Nginx+FastCGI-mono-server架起MVC3站点，但是表单提交后，ModelBinder无法正常绑定Action Model.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;XSP行不通，只能想其它办法。在尝试了在Windows使用Apache失败之后，选择了Nginx+FastCGI来架构ASP.NET站点。在园子里面找到&lt;a href="http://www.cnblogs.com/zealic/archive/2010/04/01/1701897.html" target="_blank"&gt;这篇&lt;/a&gt;介绍在Mono架设ASP.NET站点，不过根据这篇文章我始终没有搞明白具体如何配置，好像很复杂的样子，还要下载它提供的文件，尝试无果。幸好在Mono的官方站点找到&lt;a href="http://www.mono-project.com/FastCGI_Nginx" target="_blank"&gt;Nginx的Mono配置文件&lt;/a&gt;，不过是Linux下面的配置。没关系，看那配置也就那几个步骤，应该很简单。期间也遇到过一些问题，不过都顺利解决，下面简单记录一下Nginx+Mono在Windows下部署ASP.NET站点的步骤：&lt;/p&gt; &lt;p&gt;1）从&lt;a href="http://nginx.org/en/download.html" target="_blank"&gt;Nginx 的官方网站&lt;/a&gt;下载最新的Nginx软件，纯绿色，无需安装。下载之后，解压到某一目录，这里是：E:\Mono\nginx-0.9.4&lt;/p&gt; &lt;p&gt;2）为了方便，把你的ASP.NET站点，也拷到Nginx目录下面，我这边使用的目录名称是：Mono_MVC3&lt;/p&gt; &lt;p&gt;3）进入conf目录下，找到nginx.conf，找到location /{}，我们把这段配置改为我们自己的值：&lt;/p&gt;location / {&lt;br/&gt;         root Mono_MVC3; #如果是其它绝对路径要用：E:/a/b/c&lt;br/&gt;         &lt;br/&gt;         fastcgi_pass 127.0.0.1:9000;&lt;br/&gt;         fastcgi_param  SCRIPT_FILENAME  $document_root/$fastcgi_script_name;&lt;br/&gt;         include fastcgi_params;&lt;br/&gt; }&lt;br/&gt;&lt;p&gt;原本应该是还有一行fastcgi_index Default.aspx，用来设置默认页面; 但因为我这里是Host MVC站点，所以去掉这行配置。如果没有去掉这行，站点的根目录将会无法访问。 &lt;p&gt;4）部署MVC站点，除了去掉默认首页设置之外，还需要在conf目录下找到fastcgi_params这个文件，在文件最后加上下面两行配置：fastcgi_param  PATH_INFO          "";&lt;br/&gt;fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;&lt;br/&gt;&lt;p&gt;5）需要配置的部分都已经结束。下面就可以Mono FastCGI Server，这个命令行稍有差错，就会让我们的站点无法站点，结合了各方资料，我自己可以正常工作的命令行是：&lt;/p&gt;D:\Program Files\Mono-2.10\bin&amp;gt;fastcgi-mono-server4 /socket=tcp:127.0.0.1:9000 /root="E:\Mono\nginx-0.9.4\Mono_MVC3" /applications=/:. /multiplex=True /port=8080&lt;br/&gt;&lt;p&gt;6）最后一步，运行：nginx.exe。注意，只需要双击运行就可以了，双击就会在后台执行，不用在命令行下面运行。&lt;/p&gt;&lt;p&gt;以上就是Nginx+Mono在Windows下面最详细的配置步骤了。正常情况下，这时候我们只要访问&lt;a href="http://localhost:8080"&gt;Http://localhost:8080&lt;/a&gt;，站点应该就可以正常工作了。但是，因为Mono 2.10和MVC3，我还是遇到问题了：&lt;/p&gt;&lt;p&gt;1）每次FastCGI-Mono-Server第一次解析站点，都会出错，只要你刷新一下页面就可以正常访问了。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/hjf1223/201102/201102201525134187.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Nginx-exception" border="0" alt="Nginx-exception" src="http://images.cnblogs.com/cnblogs_com/hjf1223/201102/201102201525207986.png" width="1174" height="329"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;2）当我们重定向到登录页，输入用户名密码之后提交，又会遇到另外一个问题，Action无法被执行：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/hjf1223/201102/20110220152539574.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Nginx-exception2" border="0" alt="Nginx-exception2" src="http://images.cnblogs.com/cnblogs_com/hjf1223/201102/201102201525427278.png" width="1253" height="390"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;其实，在我最开始的尝试中（另一台机器），是不会抛出异常的，提交过程正常，但是会提示你没有输入用户名/密码，也就是LogOnModel的属性没有通过ModelBinder正确绑定。我目前也搞不清楚是不是在官方的下载中，提供了不同的编译版本。&lt;/p&gt;&lt;p&gt;在Windows下的部署，总算是有一个可以成功了，可是仍然还是存在运行不正确的问题。于是把目光投到Linux下面，看看Linux下面的部署会不会正确 ，毕竟Mono的主要部署场景还是在非Windows平台下。&lt;/p&gt;&lt;p&gt;后续：&lt;a href="http://www.cnblogs.com/hjf1223/archive/2011/02/20/mono_linux.html"&gt;ASP.NET MVC3 on Mono的折腾（二）：Linux(openSUSE)下的部署&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;script type="text/javascript"&gt;&lt;!--google_ad_client = "ca-pub-2436264902635851";/* MVC */google_ad_slot = "4856980617";google_ad_width = 728;google_ad_height = 90;//--&gt;&lt;/script&gt;&lt;script type="text/javascript"src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;&lt;/script&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hjf1223/aggbug/1959061.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hjf1223/archive/2011/02/20/mono_windows.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hjf1223/archive/2011/02/20/mono_linux.html</id><title type="text">ASP.NET MVC3 on Mono的折腾（二）：Linux(openSUSE)下的部署</title><summary type="text">续上篇介绍Mono在Window下的部署，对于我而言，相比于Windows平台下的部署，Linux平台下的部署挑战性会更大一点。从来没有Linux使用经验的我，要在Linux下部署Mono，遇到的不仅仅是Mono部署的问题，还有各种Linux操作的问题。我是使用的从Mono官方下载的安全配置好的虚拟机，安装方面的问题就可以暂时先跳过去，不过这里还是要先总结一下，先熟悉一下的几个常用的openSUSE操作。 1）配置网卡，进行文件管理都需要超级用户（su）密码，官方下载的虚拟机，su密码是:mono。刚开始不知道，还是小小的折腾了一会儿。其实，访问http://localhost 的默认首页就有</summary><published>2011-02-20T08:43:00Z</published><updated>2011-02-20T08:43:00Z</updated><author><name>阿不</name><uri>http://www.cnblogs.com/hjf1223/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hjf1223/archive/2011/02/20/mono_linux.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hjf1223/archive/2011/02/20/mono_linux.html"/><content type="html">&lt;p&gt;续&lt;a href="http://www.cnblogs.com/hjf1223/archive/2011/02/20/mono_windows.html" target="_blank"&gt;上篇&lt;/a&gt;介绍Mono在Window下的部署，对于我而言，相比于Windows平台下的部署，Linux平台下的部署挑战性会更大一点。从来没有Linux使用经验的我，要在Linux下部署Mono，遇到的不仅仅是Mono部署的问题，还有各种Linux操作的问题。我是使用的从Mono官方下载的安全配置好的虚拟机，安装方面的问题就可以暂时先跳过去，不过这里还是要先总结一下，先熟悉一下的几个常用的openSUSE操作。&lt;/p&gt; &lt;p&gt;1）配置网卡，进行文件管理都需要超级用户（su）密码，官方下载的虚拟机，su密码是:mono。刚开始不知道，还是小小的折腾了一会儿。其实，访问http://localhost 的默认首页就有这个用户密码的说明了。&lt;/p&gt; &lt;p&gt;2）apache的配置路径是在：/etc/apache2/。站点的默认目录是在：/srv/www/htdocs/，你会频繁在这两个目录做切换。&lt;/p&gt; &lt;p&gt;3）要修改配置你需要用超级用户的权限打开文件管理器，这个也是让我花了一些时间去查找。解决方案是创建一个命令行的快捷方式就可以了，看这个&lt;a href="http://forums.opensuse.org/english/get-technical-help-here/how-faq-forums/unreviewed-how-faq/426153-how-nautilus-super-user-mode-gnome.html" target="_blank"&gt;贴子&lt;/a&gt;。&lt;/p&gt; &lt;p&gt;4）重启apache的命令行：/etc/init.d/apache2 restart&lt;/p&gt; &lt;p&gt;5）如果需要，最好去扫盲一下什么是，YaST，什么是安装源。Linux下，动不动就要下源码编译安装，吓都吓死人了。使用的VPC是之前下载的，装的是Mono 2.8，昨天就是利用YaST自动升级到Mono 2.10。&lt;/p&gt; &lt;p&gt;事后总结，在Linux下部署Mono ASP.NET，主要是没有像IIS那样可视化的操作，都是命令行，看起来就是恐怖，但其实并没有的想像中的那么复杂。我们要先把官方的几个文档（&lt;a href="http://mono-project.com/Mod_mono" target="_blank"&gt;Mod_mono&lt;/a&gt;，&lt;a href="http://mono-project.com/AutoHosting" target="_blank"&gt;AutoHosting&lt;/a&gt;，&lt;a href="http://go-mono.com/config-mod-mono/" target="_blank"&gt;Apache mod_mono configuration tool&lt;/a&gt;）先熟悉一下，了解一下工作原理，再动手实施可能会少走一些弯路。另外一条教训就是，先部署低版本的，简单的，再尝试新版本刚支持的特性，比如先部署简单的ASP.NET WebForm页面（简单的一个页面），再部署MVC站点；先部署MVC1.0，再部署最新的MVC3.0，这样即使出错也更利于我们排错。刚开始直接折腾MVC3，那个杯具啊。&lt;/p&gt; &lt;p&gt;官方文档中介绍，在openSUSE+Apache有两种部署方式：自动Hosting和手工创建配置文件部署：&lt;/p&gt; &lt;p&gt;1.自动Hosting，只需做一次Apache配置，以后的部署Mono站点就只需要Xcopy到站点目录下就可以了，不需要再做额外部署。参考&lt;a href="http://mono-project.com/AutoHosting" target="_blank"&gt;AutoHosting&lt;/a&gt;，主要步骤：&lt;/p&gt; &lt;p&gt;1）在/etc/apache2/httpd.conf添加：&lt;strong&gt;Include /etc/apache2/mod_mono.conf&lt;/strong&gt; （这个文件在openSUSE 11.3的实际路径是&lt;strong&gt;Include /etc/apache2/conf.d/mod_mono.conf&lt;/strong&gt;），引入mod_mono的配置。&lt;/p&gt; &lt;p&gt;2）在mod_mono.conf文件中添加这一行：&lt;strong&gt;MonoAutoApplication enabled&lt;/strong&gt;。在官方文档中，还需要加入：MonoServerPath "/usr/bin/mod-mono-server2"，但实际上好像不需要。而且我改成：MonoServerPath "/usr/bin/mod-mono-server4"，它也不工作，不知道是什么问题。&lt;/p&gt; &lt;p&gt;3）如果是部署MVC站点，还需要在mod_mono.conf文件中加入这行：&lt;strong&gt;ForceType application/x-asp-net。&lt;/strong&gt;但是对MVC站点，不推荐使用自动Hosting。&lt;/p&gt; &lt;p&gt;当我做了以上配置之后，我们在部署ASP.NET站点时，就只需要把目录拷贝到/srv/www/htdocs/就可以完成部署了。文件目录对应的就是站点的虚拟目录。&lt;/p&gt; &lt;p&gt;2.手工创建部署文件，看完&lt;a href="http://www.cnblogs.com/shanyou" target="_blank"&gt;善友兄&lt;/a&gt;的这篇文章&lt;a href="http://www.cnblogs.com/shanyou/archive/2010/10/08/1846171.html"&gt;在Mono 2.8上部署ASP.NET MVC 2&lt;/a&gt;，我有点晕。那个配置文件就足以让我头痛，之前这篇文档&lt;a href="http://go-mono.com/config-mod-mono/" target="_blank"&gt;Apache mod_mono configuration tool&lt;/a&gt;一直都被我无视着。当我完整的看完几个官方文档之后，我才意识到这个工具的重要性。这个工具会根据我们希望部署的站点的情况，提供几个输入框让我们输入，之后我们就可以下载它生成的配置文件，直接将这些配置文件下载放到：&lt;em&gt;/etc/apache2/conf.d/&lt;/em&gt;&amp;nbsp; 而不需要再做其它配置站点就可以正常工作。&lt;/p&gt; &lt;p&gt;3.最土的部署办法，就是看看内置的那几个站点是如何部署的，直接把它的几个配置文件拷贝一下，修修改改也一样可以。在刚开始，我没有按正常流程来的时候，实在搞不清楚了，我用这种办法也一样把站点部署成功了。而且还是直接部署MVC3站点。&lt;/p&gt; &lt;p&gt;总结到这里，我发现在Linux下部署Mono ASP.NET也不是很难的事情嘛。只是一开始走的弯路有点多，心情有点急躁。不过，上篇中提到的MVC3的几个问题，在Linux下面也是同样存在的，应该可以判断是Mono2.10存在的bug吧，不过这bug也太过低级吧，难道他们没有测试提交的情况？如果是这样，希望能尽快解决这些bug。&lt;/p&gt;&lt;p&gt;&lt;script type="text/javascript"&gt;&lt;!--google_ad_client = "ca-pub-2436264902635851";/* MVC */google_ad_slot = "4856980617";google_ad_width = 728;google_ad_height = 90;//--&gt;&lt;/script&gt;&lt;script type="text/javascript"src="http://pagead2.googlesyndication.com/pagead/show_ads.js"&gt;&lt;/script&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hjf1223/aggbug/1959117.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hjf1223/archive/2011/02/20/mono_linux.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hjf1223/archive/2011/02/12/summary_2010.html</id><title type="text">迟到的2010总结</title><summary type="text">中国人的年，是以春节为准的。春节过后，才真的算是一年的结束，新的一年的开始。而今天已经是2011年2月12日了，现在看着2010的字眼，总是觉得那么遥远，第一感觉就像是过了好几年似的。今天原本是周末，不过按国家规定，今天还是一个工作日。不过前几天的忙碌结束以后，今天还算终于事情比较少一点。早上折腾了手机之后，下午也需要好好思考一下未来的工作安排了。 原本没想写这个2010年总结了，但是看着别人的总结，再看看自己过去曾经所写的总结。怎么看，虽然都是流水帐似的，但还是很有意义。通过这样的记录，至少能让我知道过去的一年都做了一些什么样的事情，在技术和认识上都有什么新的变化。几年下来，当然我们再将这些</summary><published>2011-02-12T08:26:00Z</published><updated>2011-02-12T08:26:00Z</updated><author><name>阿不</name><uri>http://www.cnblogs.com/hjf1223/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hjf1223/archive/2011/02/12/summary_2010.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hjf1223/archive/2011/02/12/summary_2010.html"/><content type="html">&lt;p&gt;中国人的年，是以春节为准的。春节过后，才真的算是一年的结束，新的一年的开始。而今天已经是2011年2月12日了，现在看着2010的字眼，总是觉得那么遥远，第一感觉就像是过了好几年似的。今天原本是周末，不过按国家规定，今天还是一个工作日。不过前几天的忙碌结束以后，今天还算终于事情比较少一点。早上折腾了手机之后，下午也需要好好思考一下未来的工作安排了。&lt;/p&gt; &lt;p&gt;原本没想写这个2010年总结了，但是看着别人的总结，再看看自己过去曾经所写的总结。怎么看，虽然都是流水帐似的，但还是很有意义。通过这样的记录，至少能让我知道过去的一年都做了一些什么样的事情，在技术和认识上都有什么新的变化。几年下来，当然我们再将这些总结放一起阅读时，就会别有一种味道。&lt;/p&gt; &lt;p&gt;2010年，工作内容没有任何的变化。自己的工作重心仍然是在Kooboo CMS这个开源项目之上。但是跟踪我们产品的很多人可能会误以为我们这个项目已经死掉了（估计也没什么人跟踪）。自从2010年4月之后，我们就没再发布任何新的版本。因为我们做了一个艰难的决定，我们决定完全放弃之前版本的所有代码和实现，在不改变技术架构和业务模型的基础之上重新开发。这也就等于宣布了之前版本的失败，之后很长的一段时间，我们中断了社区支持，全心投入到新版本的开发。&lt;/p&gt; &lt;p&gt;新版本的开发，并不是一帆风顺的，从4月份到2011年初我们才又发了一个预览版。其中很长的时间，我们是在做界面和用户体验开发。单从程序上的实现角度来讲，我们希望尽可能的简单和干净，很多可有，可没有的东西，我们都尽量去掉。而程序本身也会更的更小，在并发控制，内存占用，安全，多数据库支持，目录结构，API设计等很多方面我们都吸引了之前版本的教训。我们也在不断的改进代码和功能，希望这个版本能真正成为一个有竟争力的产品。&lt;/p&gt; &lt;p&gt;产品开发本身的很多事情，并不是我一个人的工作，也不方便在这里过多描述。接下来，我要总结一下我这过去一年的技术感受，我要用一个非常流行的技术词汇来描述我过去一年最大的收获：No-SQL。即使我过去一年所使用的No-SQL技术并非像大部分人谈到No-SQL就会联想到的Mongdb，Cassandra等no-sql数据库技术，但我还是要说，我得到了彻底抛弃SQL关系型数据库的理由，并且应用在项目当中也给我带来了不少的便利。特别是，当我们要处理关系型数据库和对象之间的关系映射时，映射能力再强的ORM框架也都很难无障碍的映射。而在我们很多现实的项目开发中，很多关系型数据库的严谨模型我们也并不是一定会完全利用到。更多的时候，我们只是利用了它“存储”的属性，其它的属性我们可能都不是特别需要。如果只是简单的储存，我们可选择的就太多了，最简单的，我们可以把一个对象序列化存储到一个文件当中。使用了这种存储之后，我们也就没有了跨数据库的问题。在Kooboo CMS当中，大部的站点元素对象都是使用这种方式进行存储的。再结合目录的树状结构，我们就能很轻松的完成多种树状关系的设计。&lt;/p&gt; &lt;p&gt;在Kooboo CMS当中，有一个非常重要的组成部分就是通用化内容存储（内容数据库）。这部分的设计以前是以关系型数据库为基础的。但其实，内容本身就没有特别多的关系，在我们的设计中，也无非就是类别和子内容的关系，除此之外，它就是一个标标准准的key-value文档。这个版本在理清了这些关系之后，也给我们的API设计带来了灵感。我们用一套类似LINQ表达式的语法来表达我们对数据查询的需要，比如：&lt;/p&gt; &lt;p&gt;1. 从一个目录查询所有内容： newsFolder.CreateQuery() ；&lt;/p&gt; &lt;p&gt;2. 从一个目录查询一条记录： newsFolder.CreateQuery().WhereEquals(“UUID”,uuid).First()；&lt;/p&gt; &lt;p&gt;3. 根据一个类别查询内容： newsFolder.CreateQuery().WhereCategory(categoryFolder.CreateQuery().WhereEquals(“UUID”,categoryUUID))；&lt;/p&gt; &lt;p&gt;由于我们的文档内容本身都不是设计时的强类型，因此我们无法使用LINQ的那一套。在我们设计的这套API下面，我们可以像LINQ的Provider那样，根据不同的数据库查询语法需要，将上面的表达式解析成各种数据库的查询语法。目前已经支持的数据库包括：SQLCe，SQLServer，MongoDB，RavenDB还有最原生的XML文件。&lt;/p&gt; &lt;p&gt;对于No-SQL，我们缺乏的不是技术去使用它，缺乏的是那种思维和变通。如果我们脑子里面总是带着那么强烈的模型关系优先体现的原则来设计的话，就很难接受这种零散和看似不严谨的实现方式。但其实，对于大部分的Web性质的应用来讲，我们很多时候并没有那么强的事务性。&lt;/p&gt; &lt;p&gt;在2010年，我放弃了大量的可用可不用，用着看起来比较先进也能带来方便的第三方框架。以前，我还用Unity，EntityFramework（由于不再使用关系型数据库的原因），Enterprise Library等框架。可是用到最后，我发现，除了带来大量的dll文件之后，其它的一点用处都没有（dll的数量是我们项目的几倍）。Unity，发布以后就基本不再做配置变动了，很多时候让用户自己修改配置也都不是很现实的事情。Enterprise Library的每一个block，我们真的没有办法认定哪一个对我们来说都不可或缺，而配置和版本问题又特别的麻烦。其它的第三方框架尤是如此。我现在的原则是，只有那种不可替换的框架我们才用，比如压缩/解压。&lt;/p&gt; &lt;p&gt;ASP.NET MVC在这一年中，也有不小的进步和完善。从MVC2开始，MVC框架的可用性提高太多了，ModelMetadata和基于Metadata之上的表单方案，验证框架彻底激活了MVC的生产效率。因为MVC3，我还完全改变了我之后对于脚本与HTML结合的传统思维，虽然jquery一直都是unobtrusive js的写法，但直到MVC3中内置的unobtrusive特性之后，我才更加深刻的认识到这种无侵入的脚本与HTML结合能给我们的客户端世界带来多大的改变，在思维上有一个全新的改变。&lt;/p&gt; &lt;p&gt;2010已经过去了很久很久，我所能记忆起来的东西又极其有限。我只能通过Kooboo CMS这个项目的变化主线来总结我的这一年。在这一年当中，我其实什么事也干，就只花在这个项目的上了。虽然这是我的工作，但是一年（其它好几年）都只做这个事，没有其它的成就，还是让我缺少那么一点的成就感。当然希望在已经到来的2011年里，能够将这个项目做的更好，成为一个社区能得到更多人接受的产品。也算是我对自己的一点点负责吧。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hjf1223/aggbug/1952594.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hjf1223/archive/2011/02/12/summary_2010.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hjf1223/archive/2011/01/15/weekend-thinking.html</id><title type="text">周末随想之移动随笔</title><summary type="text">手头没有电脑，只有一部低端的Android手机。而且有比较长的空闲时间，突发奇想用这个仅有的互联网设备来发一篇博文吧，也算赶一下潮流吧。话说最近的热点就是微博了，做为一名互联网的从业者，我也很早就有了twitter账户了，但是每天翻墙我也没那个体力，加上交友圈的问题，那并不是个适合我的场所。国内很早也有了类似的网站，但是由于外力的因素没有存活下来，否则说不定没有新浪微博什么事了。新浪的微博是目前中国最成功的微博社区（没有之一），即使是我现在使用的腾讯微博规模也不去他。个人总结中国模式的微博，一个是监管，另一个是名人效应。微博虽然在国内已经很火了，按中国网民的惯例，已经有一个专有名词来形容这么一</summary><published>2011-01-14T18:10:00Z</published><updated>2011-01-14T18:10:00Z</updated><author><name>阿不</name><uri>http://www.cnblogs.com/hjf1223/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hjf1223/archive/2011/01/15/weekend-thinking.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hjf1223/archive/2011/01/15/weekend-thinking.html"/><content type="html">&lt;p&gt;手头没有电脑，只有一部低端的Android手机。而且有比较长的空闲时间，突发奇想用这个仅有的互联网设备来发一篇博文吧，也算赶一下潮流吧。&lt;/p&gt;&lt;p&gt;话说最近的热点就是微博了，做为一名互联网的从业者，我也很早就有了twitter账户了，但是每天翻墙我也没那个体力，加上交友圈的问题，那并不是个适合我的场所。国内很早也有了类似的网站，但是由于外力的因素没有存活下来，否则说不定没有新浪微博什么事了。新浪的微博是目前中国最成功的微博社区（没有之一），即使是我现在使用的腾讯微博规模也不去他。个人总结中国模式的微博，一个是监管，另一个是名人效应。微博虽然在国内已经很火了，按中国网民的惯例，已经有一个专有名词来形容这么一群人，"微博控"，虽然李开复老师还专门写了一本关于微博的书，但微博还是有很大的空间。我身边的朋友（非计算机行业从业者）大部分都还没有微博账户，有的甚至不微博是哪知神马，在他们的习惯里（包括我）还是会在QQ空间里互相了解近况。&lt;/p&gt;&lt;p&gt;换个技术话题，编辑框太小，一个话题写太长容易内容断链。今天老赵的那篇博文，主题讲平台跟语言的关系，主要内容是在极力推广用.NET平台和基于这个平台的语言去开发可以夸平台的应用。其中包括最流行的移动平台应用。移动平台是未来的重要方向这点毋庸置疑，但是monoroid/monotuch是否能被广泛采用我也不是特别有信心，毕竟有.NET WINFORM的前例在这里。但是我也不会现在就去否定他们存在的意义，没有接触就没有发言权。不过对Mono本身我本人从没有任何怀疑过，我也在打算在未来的合适机会让我们的项目也完全支持在mono平台下运行。之前做过尝试，有一个好的开始，但程序本身还没成型就没继续。不过，我还得对他文中提到的现代程序员的桌面必备物品的要求表示一点点的抗议，你桌面上除了那个小的显示器和鼠标键盘，其他我一样都没有:(，就这样我就回到当代去了，有点不公平，不是我不想要那些东西，我是没能力拥有那些东西啊！&lt;/p&gt;&lt;p&gt;最后一个话题与时间相关。印象中上一次这么还在写博客已经是几年前的事情了。对于我来，写博客直接反映了我的技术学习情况。很多年前，每天半夜还精神抖擞的坐在电脑前写着各种现在看起来还稍显稚嫩的学习笔记和教程，但是那些却给自己带来了极大的帮助和积累。虽然那时候还看不懂很多开源项目的代码，到了解开源项目最多的却是那时候。我一直在想，锻炼学习能力最重要，但是我却忽略了学习能力提高了是不能代替我们每天花在技术上的时间，我们还是要花很多的时间，利用学习能力带来的高效率来学习以前花相同时间所学不到的知识。至于学什么，我自己有时候也迷茫，学很多平常可能用不到的东西，好像都不是特别深入，这个最重要的还是培养兴趣，但有些兴趣好像真的培养不来，比如我不喜欢玩游戏，不习惯看小说，就怎么也培养不起来。当然与工作相关的技术，如果我们愿意，一定还会有很多东西我们不懂的。&lt;/p&gt;&lt;p&gt;现在看到刚毕业的同事，同行们，真的特别嫉妒他们才刚刚毕业，刚刚起步，没有压力，没有顾忌。当然，能走到现在，我已经非常幸运了。我也希望以后能多坚持一点技术学习，没有优秀的教育背景，只希望能不落后太多吧。&lt;/p&gt;&lt;p&gt;该收笔了，要不手机该自动关机了。晚安&lt;/p&gt;&lt;p&gt;&lt;b style="color:red"&gt;手机发不出去，最后还是得打开电脑发送 :(&lt;/b&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hjf1223/aggbug/1936069.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hjf1223/archive/2011/01/15/weekend-thinking.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hjf1223/archive/2011/01/13/nvelocity-for-asp-net-mvc.html</id><title type="text">NVelocity for ASP.NET MVC</title><summary type="text">在我的这篇博文中，有这么一段话：“我一直在想，有没有办法可以单独限制View中的代码的访问权限，类似于trust level，只是这个trust level是用来限制模板中的代码。”。有读者johngeng问，为什么要用trust level来锁住view，他不是很理解。我的本意是，希望在view中，开发人员只能写某一些特定功能的代码，调用某一些特定开放的API，对于大部分安全级比较高的代码，比如读写文件等API或类库，不允许在view当中使用。这对于我们将模板开放出来，在线提供给我们的用户去修改的需求下是非常重要的。而目前，不管WebForm还是Razor，都是非常自由的模板，在View能做</summary><published>2011-01-13T15:37:00Z</published><updated>2011-01-13T15:37:00Z</updated><author><name>阿不</name><uri>http://www.cnblogs.com/hjf1223/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hjf1223/archive/2011/01/13/nvelocity-for-asp-net-mvc.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hjf1223/archive/2011/01/13/nvelocity-for-asp-net-mvc.html"/><content type="html">&lt;p&gt;在我的&lt;a href="http://www.cnblogs.com/hjf1223/archive/2010/12/16/MVC3-razor.html"&gt;这篇&lt;/a&gt;博文中，有这么一段话：“我一直在想，有没有办法可以单独限制View中的代码的访问权限，类似于trust level，只是这个trust level是用来限制模板中的代码。”。有读者&lt;a href="http://www.cnblogs.com/johngeng/"&gt;johngeng&lt;/a&gt;问，为什么要用trust level来锁住view，他不是很理解。我的本意是，希望在view中，开发人员只能写某一些特定功能的代码，调用某一些特定开放的API，对于大部分安全级比较高的代码，比如读写文件等API或类库，不允许在view当中使用。这对于我们将模板开放出来，在线提供给我们的用户去修改的需求下是非常重要的。而目前，不管WebForm还是Razor，都是非常自由的模板，在View能做的事情等同于Controller或其它地方所写的代码，这样View就不允许开放出来由用户在线修改。&lt;/p&gt; &lt;p&gt;在相同的博文里面，还是那位读者&lt;a href="http://www.cnblogs.com/johngeng/"&gt;johngeng&lt;/a&gt;提到它更喜欢$而不是@，由于我之前并不了解NVelocity，所以我误解为它是在说客户端开发包jquery。现在看来，他说的应该就是NVelocity，也许他觉得此人不可教，他并没有直接回复我的疑问，这也只能怪自己知识面太窄了。&lt;img style="border-bottom-style: none; border-right-style: none; border-top-style: none; border-left-style: none" class="wlEmoticon wlEmoticon-sadsmile" alt="悲伤" src="http://images.cnblogs.com/cnblogs_com/hjf1223/201101/201101132337043732.png"&gt;&lt;/p&gt; &lt;p&gt;若不是最近在为项目添加多模板引擎的支持，或许我永远也无法得到以上两个问题的答案，而这两个答案都与NVelocity有关。虽然我平常肯定也见过NVelocity这个词，但到要选择除WebForm以外的模板引擎，我还是完完全全没有记起他，还是同事&lt;a href="http://walkingboy.cnblogs.com/"&gt;@浪子&lt;/a&gt;提醒我NVelocity这个模板引擎值得一试。看了官方的语法介绍后，我不得不说它是一种非常简洁且实用的模板，同时又不失它的灵活性和安全性。我所指的灵活性是它不像StringTemplate那样，限制的那么死，连个对象的函数都不允许调用。安全性方面又可以满足我希望模板上限制开发人员只能在模板上调用指定的API。到目前为止，NVelocity仍然让我非常满意。&lt;/p&gt; &lt;p&gt;在ASP.NET MVC切换视图引擎非常简单，在ASP.NET MVC1.0出来以后，&lt;a href="http://mvccontrib.codeplex.com/"&gt;MvcContrib&lt;/a&gt;就曾经提供了多种视图引擎的切换选择，但是在最近的版本中，我却始终没有找到相关的代码，应该是这些代码已经被移出去了，但它的介绍文档中还没有删掉相关的主题。还好在&lt;a href="http://www.cnblogs.com/chsword"&gt;@重典&lt;/a&gt;童鞋的博客上找到了他从MvcContrib中提取出来的&lt;a href="http://www.cnblogs.com/chsword/archive/2008/08/21/dotnetmvc_nvelocityengine.html"&gt;实现&lt;/a&gt;。但是这个实现相对于MVC3来说，已经相对过时了，有些接口已经改变或被移除了，比如IViewLocator这个接口就已经不存在了。还有就是，它去掉了原先支持的调用HtmlHelper扩展方法的功能，而我最重要的就是要支持扩展函数，因为我自定义了一些必须的扩展方法。下面我们就来看看NVelocity for ASP.NET MVC几个类的详细情况：&lt;/p&gt; &lt;p&gt;&lt;strong&gt;NVelocityViewEngine&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;在之前的实现中，直接实现了IViewEngine这个接口，查找View的路径是通过实现IViewLocator来定位。在MVC2当中，修改了这部分的实现，MVC内部提供了VirtualPathProviderViewEngine这个模板方法类，在子类当中，我们中需要设置一下我们要查找的路径格式，其它的事件就可以交给模板方法类来完成，这样一方面可以简化我们的实现，另一方面还可以和默认的路径查找方式统一。&lt;/p&gt; &lt;p&gt;同时，由于我使用Nvelocity内置的相对文件路径的方式来查找模板，而使用VirtualPath的风格，因此在找到VirtualPath后，我们需要转换成实际的物理路径，直接通过物理路径来加载模板内容，而内置的FileResourceLoader并不支持从物理路径加载模板，所以我们还要额外实现一下FileResourceLoader，让支持从物理路径的加载方法。这两个类的代码如下：&lt;/p&gt;public class FileResourceLoaderEx : FileResourceLoader&lt;br/&gt;{&lt;br/&gt;    public FileResourceLoaderEx() : base() { }&lt;br/&gt;    private Stream FindTemplate(string filePath)&lt;br/&gt;    {&lt;br/&gt;        try&lt;br/&gt;        {&lt;br/&gt;            FileInfo file = new FileInfo(filePath);&lt;br/&gt;            return new BufferedStream(file.OpenRead());&lt;br/&gt;        }&lt;br/&gt;        catch (Exception exception)&lt;br/&gt;        {&lt;br/&gt;            base.runtimeServices.Debug(string.Format("FileResourceLoader : {0}", exception.Message));&lt;br/&gt;            return null;&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;    public override long GetLastModified(global::NVelocity.Runtime.Resource.Resource resource)&lt;br/&gt;    {&lt;br/&gt;        if (File.Exists(resource.Name))&lt;br/&gt;        {&lt;br/&gt;            FileInfo file = new FileInfo(resource.Name);&lt;br/&gt;            return file.LastWriteTime.Ticks;&lt;br/&gt;        }&lt;br/&gt;        return base.GetLastModified(resource);&lt;br/&gt;    }&lt;br/&gt;    public override Stream GetResourceStream(string templateName)&lt;br/&gt;    {&lt;br/&gt;        if (File.Exists(templateName))&lt;br/&gt;        {&lt;br/&gt;            return FindTemplate(templateName);&lt;br/&gt;        }&lt;br/&gt;        return base.GetResourceStream(templateName);&lt;br/&gt;    }&lt;br/&gt;    public override bool IsSourceModified(global::NVelocity.Runtime.Resource.Resource resource)&lt;br/&gt;    {&lt;br/&gt;        if (File.Exists(resource.Name))&lt;br/&gt;        {&lt;br/&gt;            FileInfo file = new FileInfo(resource.Name);&lt;br/&gt;            return (!file.Exists || (file.LastWriteTime.Ticks != resource.LastModified));&lt;br/&gt;        }&lt;br/&gt;        return base.IsSourceModified(resource);&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;public class NVelocityViewEngine : VirtualPathProviderViewEngine, IViewEngine&lt;br/&gt;{&lt;br/&gt;    public static NVelocityViewEngine Default = null;&lt;br/&gt;&lt;br/&gt;    private static readonly IDictionary DEFAULT_PROPERTIES = new Hashtable();&lt;br/&gt;    private readonly VelocityEngine _engine;&lt;br/&gt;&lt;br/&gt;    static NVelocityViewEngine()&lt;br/&gt;    {&lt;br/&gt;        string targetViewFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "views");&lt;br/&gt;        //DEFAULT_PROPERTIES.Add(RuntimeConstants.RESOURCE_LOADER, "file");&lt;br/&gt;        DEFAULT_PROPERTIES.Add(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, targetViewFolder);&lt;br/&gt;        DEFAULT_PROPERTIES.Add("file.resource.loader.class", "NVelocityEngine.FileResourceLoaderEx\\,NVelocityEngine");&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;        Default = new NVelocityViewEngine();&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public NVelocityViewEngine()&lt;br/&gt;        : this(DEFAULT_PROPERTIES)&lt;br/&gt;    {&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public NVelocityViewEngine(IDictionary properties)&lt;br/&gt;    {&lt;br/&gt;        base.MasterLocationFormats = new string[] { "~/Views/{1}/{0}.vm", "~/Views/Shared/{0}.vm" };&lt;br/&gt;        base.AreaMasterLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.vm", "~/Areas/{2}/Views/Shared/{0}.vm" };&lt;br/&gt;        base.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.vm", "~/Views/Shared/{0}.vm" };&lt;br/&gt;        base.AreaViewLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.vm", "~/Areas/{2}/Views/Shared/{0}.vm" };&lt;br/&gt;        base.PartialViewLocationFormats = base.ViewLocationFormats;&lt;br/&gt;        base.AreaPartialViewLocationFormats = base.AreaViewLocationFormats;&lt;br/&gt;        base.FileExtensions = new string[] { "vm" };&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;        if (properties == null) properties = DEFAULT_PROPERTIES;&lt;br/&gt;&lt;br/&gt;        ExtendedProperties props = new ExtendedProperties();&lt;br/&gt;        foreach (string key in properties.Keys)&lt;br/&gt;        {&lt;br/&gt;            props.AddProperty(key, properties[key]);&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        _engine = new VelocityEngine();&lt;br/&gt;        _engine.Init(props);&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)&lt;br/&gt;    {&lt;br/&gt;        Template viewTemplate = GetTemplate(viewPath);&lt;br/&gt;        Template masterTemplate = GetTemplate(masterPath);&lt;br/&gt;        NVelocityView view = new NVelocityView(controllerContext, viewTemplate, masterTemplate);&lt;br/&gt;        return view;&lt;br/&gt;    }&lt;br/&gt;    protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)&lt;br/&gt;    {&lt;br/&gt;        Template viewTemplate = GetTemplate(partialPath);&lt;br/&gt;        NVelocityView view = new NVelocityView(controllerContext, viewTemplate, null);&lt;br/&gt;        return view;&lt;br/&gt;    }&lt;br/&gt;    public Template GetTemplate(string viewPath)&lt;br/&gt;    {&lt;br/&gt;        if (string.IsNullOrEmpty(viewPath))&lt;br/&gt;        {&lt;br/&gt;            return null;&lt;br/&gt;        }&lt;br/&gt;        return _engine.GetTemplate(System.Web.Hosting.HostingEnvironment.MapPath(viewPath));&lt;br/&gt;    }&lt;br/&gt;    &lt;br/&gt;}&lt;br/&gt;&lt;p&gt;&lt;strong&gt;NVelocityView&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;主要实现IView接口，实现Render方法来将模板和当前的上下文结合之后输出出来。这个类还实现了，IViewDataContainer好像不是特别必要。NVelocity的Render也很简单，只是把所需要的对像塞到NVelocity执行的上下文当中，然后调用一下Merge方法就OK了。这里要特别说明的是，在NVelocity模板上面，我们可以调用上下文对象的中的任何属性和方法，但是没有办法调用到对象上的扩展方法，这时候，我们就需要借助NVelocity所提供的IDuck这个接口来提供扩展方法的支持，如下代码的：new HtmlExtensionDuck(context, this); 。完全代码如下：&lt;/p&gt;public class NVelocityView : IViewDataContainer, IView&lt;br/&gt;{&lt;br/&gt;    private ControllerContext _controllerContext;&lt;br/&gt;    private readonly Template _masterTemplate;&lt;br/&gt;    private readonly Template _viewTemplate;&lt;br/&gt;&lt;br/&gt;    public NVelocityView(ControllerContext controllerContext, string viewPath, string masterPath)&lt;br/&gt;        : this(controllerContext, NVelocityViewEngine.Default.GetTemplate(viewPath), NVelocityViewEngine.Default.GetTemplate(masterPath))&lt;br/&gt;    {&lt;br/&gt;&lt;br/&gt;    }&lt;br/&gt;    public NVelocityView(ControllerContext controllerContext, Template viewTemplate, Template masterTemplate)&lt;br/&gt;    {&lt;br/&gt;        _controllerContext = controllerContext;&lt;br/&gt;        _viewTemplate = viewTemplate;&lt;br/&gt;        _masterTemplate = masterTemplate;&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public Template ViewTemplate&lt;br/&gt;    {&lt;br/&gt;        get { return _viewTemplate; }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public Template MasterTemplate&lt;br/&gt;    {&lt;br/&gt;        get { return _masterTemplate; }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    private VelocityContext CreateContext(ViewContext context)&lt;br/&gt;    {&lt;br/&gt;        Hashtable entries = new Hashtable(StringComparer.InvariantCultureIgnoreCase);&lt;br/&gt;        if (context.ViewData != null)&lt;br/&gt;        {&lt;br/&gt;            foreach (var pair in context.ViewData)&lt;br/&gt;            {&lt;br/&gt;                entries[pair.Key] = pair.Value;&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;        entries["viewdata"] = context.ViewData;&lt;br/&gt;        entries["tempdata"] = context.TempData;&lt;br/&gt;        entries["routedata"] = context.RouteData;&lt;br/&gt;        entries["controller"] = context.Controller;&lt;br/&gt;        entries["httpcontext"] = context.HttpContext;&lt;br/&gt;        entries["viewbag"] = context.ViewData;&lt;br/&gt;        CreateAndAddHelpers(entries, context);&lt;br/&gt;&lt;br/&gt;        return new VelocityContext(entries);&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    private void CreateAndAddHelpers(Hashtable entries, ViewContext context)&lt;br/&gt;    {&lt;br/&gt;        entries["html"] = entries["htmlhelper"] = new HtmlExtensionDuck(context, this);&lt;br/&gt;        entries["url"] = entries["urlhelper"] = new UrlHelper(context.RequestContext);&lt;br/&gt;        entries["ajax"] = entries["ajaxhelper"] = new AjaxHelper(context, this);&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public void Render(ViewContext viewContext, TextWriter writer)&lt;br/&gt;    {&lt;br/&gt;        this.ViewData = viewContext.ViewData;&lt;br/&gt;&lt;br/&gt;        bool hasLayout = _masterTemplate != null;&lt;br/&gt;&lt;br/&gt;        VelocityContext context = CreateContext(viewContext);&lt;br/&gt;&lt;br/&gt;        if (hasLayout)&lt;br/&gt;        {&lt;br/&gt;            StringWriter sw = new StringWriter();&lt;br/&gt;            _viewTemplate.Merge(context, sw);&lt;br/&gt;&lt;br/&gt;            context.Put("childContent", sw.GetStringBuilder().ToString());&lt;br/&gt;&lt;br/&gt;            _masterTemplate.Merge(context, writer);&lt;br/&gt;        }&lt;br/&gt;        else&lt;br/&gt;        {&lt;br/&gt;            _viewTemplate.Merge(context, writer);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    private ViewDataDictionary _viewData;&lt;br/&gt;    public ViewDataDictionary ViewData&lt;br/&gt;    {&lt;br/&gt;        get&lt;br/&gt;        {&lt;br/&gt;            if (_viewData == null)&lt;br/&gt;            {&lt;br/&gt;                return _controllerContext.Controller.ViewData;&lt;br/&gt;            }&lt;br/&gt;            return _viewData;&lt;br/&gt;        }&lt;br/&gt;        set&lt;br/&gt;        {&lt;br/&gt;            _viewData = value;&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;&lt;p&gt;&lt;strong&gt;ExtensionDuck&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;ExtensionDuck就是对IDuck接口的实现，它是我们需要提供扩展方法支持的Duck对象的基类。所有需要接供扩展方法的对象，通过继承该方法可以简化大部分的工作：&lt;/p&gt;public class ExtensionDuck : IDuck&lt;br/&gt;{&lt;br/&gt;    private readonly object _instance;&lt;br/&gt;    private readonly Type _instanceType;&lt;br/&gt;    private readonly Type[] _extensionTypes;&lt;br/&gt;    private Introspector _introspector;&lt;br/&gt;&lt;br/&gt;    public ExtensionDuck(object instance)&lt;br/&gt;        : this(instance, Type.EmptyTypes)&lt;br/&gt;    {&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public ExtensionDuck(object instance, params Type[] extentionTypes)&lt;br/&gt;    {&lt;br/&gt;        if(instance == null) throw new ArgumentNullException("instance");&lt;br/&gt;&lt;br/&gt;        _instance = instance;&lt;br/&gt;        _instanceType = _instance.GetType();&lt;br/&gt;        _extensionTypes = extentionTypes;&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public Introspector Introspector&lt;br/&gt;    {&lt;br/&gt;        get&lt;br/&gt;        {&lt;br/&gt;            if(_introspector == null)&lt;br/&gt;            {&lt;br/&gt;                _introspector = RuntimeSingleton.Introspector;&lt;br/&gt;            }&lt;br/&gt;            return _introspector;&lt;br/&gt;        }&lt;br/&gt;        set { _introspector = value; }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public object GetInvoke(string propName)&lt;br/&gt;    {&lt;br/&gt;        throw new NotSupportedException();&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public void SetInvoke(string propName, object value)&lt;br/&gt;    {&lt;br/&gt;        throw new NotSupportedException();&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public object Invoke(string method, params object[] args)&lt;br/&gt;    {&lt;br/&gt;        if(string.IsNullOrEmpty(method)) return null;&lt;br/&gt;&lt;br/&gt;        MethodInfo methodInfo = Introspector.GetMethod(_instanceType, method, args);&lt;br/&gt;        if(methodInfo != null)&lt;br/&gt;        {&lt;br/&gt;            return methodInfo.Invoke(_instance, args);&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        object[] extensionArgs = new object[args.Length + 1];&lt;br/&gt;        extensionArgs[0] = _instance;&lt;br/&gt;        Array.Copy(args, 0, extensionArgs, 1, args.Length);&lt;br/&gt;&lt;br/&gt;        foreach(Type extensionType in _extensionTypes)&lt;br/&gt;        {&lt;br/&gt;            methodInfo = Introspector.GetMethod(extensionType, method, extensionArgs);&lt;br/&gt;            if(methodInfo != null)&lt;br/&gt;            {&lt;br/&gt;                return methodInfo.Invoke(null, extensionArgs);&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        return null;&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;&lt;p&gt;接下，我们就可以来实现一个HtmlExtensionDuck，指定一下，View中可以调用到HtmlHelper的哪些扩展方法，需要被开放的扩展方法可以在HTML_EXTENSION_TYPES中提供扩展方法所在的静态类名：&lt;/p&gt;public class HtmlExtensionDuck : ExtensionDuck&lt;br/&gt;{&lt;br/&gt;    public static readonly Type[] HTML_EXTENSION_TYPES =&lt;br/&gt;        new Type[]&lt;br/&gt;            {&lt;br/&gt;                typeof(DisplayExtensions),&lt;br/&gt;                typeof(DisplayTextExtensions),&lt;br/&gt;                typeof(EditorExtensions),&lt;br/&gt;                typeof(FormExtensions), &lt;br/&gt;                typeof(InputExtensions), &lt;br/&gt;                typeof(LabelExtensions),&lt;br/&gt;                typeof(LinkExtensions), &lt;br/&gt;                typeof(MvcForm),&lt;br/&gt;                typeof(PartialExtensions),&lt;br/&gt;                typeof(RenderPartialExtensions),&lt;br/&gt;                typeof(SelectExtensions),&lt;br/&gt;                typeof(TextAreaExtensions),&lt;br/&gt;                typeof(ValidationExtensions)&lt;br/&gt;            };&lt;br/&gt;&lt;br/&gt;    public HtmlExtensionDuck(ViewContext viewContext, IViewDataContainer container)&lt;br/&gt;        : this(new HtmlHelper(viewContext, container))&lt;br/&gt;    {&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public HtmlExtensionDuck(HtmlHelper htmlHelper)&lt;br/&gt;        : this(htmlHelper, HTML_EXTENSION_TYPES)&lt;br/&gt;    {&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public HtmlExtensionDuck(HtmlHelper htmlHelper, params Type[] extentionTypes)&lt;br/&gt;        : base(htmlHelper, extentionTypes)&lt;br/&gt;    {&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;完整的NVelocity for ASP.NET MVC的实现就是以上几个类就可以完成。然后，我们就可以直接注册到系统中来。我们不需要重写任何Conroller，我们直接把ViewEngine注册到MVC中来就可以被用到，也可以支持一个程序支持多种视图引擎共存的和谐场面。简单的注册代码放在Global.asax文件中：&lt;/p&gt;ViewEngines.Engines.Add(NVelocityViewEngine.Default);&lt;br/&gt;&lt;p&gt;详细的使用示例，&lt;a href="http://files.cnblogs.com/hjf1223/NVelocity_for_ASP.NET_MVC.zip"&gt;下载附件&lt;/a&gt;查看详细。&lt;/p&gt;&lt;p&gt;最后总结一下，NVelocity确实是一种简单实用的模板引擎，特别是它的语法非常简洁，而且API的可扩展性还是挺强的。Razor的语法的基本风格应该是有借鉴了它的语法风格。我现在虽然也很喜欢NVelocity，但如果不是特殊情况的特殊需要，在普通的ASP.NET MVC程序中，我还是会侧向于使用Razor。它更自由一点，由于是直接的C#编译支持，所以我们可以做任何的事情，这对于很多开发人员来说很重要。另一个不可忽视的就是它的IDE支持，特别是代码提示确实是让人相当的舒服。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hjf1223/aggbug/1935071.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hjf1223/archive/2011/01/13/nvelocity-for-asp-net-mvc.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/hjf1223/archive/2010/12/25/aspnet-4-extensionless-url.html</id><title type="text">ASP.NET 4.0在IIS6原生支持没有后缀名的URL请求</title><summary type="text">看到haack的这篇文章《ASP.NET MVC 3 Extensionless URLs on IIS 6》，我才知道在IIS6的环境下运行ASP.NET4.0程序，我们已经可以原生的支持没有后续名（扩展名）的URL请求，而不需要再做通配符映射，这对我们在IIS6部署ASP.NET MVC站点来说，相当的重要。 在以前，我们要让ASP.NET MVC程序可以正常工作在IIS6上面，要么在我们的程序路由中添加*.mvc（或其它任意后缀），并且在部署时添加isapi映射规则，把*.mvc映射到aspnet_isapi.dll，让他交由ASP.NET处理程序进行处理，但一般情况下，这种做法我们都不</summary><published>2010-12-25T15:36:00Z</published><updated>2010-12-25T15:36:00Z</updated><author><name>阿不</name><uri>http://www.cnblogs.com/hjf1223/</uri></author><link rel="alternate" href="http://www.cnblogs.com/hjf1223/archive/2010/12/25/aspnet-4-extensionless-url.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/hjf1223/archive/2010/12/25/aspnet-4-extensionless-url.html"/><content type="html">&lt;p&gt;看到&lt;a href="http://haacked.com/"&gt;haack&lt;/a&gt;的这篇文章《&lt;a href="http://haacked.com/archive/2010/12/22/asp-net-mvc-3-extensionless-urls-on-iis-6.aspx"&gt;ASP.NET MVC 3 Extensionless URLs on IIS 6&lt;/a&gt;》，我才知道在IIS6的环境下运行ASP.NET4.0程序，我们已经可以原生的支持没有后续名（扩展名）的URL请求，而不需要再做通配符映射，这对我们在IIS6部署ASP.NET MVC站点来说，相当的重要。&lt;/p&gt; &lt;p&gt;在以前，我们要让ASP.NET MVC程序可以正常工作在IIS6上面，要么在我们的程序路由中添加*.mvc（或其它任意后缀），并且在部署时添加isapi映射规则，把*.mvc映射到aspnet_isapi.dll，让他交由ASP.NET处理程序进行处理，但一般情况下，这种做法我们都不会采用，用户体验不好，至少到目前为止，我还没有见过类似.mvc的网站；要么，我们可以在部署时，添加通配符映射（wildcard mappings），映射到aspnet_isapi.dll，它的作用就是把所有的URL请求都交由ASP.NET程序处理，但是这样做的使得很多原本不需要经过ASP.NET处理的请求，比如，样式，脚本，图片等静态资源都需要经过ASP.NET通道处理一遍，后果是可能会带来一定的性能影响。虽不完美，但大多数人还是会选择这种方案。&lt;/p&gt; &lt;p&gt;现在如果我们的ASP.NET站点基于ASP.NET 4.0，我们就不需要再做类似的事情了，因为ASP.NET4.0在安装的过程中，已经在IIS6做了一些手脚，让它可以原生的支持的无后缀的URL请求，那么它究竟是做了什么事情呢？在实现这个功能的作者&lt;a href="http://blogs.msdn.com/b/tmarq/"&gt;Thomas Marquardt&lt;/a&gt;的这篇&lt;a href="http://blogs.msdn.com/b/tmarq/archive/2010/06/18/how-to-disable-the-asp-net-v4-0-extensionless-url-feature-on-iis-6-0.aspx"&gt;博客&lt;/a&gt;上的一段话解释了它的工作原理，大概是这样的：&lt;/p&gt; &lt;p&gt;&lt;strong&gt;ASP.NET 4.0在安装的时候，会在IIS6注册一个ISAPI Filter，叫做”aspnet_filter.dll”，ISAPI Filter会先于ISAPI处理程序前执行，它会在所有的的无后缀的URL后面加一串字符“/eurl.axd/GUID”， 同时ASP.NET 4.0还会在IIS默认添加一个请求映射规则“*.axd”，映射到aspnet_isapi.dll。此时，所有的无后缀URL加上“/eurl.axd/GUID”后都会变成带.axd后缀，这样就匹配*.axd的映射规则进行ASP.NET的处理通道。在进入ASP.NET通道后，ASP.NET处理程序会删除掉“/eurl.axd/GUID”，让它还原到无后缀的原始情况，并且不会对后续的请求处理带来任何影响。此时，所有的无后缀请求，就进入了ASP.NET的处理通道中，在默认情况下，ASP.NET4.0的全局的web.config中配置了DefaultHttpHandler来接收无后缀的URL请求，但是我们也可以随意更换默认处理程序（比如ASP.NET MVC处理程序）来处理无后缀的URL请求。&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;从上面的解释中， 我们不难看到，这与IIS7的集成模式有本质的不同，它并不是很原生的处理方案。不得不说，这并不是一个非常流畅的解决方案，也许这也是不得以之下的非常之举。但是其实在没有ASP.NET MVC以前，对无后缀的URL请求的支持并不是那么迫切需要。即使有需要，也是通过一个URL Rewriter组件，而因为首先要进入ASP.NET 处理通道，所以这种URL Rewriter组件也需要ISAPI级别的扩展。当我们在ASP.NET 4.0的程序中，需要用类似的组件的时候，可能就会需跟上面的功能有所冲突，此时，我们可能就会需要禁用ASP.NET4.0对无后缀URL请求的这个功能。这时候你可能修改注册表相关键值（并重启IIS），或者是删除aspnet_filter.dll的注册（因为在这步增加了/eurl.axd/GUID）。&lt;/p&gt; &lt;p&gt;在&lt;a href="http://haacked.com/"&gt;haack&lt;/a&gt;的&lt;a href="http://haacked.com/archive/2010/12/22/asp-net-mvc-3-extensionless-urls-on-iis-6.aspx"&gt;文章&lt;/a&gt;最后还提到，他想要去确认这种行为的时候，出现了一点小问题，而它只是做了几步就可以很简单的修复这些问题。可能是在安装ASP.NET 4.0的时候，某些IIS注册行为没有被正确执行才导致的这些错误的发生。但我自己目前还没有IIS6的环境来验证，但我相信，这个功能对于我们希望在IIS6部署ASP.NET MVC站点来说是非常有意义的。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/hjf1223/aggbug/1917001.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/hjf1223/archive/2010/12/25/aspnet-4-extensionless-url.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
