<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_Teddy's Knowledge Base</title><subtitle type="text"/><id>http://feed.cnblogs.com/blog/u/8624/rss</id><updated>2010-08-08T06:37:55Z</updated><author><name>Teddy's Knowledge Base</name><uri>http://www.cnblogs.com/teddyma/</uri></author><generator>CNBlogs BlogServer</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/teddyma/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/8624/rss"/><entry><id>http://www.cnblogs.com/teddyma/archive/2010/08/08/1795135.html</id><title type="text">讨论：WCF可序列化的ActiveRecord实现有意义吗？</title><summary type="text">本文的目的，是探讨WCF可序列化的ActiveRecord实现是否有意义。</summary><published>2010-08-08T06:37:00Z</published><updated>2010-08-08T06:37:00Z</updated><author><name>Teddy's Knowledge Base</name><uri>http://www.cnblogs.com/teddyma/</uri></author><link rel="alternate" href="http://www.cnblogs.com/teddyma/archive/2010/08/08/1795135.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/teddyma/archive/2010/08/08/1795135.html"/><content type="html">&lt;p&gt;本文的目的，是探讨WCF可序列化的ActiveRecord实现是否有意义。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;ActiveRecord模式简介&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;ActiveRecord模式是Martin Fowler的企业应用架构模式中提到的一种数据访问模式，如下图：&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/teddyma/WindowsLiveWriter/WCFActiveRecord_104D4/image_2.png"&gt;&lt;img style="border-width: 0px; display: inline;" title="image" alt="image" src="http://images.cnblogs.com/cnblogs_com/teddyma/WindowsLiveWriter/WCFActiveRecord_104D4/image_thumb.png" width="244" border="0" height="162" /&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;Person类包含lastName, firstName等这些属性，一个Person类的实例，对应数据库的一个Person表或视图的一条记录，每一个属性，对应数据库记录的一个字段；同时，Person类包含CRUD方法，封装了对数据库的操作；Person类同时还可以包含其他和这条记录相关的业务方法。&lt;/p&gt;&lt;p&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://martinfowler.com/eaaCatalog/activeRecord.html" target="_blank"&gt;ActiveRecord&lt;/a&gt;模式和&lt;a href="http://martinfowler.com/eaaCatalog/repository.html" target="_blank"&gt;Repository&lt;/a&gt;模式，从外部接口上看起来在很多方面很像，但理念其实有区别：&lt;/p&gt; &lt;ol&gt; &lt;li&gt;ActiveRecord和数据库访问紧耦合，它更像是对数据库中的一条记录的包装类；Repository则隔离了具体的数据访问和领域对象的CRUD的耦合，这样一层隔离的存在，使得Repository更灵活，他可以还是像ActiveRecord一样包装一条数据库记录，却不限于数据库，数据也可以保存在任何地方，只要外部接口不变，其内部实现有更灵活性。  &lt;/li&gt;&lt;li&gt;ActiveRecord的属性，一般应该完全对应于数据库的字段；而Repository的属性，则没有这样的限制。  &lt;/li&gt;&lt;li&gt;ActiveRecord对数据库访问的紧耦合，也不完全是缺点，紧耦合的好处是，ActiveRecord上的方法的参数，也可以是和数据库紧耦合的，例如：它的CRUD方法的参数，可以包含数据库特定的信息，如字段信息，或者数据库特有的查询条件，面向数据库的查询条件，从数据库的视角当然更直观；而Repository在外部接口上，原则上是不能有任何数据库特定的信息的，如果最终还是要查数据库，还需要额外把非数据库特定的查询条件转换为数据库特定的查询条件，对于参数复杂的查询，处理查询条件的成本要高得多。&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;&lt;strong&gt;什么是WCF可序列化的ActiveRecord？&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;这里的&amp;#8220;WCF可序列化&amp;#8221;指的是，一个类可以被支持WCF DataContract序列化的类进行序列化。这里，&amp;#8220;WCF可序列化的ActiveRecord&amp;#8221;指的是，不仅仅ActiveRecord可序列化，还包括服务于ActiveRecord的查询条件也可以被序列化。ActiveRecord及其查询条件可以被WCF序列化就意味着，他们可以通过WCF服务进行远程通信，换句话说，他们可以作为DTO对象。&lt;/p&gt;&lt;p&gt;&lt;/p&gt; &lt;p&gt;在有关ActiveRecord的讨论中，经常会被提到，ActiveRecord不能用作DTO，因为其内部包装着数据库访问，尤其不能跨进程边界传递ActiveRecord实例。这种说法当然没错，不过他有一个前提，就是如果ActiveRecord实例连接着数据库，通过ActiveRecord上的CRUD可以直接访问数据库的话。换句话说，如果可以解除ActiveRecord实例对数据库的连接（例如：可以提供Attach(db)/Detach()方法将一个ActiveRecord实例绑定或解绑具体的数据库连接），那么，把它作为DTO我觉得未尝不可。&lt;/p&gt;&lt;p&gt;&lt;/p&gt; &lt;p&gt;让数据库查询条件作为DTO会不会有问题呢？在传统的程序开发中，一般我们说传递&amp;#8220;数据库查询条件&amp;#8221;往往指的是传递SQL片段，虽然传递SQL使得查询条件非常灵活，却毫无疑问是不安全的。但是，如果是对SQL语义的查询条件进行强类型的包装，则可以做到没有这样的安全隐患，却又可以保留查询条件灵活的特性。&lt;/p&gt;&lt;p&gt;&lt;/p&gt; &lt;p&gt;如果从安全性的角度，能够接受ActiveRecord及包含数据库查询语义的强类型的ActiveRecord的查询条件作为WCF服务的DTO进行传递，那么，我们就可以再来谈谈由此带来的好处了。以下，假设我们可以用一个强类型的Criteria对象来表示一个任意的数据库语义的查询条件，并且它可序列化，可以作为DTO进行传递。&lt;/p&gt; &lt;ol&gt; &lt;li&gt;简化CRUD和WCF服务接口。相对于Repository只能使用一个或多个非数据库语义的参数来描述查询条件，如果我们可以用一个Criteria对象来表示一个任意的数据库语义的查询条件，我们就只需要一个Find(Criteria criteria)方法，作为所有的查询条件的入口。则无论对于CRUD本身的API还是WCF服务的API，都得到了极大的简化，而描述能力却大大提升。  &lt;/li&gt;&lt;li&gt;简化数据库查询语句的构造和执行。在数据层的数据库查询的执行端。由于我们的Criteria是基于数据库语义的强类型封装，翻译成SQL的成本，相对于Repository只能使用一个或多个非数据库语义的参数来描述的查询条件到SQL的翻译，成本要低得多，语义上也直接得多。&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;需要注意的问题：&lt;/p&gt; &lt;ol&gt; &lt;li&gt;如何限制客户端的查询能力？由于采用这样一种方案的ActiveRecord使得客户端拥有了最大化的自定义查询能力，我们一定需要有一些机制，可以限制这种能力：在Find(Criteria criteria)的实现端，在执行查询之前，需要根据一定的规则，验证criteria是否允许执行；在客户端的查询对象上，我们也可以有一些限制，比如，只允许某些字段作为查询条件，或者，有些ActiveRecord是只读的，不允许写操作。  &lt;/li&gt;&lt;li&gt;以数据库的思维来构造查询条件。即使在允许的查询条件范围之内，由于不同的查询组合条件潜在地会导致不同语义的数据库查询被执行，不同的语义可能会导致不同的性能差异，因此，在客户端构造查询条件的时候，更大程度上，我们需要以数据库的思维来构造，而不仅仅是以业务的思维。&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;有人可能要问，客户端代码需要以数据库的思维来写，或者可能影响到数据层的执行效率，这样的设计是合理的吗？我觉得要看情况。&lt;/p&gt;&lt;p&gt;&lt;/p&gt; &lt;p&gt;首先，ActiveRecord是允许和数据库紧耦合的，从这个角度，ActiveRecord的客户端本身就是允许通过ActiveRecord的外部接口，接触到数据库特定语义的。从这个角度来说，给予客户端一定的构造查询条件的自由度，至少是说得过去的。&lt;/p&gt;&lt;p&gt;&lt;/p&gt; &lt;p&gt;其次，所谓ActiveRecord的客户端，对于不同的项目来说，对性能的要求可能有很大差异。比如，如果是一个公司内部项目，客户端的并发，整体的数据量也不是很大，则更多关注的可能是灵活性，那么，可以给客户端尽可能多的自由度；而如果是一个开放的WCF服务，或者并发或数据量很大，那么，可能应该尽可能的减少客户端的灵活性，Find(Criteria criteria)这样过于灵活的接口，也可能是不合适。&lt;/p&gt;&lt;p&gt;&lt;/p&gt; &lt;p&gt;欢迎讨论！&lt;/p&gt;&lt;img src="http://www.cnblogs.com/teddyma/aggbug/1795135.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/teddyma/archive/2010/08/08/1795135.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/teddyma/archive/2010/08/02/1790748.html</id><title type="text">理解Design Pattern</title><summary type="text">本文的目的是以最精炼的语言，理解什么是Design Pattern，为什么需要Design Pattern，和关于Design Pattern的争论。</summary><published>2010-08-02T12:23:00Z</published><updated>2010-08-02T12:23:00Z</updated><author><name>Teddy's Knowledge Base</name><uri>http://www.cnblogs.com/teddyma/</uri></author><link rel="alternate" href="http://www.cnblogs.com/teddyma/archive/2010/08/02/1790748.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/teddyma/archive/2010/08/02/1790748.html"/><content type="html">&lt;p&gt;本文的目的是以最精炼的语言，理解什么是Design Pattern，为什么需要Design Pattern，和关于Design Pattern的争论。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;什么是Design Pattern？&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;从字面上理解，Pattern，指的是解决常见问题的可重用的方案。Design，则限定了，这里的Pattern是为软件的设计服务的。因此，所谓Design Pattern，指的是解决常见的软件设计问题的可重用的方案，或者说解决方案的模板。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;两点需要特别注意的是：&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
    &lt;li&gt;算法不是Design Pattern。&lt;/li&gt;&#xD;
    &lt;li&gt;Design Pattern是为Design服务的，而不是为具体的实现服务的。 &lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;说道什么是Design Pattern，不得不提起一本书：《&lt;a href="http://www.amazon.com/exec/obidos/ASIN/0201633612/"&gt;Design Patterns: Elements of Reusable Object-Oriented Software&lt;/a&gt;》，俗称Gang of Four或者是GoF。为什么？因为这本书太有名了，有名到太多人从听说到自以为熟悉Design Pattern，都是通过它，甚至狭义地认为所谓Design Pattern，就是指的GoF中的23种Pattern。这真是一个相当大的误会了。同样的，很多人提到Design Pattern，必提到SOLID原则，其实也是误会，&lt;a href="http://en.wikipedia.org/wiki/Solid_(object-oriented_design"&gt;SOLID&lt;/a&gt;是面向对象设计的基本原则，并非Design Pattern的基本原则，只是因为GoF是面向对象的Design Pattern，所以GoF中的Pattern自然需要遵守面向对象的&lt;a href="http://en.wikipedia.org/wiki/Solid_(object-oriented_design"&gt;SOLID&lt;/a&gt;原则。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;针对不同的软件开发领域，其实有许多前人总结的Pattern。例如：GoF其实属于典型的面向对象Design Pattern，是前人从面向对象的设计实践中，总结的一些典型的Pattern。另外，还有&lt;a href="http://martinfowler.com/eaaCatalog"&gt;针对架构设计的Pattern&lt;/a&gt;，&lt;a href="http://www.enterpriseintegrationpatterns.com/"&gt;针对企业集成的Pattern&lt;/a&gt;，&lt;a href="http://www.soapatterns.org/"&gt;针对SOA的Pattern&lt;/a&gt;，等等。大大牛Martin Fowler维护了一个他个人感兴趣的不同开发领域的Deisng Pattern的&lt;a href="http://martinfowler.com/articles/enterprisePatterns.html"&gt;Catalogs&lt;/a&gt;。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;Pattern只是针对不同的场景的一些可重用的方案，因此，无需崇拜甚至神话任何已有的Pattern。一但一个Pattern服务的问题场景发生了变化，则往往可能导致这个Pattern不再适用此场景，或者需要被修正。任何人也都可以发现和总结新的Pattern。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;为什么需要Design Pattern？&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;应用Design Pattern主要的好处如下：&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
    &lt;li&gt;降低设计成本 - 每一个Design Pattern是解决一个常见问题设计问题最佳实践，因此，对于相同的问题，我们就无需做重复的设计，只需要重用即可。&lt;/li&gt;&#xD;
    &lt;li&gt;降低设计风险 - 相对于未经实现证明的朴素的设计，Design Pattern作为来源于实践，并且已经被前人证明的最佳实践，无疑风险更小。&lt;/li&gt;&#xD;
    &lt;li&gt;降低讨论和交流的成本 - 对于软件设计专家之间对设计的交流，如果大家都理解同一个Design Pattern，则无需提供太多的设计细节，就能让对方理解一个设计，因此交流的成本大大降低。&lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;strong&gt;关于Design Pattern的争论&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Design Pattern是否影响性能？&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;一定程度上，可以说&amp;#8220;是&amp;#8221;。相对于直接、朴素的实现，Design Pattern为了获得一定的灵活性，往往会引入一些额外的层次，对于局部来说，客观上的确是占用更多的资源，或者影响一些性能。&lt;/p&gt;&#xD;
&lt;p&gt;但是，我们不能孤立的看问题。撇开那些有极至性能要求的应用，Design Pattern在局部引入的一点点性能损失，往往并非是不可接受的，且还要考虑到其带来的其他好处；而一个错误的或者不够灵活的设计更有可能造成程序整体的缺陷和瓶颈，并且往往补救的成本要高得多。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Design Pattern是否为了弥补具体语言描述能力的不足？&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;判断这个命题，要看这里的&amp;#8220;Design Pattern&amp;#8221;是泛指，还是特指。如果是特指，Design Pattern既然只是指一些可重用的方案模板，针对，一些本身就是来源于特定语言的编程实践来说，当然，未尝不能使这个命题成立。例如：&lt;a href="http://www.corej2eepatterns.com"&gt;Core J2EE Patterns&lt;/a&gt;。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;但是，对于更多的并非来源于具体语言的的Design Pattern，如GoF这样的面向对象设计的Design Pattern，它关注的是面向对象设计的基本原则和限制，和具体的语言没有任何关系，又何来&amp;#8220;弥补具体语言描述能力的不足&amp;#8221;之说呢？&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/teddyma/aggbug/1790748.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/teddyma/archive/2010/08/02/1790748.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/teddyma/archive/2010/08/01/1789714.html</id><title type="text">理解O/R Mapping</title><summary type="text">本文的目的是以最精炼的语言，理解什么是O/R Mapping，为什么要O/R Mapping，和如何进行O/R Mapping。</summary><published>2010-07-31T16:22:00Z</published><updated>2010-07-31T16:22:00Z</updated><author><name>Teddy's Knowledge Base</name><uri>http://www.cnblogs.com/teddyma/</uri></author><link rel="alternate" href="http://www.cnblogs.com/teddyma/archive/2010/08/01/1789714.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/teddyma/archive/2010/08/01/1789714.html"/><content type="html">&lt;p&gt;本文的目的是以最精炼的语言，理解什么是O/R Mapping，为什么要O/R Mapping，和如何进行O/R Mapping。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;什么是O/R Mapping？&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;广义上，ORM指的是面向对象的对象模型和关系型数据库的数据结构之间的相互转换。&lt;/p&gt;&#xD;
&lt;p&gt;狭义上，ORM可以被认为是，基于关系型数据库的数据存储，实现一个虚拟的面向对象的数据访问接口。理想情况下，基于这样一个面向对象的接口，持久化一个OO对象应该不需要要了解任何关系型数据库存储数据的实现细节。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;为什么需要O/R Mapping？&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;广义上，因为我们需要面向对象来描述我们的业务，我们也需要关系型数据库来存储我们的数据。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;em&gt;有人可能会提到，我们未必要用面向对象来描述业务，或者未必用关系型数据库来存储数据。没错，但是，至少，还是有相当大部分的程序，同时需要这两者的合作。存在即合理。因为同时需要彼此，两者都客观存在，就值得讨论。&lt;/em&gt;&lt;/p&gt;&#xD;
&lt;p&gt;既然从广义上，存在即合理，无需讨论为什么需要ORM，很多关于ORM的讨论，其实都是针对上面提到的狭义的定义。但是即使到目前为止，真正能够完美的实现这个狭义定义的ORM的工具，其实还并不存在(很多工具如Hibernate，已经相当接近，但是，离完美还有相当距离)。&lt;/p&gt;&#xD;
&lt;p&gt;既然还不存在，那么，在讨论需不需要之前，我们恐怕要先讨论一下，是否可能，即从理论上，从数学上，面向对象的对象模型和关系型数据库的数据结构之间，到底能不能做到完美的映射？要回答这个问题，我们要解决两个难题。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;O/R阻抗失衡&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;第一个难题，叫&amp;#8220;O/R阻抗失衡(O/R Impedance Mismatch)&amp;#8221;，指的是OO对象模型和关系型数据库的数据结构之间的，设计理念上的差异。OO的设计理念，是以最正确的语义来描述真实世界；而关系型数据库的设计理念，则是从数学的角度，如何更有效的存储和管理数据。由于两者设计理念的差异，导致它们尽管从数据结构上，可能很相近，但是关注点往往是不同的。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;例如：&lt;/p&gt;&#xD;
&lt;ul&gt;&lt;li&gt;从OO的角度，凡是语义明确，每一个对象语义上的属性，就应该定义为一个属性；从关系型数据库的角度，有可能考虑到一些属性从来不会被作为查询条件，而把多个语义上的属性，以一定的格式，存在一个数据表的字段中，也有可能因为一组语义上的属性使用的频度完全不同与另一组属性，即使他们语义上属于一个对象，也有可能将他们拆分成两个数据表来存储。&lt;/li&gt;&lt;li&gt;从OO的角度，对象只关心自己的固有属性，不需要被唯一标识；但是，从关系型数据库的角度，一般一个数据表中的每一行数据都需要要一个唯一标识，且很可能是语义上没有意义的，如自增长的标识。&lt;/li&gt;&lt;li&gt;从OO角度的优化，一般遵循&lt;a href="http://en.wikipedia.org/wiki/Solid_(object-oriented_design)"&gt;SOLID&lt;/a&gt;这样的原则，以更正确的语义来组织对象；从关系型数据库角度的优化，往往为了查询性能，来修改字段的类型、长度，修改索引，甚至分表、分库。&lt;/li&gt;&lt;li&gt;... &lt;/li&gt;&lt;/ul&gt;&#xD;
&lt;p&gt;一个ORM工具想要通过简单的配置，完美的解决&amp;#8220;O/R阻抗失衡&amp;#8221;问题，在我看来几乎是不可能的。但是，一定程度上，通过灵活的配置支持绝大多数常见的映射策略，再加上提供可供用户通过自定代码来扩展的接口的话，应该还是可以相当接近完美的。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;文化阻抗失衡&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;第二个难题，叫&amp;#8220;文化阻抗失衡(Cultural Impedance Mismatch)&amp;#8221;，指的是关系型数据专家和面向对象专家之间的文化差异。关于这个难题的最经典的争论是&amp;#8220;到底应该以关系型数据库的数据结构来驱动，还是以OO的对象模型来驱动程序的开发？&amp;#8221;。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;关于这个争论，OO专家的主要观点是：&lt;br /&gt;我的业务才是程序的核心，数据库只是为我持久化数据的需求服务的，所以设计OO对象模型的时候，我不应该考虑如何存储到关系型数据库的问题；&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;而数据库专家的主要观点是:&lt;br /&gt;数据才是公司的核心财富，数据的稳定性远远强于OO对象模型的稳定性，由OO对像模型来驱动数据库架构的设计，根本保证不了性能，数据库维护的成本根本不可接受。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;其实，争论的真正原因即在于&amp;#8220;关系型数据专家和面向对象专家之间的文化差异&amp;#8221;。大大牛Scott W. Ambler的文章&lt;a href="http://www.agiledata.org/essays/drivingForces.html"&gt;Why Data Models Shouldn't Drive Object Models(And Vice Versa)&lt;/a&gt;，很好的解答了这个问题。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;归根结底，既然OO和关系型数据库都是不可缺少的部分，需要协同工作，希望做到完美的ORM，不仅仅需要好的ORM工具，更需要无论是OO专家还是数据库专家互相了解对方的技术和设计理念。对一个OO专家，一个ORM工具，可以对其所支持的常见的映射，提供直接的支持，但是，应用这些ORM工具的OO专家，必须了解关系型数据库，知道如何在不影响OO对象的语义的前提下，如何设计一个更容易映射到关系型数据库的对象模型，知道如何选择ORM工具所支持的映射方式，以及如何用自定代码扩展ORM所不能方便支持的映射方式；同样的，对一个关系型数据库的专家，也需要了解OO的语义，和可能发生的重构，从而，使得所设计的数据库结构，更容易映射到OO对象，更容易响应OO对象模型的重构。如此才是真正的和谐，真正的完美。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;小结&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;综上所述，无论对一个OO专家还是对一个关系型数据库专家，都需要了解OO，并了解关系型数据库，了解ORM的基本原理。离开全面的知识，一个再完美的ORM工具也无法被正确使用，拥有这些知识，则能够充分利用已有的ORM工具来加速自己的工作，并且合理的或扩展现有的ORM工具，或用自定代码，实现ORM工具能力之外的ORM映射。这才是理想的ORM实践。下一章节，我们就来谈谈一下，ORM的基本原理。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;如何进行O/R Mapping？&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;简单映射&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;1. Class &amp;lt;-&amp;gt; Table&lt;/p&gt;&#xD;
&lt;p&gt;一个Class一般可以映射为一个Table，一个Class的实例对应Table的一行数据。但是，一个Table中的每行数据，一般都需要有一个主键来唯一标识这行数据，而一个Class的每个实例，则不一定需要一个唯一标识。&lt;/p&gt;&#xD;
&lt;p&gt;2. Property &amp;lt;-&amp;gt; Field&lt;/p&gt;&#xD;
&lt;p&gt;一个Class的Property一般可以直接映射为Table的一个Field。但是，他们的数据类型不一定直接匹配。如果他们代表的数据类型的语义上可转换，则Field的类型，应大于等于Property的数据类型。如果他们代表的类型语义上不可转换，则需要在应用程序层面，进行自定义的转换。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;继承映射&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;1. 单表映射整个继承体系&lt;/p&gt;&#xD;
&lt;p&gt;用一张数据库表存储整个继承体系中的所有Class的数据，数据表需要额外的标志字段来区分一行记录应该映射到继承体系中的哪一个Class，适合继承体系层次较少，总记录数相对较少，子类对父类的属性扩展也相对不那么频繁的情形。&lt;/p&gt;&#xD;
&lt;p&gt;单表映射整个继承体系的优点是读/写继承体系中的每个Class的数据，都只需操作一张表，性能较好，并且，新增继承类，或扩展Class属性都只需要增减一张表的字段就可以了，易于维护；主要缺点是，因为继承体系中所有的Class共享一张表，表中会有比较多的NULL字段值的数据，浪费了一些存储空间，同时，如果记录数过多，表就会更庞大，也会影响表的读写性能。 &lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;2. 一个Class映射一个具体表&lt;/p&gt;&#xD;
&lt;p&gt;所谓一个Class映射一个具体表就是每个Class对应一张数据表，并且，每个数据表冗余包含其父类的所有属性字段，并且，子类和父类共享相同的主键值。一个Class一个具体表方案适合需要较高查询性能，继承体系层次不太复杂，并且基类包含较少的属性而子类扩展较多属性，并且能够承受一定的数据库冗余的情况。 &lt;/p&gt;&#xD;
&lt;p&gt;一个Class映射一个具体表方案的优点主要就是查询性能好，读操作只需操作一张表，和实体数据的对应结构清晰，数据库表迁移和维护会比较方便；主要的缺点是数据冗余较大，因为每次插入一条子类数据时，同时要插入一份子类包含的父类字段的数据到所有父类层次表中。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;3. 一个Class映射一个扩展表&lt;/p&gt;&#xD;
&lt;p&gt;所谓一个Class映射一个扩展表是指继承体系中的每个Class对应一张数据表，但是，每个子类不冗余包含父类的所有属性，而只是包含扩展的属性和共享的主键值。一个Class映射一个扩展表方案适合继承体系非常复杂，结构易变，并希望最大程度减少数据冗余的情形。 &lt;/p&gt;&#xD;
&lt;p&gt;一个Class映射一个扩展表方案的优点是结构灵活，新增子类或插入中间的继承类都很方便，冗余数据最少；但是缺点是，无论读还是写操作都会涉及到子类和所有的父类。读操作时，必须自然链接查询所有的父类对应的数据表，而插入或更新数据时，也需要写所有的父类表。 &lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;4. 通用的表结构映射所有的Class&lt;/p&gt;&#xD;
&lt;p&gt;这种方案其实不仅支持用一张表存储一个继承体系，它甚至可以支持，用一张表存储任意数量的不同Class。它的原理是元数据驱动。这张表的每一行，包含一个类型的标识字段，一个表示Class的属性名称的字段和一个表示Class的属性值的字段。在运行时，通过唯一标识取出描述一个Class实例的所有Property的值，再根据Property的名称来映射。&lt;/p&gt;&#xD;
&lt;p&gt;关于各种继承映射的实例分析和详细的优劣比较，请参见：Scott W. Ambler的&lt;a href="http://www.agiledata.org/essays/mappingObjects.html"&gt;O/R Mapping In Detail&lt;/a&gt;。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;关联映射&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;1. 一对一关联、一对多关联（包含一对一和一对多的自关联）&lt;/p&gt;&#xD;
&lt;p&gt;所谓一对一关联，实际上还可以分为三种情形，即0..1 - 1，1 &amp;#8211; 1，1 &amp;#8211; 0..1三种情形；而一对多关联则分为* - 1和1 - *。&lt;/p&gt;&#xD;
&lt;p&gt;以下三种方案中第1)种为最常用的映射方案，后面几种是在某些特殊情形下可参考的方案：&lt;/p&gt;&#xD;
&lt;p&gt;1) 最常用的方案为为需要其他对象引用的类对应的表增加一个到被引用对象对应表的外键即可，只不过，与表对应的实体类代码中，对于一对多情形下的&amp;#8220;多&amp;#8221;这一端，需定义成集合类型；&lt;br /&gt;2)&amp;nbsp;在Hibernate称为&amp;#8220;组件（Component）映射&amp;#8221;，举例来说，假如Person类包含一个Address成员类型的属性，而Address由City，Street，ZipCode三个成员属性组成，假如Address除了与Person关联不被其他对象使用，则我们可以考虑只用一张数据表Person来持久化Person和Address这两张表，Person数据表包含Person类中除Address的属性和Address类中的所有属性的集合，当然，这时需要在元数据中特别指明映射关系；&lt;br /&gt;3)&amp;nbsp;还有一种针对上面的方案中的Person，Address两个类的持久化方案则是将Address类型的所有属性先序列化，再存入Person表的字段Address中，这样也可以只用一张表来持久化两个类，当然，本方案中这种被序列化对象成员数据量应尽量小；&lt;br /&gt;4)&amp;nbsp;还有一种方案是共享同一主键值的一对一关联。即将原本可以同属于一个表中相对使用不太频繁的字段提出来放在另一张表中，这样，这两张表的记录就可以通过一个相同的主键进行关联。 &lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;2. 多对多关联（包含多对多的自关联）&lt;/p&gt;&#xD;
&lt;p&gt;所谓多对多关联自然就是* - *这种情形了。一般都需要一张包含关联双方主键的关联表，在取数据时，需要链接该关联表和数据表。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;英文资料&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Object-relational_mapping"&gt;http://en.wikipedia.org/wiki/Object-relational_mapping&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.agiledata.org/essays/impedanceMismatch.html"&gt;http://www.agiledata.org/essays/impedanceMismatch.html&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.agiledata.org/essays/culturalImpedanceMismatch.html"&gt;http://www.agiledata.org/essays/culturalImpedanceMismatch.html&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.agiledata.org/essays/drivingForces.html"&gt;http://www.agiledata.org/essays/drivingForces.html&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.agiledata.org/essays/mappingObjects.html"&gt;http://www.agiledata.org/essays/mappingObjects.html&lt;/a&gt; &lt;/li&gt;&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/teddyma/aggbug/1789714.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/teddyma/archive/2010/08/01/1789714.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/teddyma/archive/2010/07/31/1789538.html</id><title type="text">理解单元测试(Unit Testing)</title><summary type="text">本文的目的是以最精炼的语言，正解什么是单元测试，为什么要单元测试，和如何进行单元测试。</summary><published>2010-07-31T07:49:00Z</published><updated>2010-07-31T07:49:00Z</updated><author><name>Teddy's Knowledge Base</name><uri>http://www.cnblogs.com/teddyma/</uri></author><link rel="alternate" href="http://www.cnblogs.com/teddyma/archive/2010/07/31/1789538.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/teddyma/archive/2010/07/31/1789538.html"/><content type="html">&lt;p&gt;本文的目的是以最精炼的语言，正解什么是单元测试，为什么要单元测试，和如何进行单元测试。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;什么是单元测试(Unit Testing)？&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;测试(Testing)这个词很容易理解，那么什么是单元(Unit)呢？一个单元指的是应用程序中可测试的最小的一组源代码。一组源代码可测试，一般要求其有明确的输入和输出。因此，一般来讲，源代码中包含明确的输入和输出的每一个方法被认为是一个可测试的单元。注意，这里指的输出，并不局限于方法的返回值或对输入参数的改变，而包括了方法的执行过程中，改变的任何数据。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;为什么要做单元测试？&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;单元测试的目的，是将应用程序的所有源代码，隔离成最小的可测试的单元，保证每个单元的正确性。理想情况下，如果每个单元都能保证正确，就能保证应用程序整体相当程度的正确性。&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
另一方面，单元测试也是一种特殊类型的文档，相对于书面的文档，测试脚本本身往往就是对被测试代码的实际的使用代码，对于帮助开发人员理解被测试单元的使用是相当有帮助的。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;如何进行单元测试？&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;隔离&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;要进行单元测试，首先就是要将被测试的单元，与所有外部依赖进行隔离。一般来讲，进行隔离主要有两种可选的技术，一种叫Stub，一种叫Mock。对于两个的区别和取舍，Martin Fowler有一个很经典的文章：&lt;a href="http://martinfowler.com/articles/mocksArentStubs.html"&gt;Mocks Aren't Stubs&lt;/a&gt;。简单来说，如果要Stub或Mock一个方法，Stub指的是，为了测试，手动写一个相同签名的方法，根据我的测试需要，对给定的输入参数，返回一定的输出；而Mock则借助一定的框架组件，自动生成一个方法的实现，对于给定的输入，返回一定的输出。当然，所谓的Stub或Mock，并不单指对方法，尤其是在面向对象编程中，也可以是对属性，对对象或者对接口，等等。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;br /&gt;&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;为了方便实现隔离，对软件设计和开发的一个潜在的激励是，软件模块的设计人员和开发人员，不得不时时思考，在当前语言支持的各种特性的条件下，如何使得所写的代码，能够被方便的被隔离。虽然这看起来很像是一种限制，但是和软件设计的&lt;a href="http://en.wikipedia.org/wiki/Solid_%28object-oriented_design%29"&gt;SOLID&lt;/a&gt;原则，其实是不谋而合的，因此，也就未尝不是一个优点了。&lt;br /&gt;&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;测试&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;解决了隔离的问题，我们就可以关注对一个单元的测试本身了。首先要提的一点是，所谓测试一个单元，一般往往是写一段测试脚本，给定输入，验证输出。但是，其实并非所有的单元测试都可以完全由计算机方便地自动完成的。尤其是如图形UI这样的比较抽象元素的判断。因此，从相对广义的角度来说，凡是能够用于验证一个被测试的单元正确性的所有方法进行的测试，都可以被认为是有效的测试。&amp;nbsp;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;自动化&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;解决了隔离和测试本身的问题，下一个需要讨论的就是测试的效率的问题。尽管上面提到了，所谓单元测试，不完全都可以由计算机方便地自动完成，但是，对于那些可以由计算机自动完成的单元测试，如何提高写测试代码和运行测试的效率呢？答案是，陆续出现了很多的测试框架和测试工具。这些测试框架或工具，一般都会提供提高创建测试脚本效率的工具，并且，提供自动化执行测试和查看执行结果的功能，往往还可以很方便的和代码的自动化编译和部署进行集成，使测试自动化。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Guidelines&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;这里整理了一些如何写好测试代码的Guidelines：&lt;br /&gt;&#xD;
&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
     &lt;li&gt;有测试总比没测试好&lt;/li&gt;&#xD;
     &lt;li&gt;测试尽可能的小而快&lt;/li&gt;&#xD;
     &lt;li&gt;使测试自动化&lt;/li&gt;&#xD;
     &lt;li&gt;进行代码覆盖检测&lt;/li&gt;&#xD;
     &lt;li&gt;在写任何新的实现代码或测试代码之前，先修复所有运行失败的测试&lt;/li&gt;&#xD;
     &lt;li&gt;再简单的代码也需要测试&lt;/li&gt;&#xD;
     &lt;li&gt;特别注意输入参数的边界案例&lt;/li&gt;&#xD;
     &lt;li&gt;不要只测试正常流程，还要测试可选和例外流程&lt;/li&gt;&#xD;
     &lt;li&gt;对于每一个报告的Bug，写一个对应的测试用例，方便回归测试&lt;/li&gt;&#xD;
     &lt;li&gt;时刻注意，所有测试都通过，不代表程序就真的100%没问题，测试永远只是辅助 &lt;br /&gt;&#xD;
     &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;strong&gt;英文资料&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
     &lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Unit_testing"&gt;http://en.wikipedia.org/wiki/Unit_testing&lt;/a&gt;&lt;/li&gt;&#xD;
     &lt;li&gt;&lt;a href="http://martinfowler.com/articles/mocksArentStubs.html"&gt;http://martinfowler.com/articles/mocksArentStubs.html&lt;/a&gt;&lt;/li&gt;&#xD;
     &lt;li&gt;&lt;a href="http://geosoft.no/development/unittesting.html"&gt;http://geosoft.no/development/unittesting.html&lt;/a&gt;&lt;/li&gt;&#xD;
     &lt;li&gt;&lt;a href="http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/UnitTesting/1-Articles/UTGuidelines.html#apple_refdocuidTPEAAACBHC-BBCBGHCJ"&gt;http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/UnitTesting/1-Articles/UTGuidelines.html#apple_refdocuidTPEAAACBHC-BBCBGHCJ&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;/ul&gt;&lt;img src="http://www.cnblogs.com/teddyma/aggbug/1789538.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/teddyma/archive/2010/07/31/1789538.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/teddyma/archive/2010/07/30/1788364.html</id><title type="text">理解TDD</title><summary type="text">本文的目的是以最精炼的语言，正解什么是TDD，为什么要TDD，和TDD的难点。</summary><published>2010-07-29T17:24:00Z</published><updated>2010-07-29T17:24:00Z</updated><author><name>Teddy's Knowledge Base</name><uri>http://www.cnblogs.com/teddyma/</uri></author><link rel="alternate" href="http://www.cnblogs.com/teddyma/archive/2010/07/30/1788364.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/teddyma/archive/2010/07/30/1788364.html"/><content type="html">&lt;p&gt;本文的目的是以最精炼的语言，正解什么是TDD，为什么要TDD，和TDD的难点。 &lt;br /&gt;&#xD;
&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;什么是TDD？&lt;/strong&gt;&lt;/p&gt;&#xD;
简单的说，TDD = 测试先行(TFD, Test First Development) + 重构(Refactoring) + 回归测试(Regression Test)。&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;table cellpadding="0" cellspacing="0"&gt;&#xD;
     &lt;tbody&gt;&#xD;
         &lt;tr&gt;&#xD;
             &lt;td&gt;&lt;img src="http://nintegrate.com/GetFile.aspx?Page=TDD&amp;amp;File=tdd_cycle.jpg" alt="Image" /&gt;&lt;/td&gt;&#xD;
         &lt;/tr&gt;&#xD;
     &lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;br /&gt;&#xD;
如果要实现某个功能，TDD要求在初步定义完这个功能的外部接口之后，先根据这个功能的用例写测试代码(黑盒测试)，测试代码检验的是这个功能的外部接口的使用场景，而非具体的实现细节。然后，才是实现这个功能的这些外部接口，在实现的过程中，同时还会根据需要写一些单元测试，一般单元测试测试的不仅仅是外部接口，还包括实现的内部细节(白盒测试)。关于单元测试的更多讨论，请参考：&lt;a href="http://www.cnblogs.com/teddyma/archive/2010/07/31/1789538.html"&gt;UnitTesting&lt;/a&gt;。如果发现这些外部接口设计得有问题，则需要进行修改和重构。每次代码修改之后，或者增加新功能之前，都需要回归运行已有的所有测试(不仅仅是测试先行的这些测试，还包括所有的单元测试)。&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
测试先行保证了功能的所有使用场景的逻辑性和完备性；重构则在保证功能语义的前提下，尽可能安全的改进设计和实现；回归测试则保证了任何修改不破坏任何已有的功能，这样开发人员就能放心大胆地专注于实现或改进眼前的功能。这样无疑既提高了开发人员的信心和工作效率，而且时刻保证了功能的完整性和代码的正确性。&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
如果对TDD详细的前世今生感兴趣，请参考：&lt;a href="http://en.wikipedia.org/wiki/Test-driven_development" title="TDD on WikiPedia" target="_blank"&gt;TDD on WikiPedia&lt;/a&gt;&lt;a href="http://nintegrate.com/Edit.aspx?Page=TDD&amp;amp;Section=1"&gt;&lt;br /&gt;&#xD;
&lt;/a&gt;&#xD;
&lt;p&gt;&lt;strong&gt;为什么TDD？&lt;/strong&gt;&lt;/p&gt;&#xD;
TDD最大的好处是时刻确保了每次添加或修改任何代码的过程中，对任何已有的功能都是安全的。尤其是对于编译和回归测试的运行时间较短的情况下，有大牛甚至建议每新增或修改超过10行可运行的代码，就运行一次回归测试。这样不仅仅能确保你写的每一行代码的正确性，而且，基本可以避免使用单步调试来发现问题，因为，问题往往就应该在最新的这10多条代码上。&lt;br /&gt;&#xD;
&lt;br /&gt;&#xD;
TDD的另一个好处是，相对于几十页上百页的天知道和什么年代的代码同步的技术文档来说，TDD的测试代码因为描述的就是功能的实际使用场景，并且和代码一定是实时同步的，对开发人员来说，它其实是一种既准确，又易于理解，易于维护的&amp;#8220;技术文档&amp;#8221;。&lt;a href="http://nintegrate.com/Edit.aspx?Page=TDD&amp;amp;Section=2"&gt;&lt;br /&gt;&#xD;
&lt;/a&gt;&#xD;
&lt;p&gt;&lt;strong&gt;TDD的难点&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
     &lt;li&gt;并非所有类型的代码都适合TDD，尤其是那些不能由机器简单的判断对错的情形，比如图形UI和数据库设计。&lt;/li&gt;&#xD;
     &lt;li&gt;TDD需要让管理层意识到TDD的价值，为TDD预留额外的开发时间，并且强制每个开发人员按TDD的流程来写代码，需要自上而下的管理和开发流程的支持。&lt;br /&gt;&#xD;
     &lt;/li&gt;&#xD;
&lt;/ul&gt;&#xD;
&lt;p&gt;&lt;strong&gt;英文资料&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;ul&gt;&#xD;
     &lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Test-driven_development" title="http://en.wikipedia.org/wiki/Test-driven_development" target="_blank"&gt;http://en.wikipedia.org/wiki/Test-driven_development&lt;/a&gt;&lt;/li&gt;&#xD;
     &lt;li&gt;&lt;a href="http://www.agiledata.org/essays/tdd.html" title="http://www.agiledata.org/essays/tdd.html" target="_blank"&gt;http://www.agiledata.org/essays/tdd.html&lt;/a&gt;&lt;/li&gt;&#xD;
     &lt;li&gt;&lt;a href="http://codebetter.com/blogs/jeremy.miller/archive/2006/03/31/142091.aspx" title="http://codebetter.com/blogs/jeremy.miller/archive/2006/03/31/142091.aspx" target="_blank"&gt;http://codebetter.com/blogs/jeremy.miller/archive/2006/03/31/142091.aspx&lt;/a&gt;&lt;/li&gt;&#xD;
     &lt;li&gt;&lt;a href="http://c2.com/cgi/wiki?TestDrivenDevelopment" title="http://c2.com/cgi/wiki?TestDrivenDevelopment" target="_blank"&gt;http://c2.com/cgi/wiki?TestDrivenDevelopment&lt;/a&gt;&lt;/li&gt;&#xD;
&lt;/ul&gt;&lt;img src="http://www.cnblogs.com/teddyma/aggbug/1788364.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/teddyma/archive/2010/07/30/1788364.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/teddyma/archive/2010/07/11/1775394.html</id><title type="text">讨论: TDD in HTML &amp;amp; JavaScript 之可行性和最佳实践</title><summary type="text">最佳实践一：应用MVC模式;最佳实践二：应用依赖注入和IoC容器;最佳实践三：应用模板引擎呈现主体内容;最佳实践四：应用Script#;...</summary><published>2010-07-11T14:23:00Z</published><updated>2010-07-11T14:23:00Z</updated><author><name>Teddy's Knowledge Base</name><uri>http://www.cnblogs.com/teddyma/</uri></author><link rel="alternate" href="http://www.cnblogs.com/teddyma/archive/2010/07/11/1775394.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/teddyma/archive/2010/07/11/1775394.html"/><content type="html">&lt;p&gt;&lt;strong&gt;题外话&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;昨天就想发起这个话题的讨论，只是觉得对于讨论的支持，博客园现有的功能天然似乎还不能很好的支持。所以有了&lt;a href="http://www.cnblogs.com/teddyma/archive/2010/07/10/1774988.html" target="_blank"&gt;突然发现想在博客园发起一个有价值的讨论其实很难&lt;/a&gt;一文。&lt;a href="http://www.cnblogs.com/Alexander-Lee/" target="_blank"&gt;亚历山大同志&lt;/a&gt;提到&amp;#8220;博客园的讨论需要发起争议性话题，比如 .net sucks之类&amp;#8221;。回顾如&lt;a href="http://www.cnblogs.com/firelong/archive/2010/07/09/1774490.html" target="_blank"&gt;关于近期C#大论战的回应&lt;/a&gt;这样的近期引起讨论的焦点话题，貌似确实如此。深以为叹。近期的C#大论战是幸运的，尽管中间还是参杂了很多口水，&lt;a href="http://techvery.com/jzli/2010/07/07/metadata%E6%98%AF-net%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%A0%B8%E5%BF%83%E7%81%B5%E9%AD%82/" target="_blank"&gt;李建忠老师的加入&lt;/a&gt;，一定程度上最终将话题引向了正确的方向。幸哉。我这个围观群众也从中获益良多。不仅仅是对于这个技术话题正确理解，还包括李老师在他的博客最后提到的：抛掉&amp;#8220;非要辩个胜负，分个高低&amp;#8221;的怪诞氛围，而是来一些扎扎实实的技术说理过程，相信会更有意义&amp;#8212;&amp;#8212;如是，则国内技术社区成长可待。&lt;/p&gt; &lt;p&gt;言归正传，还是尽快开始我想讨论的主题，且不管讨论最终成效如何，既然发起讨论，还是先尽可能分享自己的想法，以示诚意。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;TDD的背景&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;自从03年Beck正式提出（事实上在00年，Beck提出eXtreme Programming时，就已经提出了这个词）Test-driven design/development这样一个基于测试优先、重构和迭代的革命性的开发方法以来，无数的实践已经证明，&lt;strong&gt;对于适合进行TDD的领域&lt;/strong&gt;，TDD能够极大地提高代码的可维护性和开发效率。&lt;/p&gt; &lt;p&gt;TDD的基本流程图如下：&lt;/p&gt; &lt;p&gt;&lt;img alt="http://upload.wikimedia.org/wikipedia/en/9/9c/Test-driven_development.PNG" src="http://upload.wikimedia.org/wikipedia/en/9/9c/Test-driven_development.PNG" /&gt;&lt;/p&gt; &lt;p&gt;在这样一个迭代的流程中，在写任何的production code之前，先写test，再写production code，并且不断地对代码进行清理和重构，并且每次迭代都要进行回归测试，保证新增的test和production code不会break任何已有的test和production代码。&lt;/p&gt; &lt;p&gt;一般来讲，支持自动化的回归测试的工具相对比较容易实现。整个流程中的难点在于：当先行写test代码的时候，必然要求先定义被测试的production code的外部接口，对于第一次迭代，自然没有问题；但是，由于需求的变更，或者整体设计的变更，在后续的迭代过程中，经常会发生，已有的已经实现并且包含完整测试的production code的外部接口需要变更或者说重构；尽管从理论上，绝大多数的重构需求，都有规律甚至是模式可循，但是，如果完全依赖于人工操作，则不仅效率不高，且极易出错。所以，但凡成功的TDD实践，其中都不乏很多支持重构的工具。比如，现行绝大多数的集成开发环境，都有很多自动化的代码重构工具，大大的降低了代码重构的成本。&lt;/p&gt; &lt;p&gt;但是还有一些领域，TDD还略微有些力不从心，或者说，至少，至今没有看到太多比较好的实践案例。比如：对于Database和UI。&lt;/p&gt; &lt;p&gt;对于数据库开发的TDD，到目前为止面临的主要挑战是工具的支持。无论是自动化的回归测试工具，还是重构工具都还远远不够成熟。&lt;/p&gt; &lt;p&gt;而对于UI的TDD，则是本文的主题。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;TDD in HTML &amp;amp; JavaScript 概述&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;谈到应用程序的UI，其实包括两个方面的内容：一方面是纯图形的look &amp;amp; feel；另一方面，则是用户和应用程序的交互。用户和应用程序的交互往往同时导致图形界面的变化，并且，转换到新的交互行为。&lt;/p&gt; &lt;p&gt;由于工作实践中主要是基于WEB的HTML和JavaScript的项目，这里对TDD in UI的讨论，将focus在基于HTML和JavaScript的UI。&lt;/p&gt; &lt;p&gt;同时，一般来讲，WEB程序的表现层主要有客户端代码和服务端代码，而服务端代码，相对来说，更容易被测试。所以，本文讨论的重点，主要focus在客户端代码。换句话说，这里讨论的TDD in HTML &amp;amp; JavaScript指的是对于客户端的HTML和JavaScript的TDD。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;TDD in HTML &amp;amp; JavaScript 之可行性&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;说到可行性，其实可以分两个层面：理论上的可行性，和实际应用的可行性。&lt;/p&gt; &lt;p&gt;第一个问题是：纯图形的look &amp;amp; feel理论上可以进行自动化的测试吗？答案几乎是否定的。因此，主要用于呈现纯图形的HTML及CSS，也几乎是很难自动化测试的。&lt;/p&gt; &lt;p&gt;那么，用户和应用程序的交互理论上是否可以进行自动化测试呢？答案毫无疑问是肯定的。&lt;/p&gt; &lt;p&gt;WEB交互的测试其实可以根据WEB程序的架构，分为两种类型：&lt;/p&gt; &lt;ol&gt; &lt;li&gt;传统的WEB程序主要基于服务端来呈现内容，用户和页面的交互，主要是get，post数据和页面跳转。因此，对应的测试方式，主要也是由测试工具模拟需要get或post的数据，并且跟踪期望的页面跳转情况。这种情况下的测试其实相对简单，因此本文不想过多讨论。&lt;/li&gt; &lt;li&gt;当前的基于AJAX的WEB程序则很大程度上丰富了用户和页面交互的方式，用户和页面的交互，除了传统的get，post数据和页面跳转，在页面不刷新的情况下，还通过触发各种DOM事件，甚至直接触发JavaScript方法的执行，由JavaScript来改变和呈现内容。此时，传统的只能模拟需要get或post的数据的测试工具就无能为力了。此时由于所有的逻辑代码都在JavaScript中，所以，本质上其实是需要对大量的JavaScript代码进行测试。此正是本文希望讨论的重点。&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;首先，针对JavaScript的自动化测试工具其实已经有不少了，如：&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;a href="http://jsunittest.com/" target="_blank"&gt;JsUnitTest&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="http://docs.jquery.com/QUnit" target="_blank"&gt;QUnit&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Mock工具也有：&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;a href="http://code.google.com/p/jqmock/" target="_blank"&gt;jqMock&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="http://jsmock.sourceforge.net/" target="_blank"&gt;JSMock&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;支持直接重构JavaScript代码的工具相对比较少，提供的功能也都还非常弱：&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;a href="http://www.jetbrains.com/idea/features/javascript_editor.html#JavaScript_refactoring" target="_blank"&gt;IntelliJ IDEA&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="http://www.eclipse.org/" target="_blank"&gt;Eclipse&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;从支持工具的现状，可以说，影响TDD in JavaScript的实际可行性的因素之一是重构工具的缺乏。&lt;/p&gt; &lt;p&gt;不过，最近的情况有了一些改变，现在也出现了一些支持JavaScript重构的变通的解决方案，如：&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Script# - Write C# code，compile C# source code directly to JavaScript code&lt;/li&gt; &lt;li&gt;jsc &amp;#8211; Write any .NET code, convert .NET assembly to JavaScript, ActionScript, java or PHP code&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;这些方案的特点是，利用现有的IDE对流行的编程语言如C#源代码的完善的coding，尤其是强类型，重构和测试的支持，让开发人员写C#，由工具转换为可直接执行的，格式化的JavaScript代码。除了充分利用IDE对流行语言的coding支持之外，这类方案的另一个好处是，相对于高薪聘请Senior的JavaScript开发人员，Junior的C#的开发人员要便宜得多，也易招得多，但得益于Script#，已经足够能用他们熟悉的C#，写出逻辑复杂和OO的JavaScript代码，因此，开发成本被大大降低。&lt;/p&gt; &lt;p&gt;综上所述，TDD in JavaScript不仅理论上是可行，实际应用上，也是有足够的工具支持的。尤其是如Script#这样的工具的出现，极大地提高了JavaScript代码的开发效率。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;TDD in JavaScript 之最佳实践&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;谁都希望能有最佳实践。什么是最佳实践呢？有很多人见不得&amp;#8220;best&amp;#8221;，&amp;#8220;最&amp;#8221;这样的词，认为，这个世界上没有&amp;#8220;最&amp;#8221;的东西。有吗？当然有！我们首先要略为上升到哲学的高度，对于包含&amp;#8220;最&amp;#8221;这样的词汇的命题，如果想要为&amp;#8220;真命题&amp;#8221;，则必然是需要加上一个适当的前提条件的。&lt;/p&gt; &lt;p&gt;比如说：我说&amp;#8220;我是这世界上最NB的人&amp;#8221;。这毫无疑问是个假命题。因为，缺乏适当的前提条件。你可以自己做个练习，如果觉得这个命题假，想办法给它加上更多的前提条件，一定能让它变真。&lt;/p&gt; &lt;p&gt;所以，所谓最佳实践，指的是，对一个或者一类特定的问题，在一个相对确定的背景下，所能采取的实际处理的方案典范。加上前提条件，则&amp;#8220;最佳实践&amp;#8221;当然是存在的，也是值得讨论的。&lt;/p&gt; &lt;p&gt;通过前面的章节，我们已经把本文重点讨论的主题，限制到一个相对小的范围，那就是对基于AJAX的WEB应用程序中的大量的JavaScript代码，如何进行TDD？&lt;/p&gt; &lt;p&gt;并且，我们也收集了足够的支持TDD需要的各种工具，包括自动化测试工具，Mock工具和重构工具。在这些工具的支持下，很大程度上，WEB程序客户端JavaScript代码的TDD和服务端代码的TDD，不应该有很大的区别。但同时，由于客户端代码的特殊性，自然也应该有一些客户端脚本代码所特有的实践模式。&lt;/p&gt; &lt;p&gt;以下首先列出本人推荐的一些实践模式，希望大家能一起修正和补缺。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;最佳实践一：应用MVC模式&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;在传统的非AJAX的WEB程序中，JavaScript往往处于非常辅助性的地位。除了实现一些特效和数据验证等辅助功能之外，一个页面的JavaScript代码，恐怕屈指可数，自然无所谓测试，甚至是TDD了。&lt;/p&gt; &lt;p&gt;但是在现在的复杂的AJAX应用中，以往必须由多个独立页面的get，post和页面跳转才能组合实现的功能，通过JavaScript，可以在一个无需刷新浏览器的页面中，轻易实现，不但用户体验更佳，速度更快，对服务器的负担也更小。&lt;/p&gt; &lt;p&gt;此时，原本传统WEB程序的服务端需要处理的问题，如数据绑定，事件绑定，逻辑控制等，需要在客户端进行处理。也因此，原本为了解决WEB程序服务端代码可测试性问题MVC模式，也就一样可以良好的应用于客户端。清晰的将JavaScript代码分割成M，V，C，将能够把相同的逻辑职责尽可能集中到一起来管理，从而极大地增加客户端代码的可维护性和可测试性。&lt;/p&gt; &lt;p&gt;下表简单对比服务端和客户端MVC下M，V，C的对应职责：&lt;/p&gt; &lt;table width="893" border="1" cellpadding="2" cellspacing="0"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td valign="top" width="100"&gt;&amp;nbsp;&lt;/td&gt; &lt;td valign="top" width="246"&gt; &lt;p align="center"&gt;Model&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="255"&gt; &lt;p align="center"&gt;View&lt;/p&gt;&lt;/td&gt; &lt;td valign="top" width="290"&gt; &lt;p align="center"&gt;Controller&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="100"&gt;Server Side&lt;/td&gt; &lt;td valign="top" width="246"&gt;返回用于呈现页面内容的数据的 Domain Objects&lt;/td&gt; &lt;td valign="top" width="255"&gt;代表了一个页面的抽象，包括页面的内容呈现，数据，事件定义&lt;/td&gt; &lt;td valign="top" width="290"&gt;处理View上触发的事件，获取数据，更新View上的数据，触发View的内容呈现&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td valign="top" width="100"&gt;Client Side&lt;/td&gt; &lt;td valign="top" width="246"&gt;返回 JSON 数据的 Restful Services&lt;/td&gt; &lt;td valign="top" width="255"&gt;同上&lt;/td&gt; &lt;td valign="top" width="290"&gt;同上&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;&lt;strong&gt;最佳实践二：应用依赖注入和IoC容器&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;应用MVC模式，本质上是抽象的逻辑职责上的解耦。而依赖注入和IoC容器则是代码的物理依赖性上的解耦。尽可能的利用构造器注入，设值注入，接口注入或IoC容器来解除具体的实现类之间的直接依赖，自然就能极大的大提高每个具体的实现类的可测试性。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;最佳实践三：应用模板引擎呈现主体内容&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;AJAX应用中的一个需要客户端呈现的View，必然需要呈现一些HTML，这些HTML往往需要根据Model返回的JSON数据动态构造。一般来讲，我们会有三种方式来构造和呈现这些HTML：&lt;/p&gt; &lt;ul&gt; &lt;li&gt;在JavaScript中遍历JSON数据，拼接HTML字符串，呈现到页面上；&lt;/li&gt; &lt;li&gt;在JavaScript中遍历JSON数据，动态实例化DOM对象，通过DOM对象的方法，呈现HTML的DOM；&lt;/li&gt; &lt;li&gt;通过如JTemplate这样的JavaScript模板引擎，将JSON数据绑定到一个HTML模板，由模板引擎呈现最终的HTML；&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;本最佳实践的建议内容就是，对于一个View的&lt;strong&gt;主体内容&lt;/strong&gt;，应该尽可能的通过模板引擎来呈现。为什么呢？因为，对于一个WEB程序来说，最不稳定的，会经常变化的部分，无疑是纯图形的HTML和CSS，使用模板引擎，将能够使得这些HTML尽可能的集中，并且易于修改，也更易于HTML和JavaScript的整合。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;最佳实践四：应用Script#&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;应用Script#好处前面已经提过了，这里再简单列举一下：&lt;/p&gt; &lt;ul&gt; &lt;li&gt;充分利用现有的IDE对流行的编程语言如C#源代码的完善的coding，尤其是强类型，重构和测试的支持；&lt;/li&gt; &lt;li&gt;相对于高薪聘请Senior的JavaScript开发人员，Junior的 C#的开发人员要便宜得多；&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;如反对，请列举我不该用它的理由？&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;对于以上几个最佳实践的应用实例，请参见我之前的文章：&lt;a href="http://www.cnblogs.com/teddyma/archive/2010/07/01/1769561.html" target="_blank"&gt;This is jqMVC# &amp;#8211; CNBLOGS Google Tracer Sample&lt;/a&gt;。&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;欢迎补缺、指正！谢谢！&lt;/p&gt;&lt;img src="http://www.cnblogs.com/teddyma/aggbug/1775394.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/teddyma/archive/2010/07/11/1775394.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/teddyma/archive/2010/07/10/1774988.html</id><title type="text">突然发现想在博客园发起一个有价值的讨论其实很难</title><summary type="text">为什么会有如题的感叹呢?其实,我们本来只是想和大家探讨一下TDD in HTML &amp;amp; JavaScript的可行性和最佳实践.首先想到用blog,可是,既然我只是想讨论,如果写一个blog一定很短,并且没有直接有价值的内容,所以,往首页放貌似是不合适的.但是不放首页其实没有几个人能看到的.就算我厚脸皮放到首页,也是很快就会被沉下去的.那么,别的呢?博问?看起来只是给新手问问题的?小组?我不...</summary><published>2010-07-10T12:56:00Z</published><updated>2010-07-10T12:56:00Z</updated><author><name>Teddy's Knowledge Base</name><uri>http://www.cnblogs.com/teddyma/</uri></author><link rel="alternate" href="http://www.cnblogs.com/teddyma/archive/2010/07/10/1774988.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/teddyma/archive/2010/07/10/1774988.html"/><content type="html">为什么会有如题的感叹呢?&lt;br /&gt;其实,我们本来只是想和大家探讨一下TDD in HTML &amp;amp; JavaScript的可行性和最佳实践.&lt;br /&gt;&lt;br /&gt;首先想到用blog,可是,既然我只是想讨论,如果写一个blog一定很短,并且没有直接有价值的内容,所以,往首页放貌似是不合适的.但是不放首页其实没有几个人能看到的.就算我厚脸皮放到首页,也是很快就会被沉下去的.&lt;br /&gt;&lt;br /&gt;那么,别的呢?&lt;br /&gt;&lt;br /&gt;博问?看起来只是给新手问问题的?&lt;br /&gt;&lt;br /&gt;小组?我不得不先加入一个小组,但是,我能发到多个小组吗?貌似不行,因为毕竟不是论坛的分类.好吧,就算我不介意选择一个小组加入.看看所有小组最近更新,最近6小时内只有8个回复.这样的整体活跃程度,我很难想象我期待的讨论能起到效果.&lt;br /&gt;&lt;br /&gt;闪存?很推特,但是,很不合适讨论稍复杂一点的问题.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;没有了.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;//其实,我本来想给dudu发这样一个站内短信.但是打了不少字,发个短信觉得有点可惜了.所以,厚脸皮放博客首页一会儿和大家讨论一下吧,其实很快也就沉下去了~~ :]]&lt;br /&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/teddyma/aggbug/1774988.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/teddyma/archive/2010/07/10/1774988.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/teddyma/archive/2010/07/01/1769561.html</id><title type="text">This is jqMVC# – CNBLOGS Google Tracer Sample</title><summary type="text">“Google Tracer” is a HTML &amp; JavaScript application, tracing the blog you are reading with Google Web Search. You should be able to see it running in the left navigation panel in my blog. It is visible only when you are reading any of my blog posts. Technically, it is just googling the title of a post as the search keyword through the Google AJAX Search API.</summary><published>2010-07-01T15:54:00Z</published><updated>2010-07-01T15:54:00Z</updated><author><name>Teddy's Knowledge Base</name><uri>http://www.cnblogs.com/teddyma/</uri></author><link rel="alternate" href="http://www.cnblogs.com/teddyma/archive/2010/07/01/1769561.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/teddyma/archive/2010/07/01/1769561.html"/><content type="html">&lt;p&gt;In previous post - &lt;a href="http://www.cnblogs.com/teddyma/archive/2010/06/20/1761122.html" target="_blank"&gt;This is jqMVC# - Definition &amp;amp; Summary&lt;/a&gt;, I briefly introduced what is jqMVC#. In this post, I&amp;#8217;ll show you a &amp;#8220;CNBLOGS Google Tracer&amp;#8221; sample application which is applying the jqMVC# architecture.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Function&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;a target="_blank" href="http://images.cnblogs.com/cnblogs_com/teddyma/WindowsLiveWriter/ThisisjqMVCCNBLOGSGoogleTracerSample_13574/image_2.png"&gt;&lt;img src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif"  id="code_img_opened_5f4d0320-42b1-4d60-939e-59caa26310af" onclick="cnblogs_code_hide('5f4d0320-42b1-4d60-939e-59caa26310af',event)" style="display: none;"&gt;&lt;div id="cnblogs_code_open_5f4d0320-42b1-4d60-939e-59caa26310af" &gt;&lt;div&gt;&lt;!--&lt;br/ /&gt;&lt;br/ /&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br/ /&gt;http://www.CodeHighlighter.com/&lt;br/ /&gt;&lt;br/ /&gt;--&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;class&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;MockGoogleTracerView&amp;nbsp;:&amp;nbsp;IGoogleTracerView&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;private&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;int&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;_searchStart&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800080;"&gt;0&lt;/span&gt;&lt;span style="color: #000000;"&gt;;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;#region&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;IGoogleTracerView&amp;nbsp;Members&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;int&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;SearchStart&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;get&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;_searchStart;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;set&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_searchStart&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;value;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;GetSearchKeyword()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;keyword&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;RenderSearchResult(NIntegrate.Scripts.Test.Demo.GoogleTracer.Records.GoogleSearchResponse&amp;nbsp;response)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;event&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;System.DHTML.DOMEventHandler&amp;nbsp;ShowMoreResults;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;#endregion&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;class&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;GoogleTracerControllerTest&amp;nbsp;:&amp;nbsp;TestCase&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;override&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Execute()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;base&lt;/span&gt;&lt;span style="color: #000000;"&gt;.Execute();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MockGoogleTracerView&amp;nbsp;mockView&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;MockGoogleTracerView();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Container.RegisterInstance(&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;typeof&lt;/span&gt;&lt;span style="color: #000000;"&gt;(IGoogleTracerView),&amp;nbsp;mockView);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;GoogleTracerController&amp;nbsp;controller&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;GoogleTracerController();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;QUnit.Test(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;Test&amp;nbsp;get&amp;nbsp;View&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;,&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;delegate&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;QUnit.Equals(mockView,&amp;nbsp;controller.View);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;QUnit.Test(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;Test&amp;nbsp;Execute()&amp;nbsp;&amp;amp;&amp;nbsp;ShowMoreResults()&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;,&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;delegate&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;GoogleSearchResponse&amp;nbsp;data&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;GoogleSearchResponse();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Mock&amp;nbsp;mockAddShowMoreResults&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Mock(mockView,&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;add_showMoreResults&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mockAddShowMoreResults.Modify().Args(Is.Anything).ReturnValue();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Mock&amp;nbsp;mockRenderSearchResult&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Mock(mockView,&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;renderSearchResult&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mockRenderSearchResult.Modify().Args(data).ReturnValue();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mockRenderSearchResult.Modify().Args(data).ReturnValue();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Mock&amp;nbsp;mockGetScript&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;Mock(Script.Eval(&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;jQuery&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;),&amp;nbsp;&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;getScript&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mockGetScript.Modify().Args(Is.Anything,&amp;nbsp;Is.Anything).Callback(&lt;/span&gt;&lt;span style="color: #800080;"&gt;1&lt;/span&gt;&lt;span style="color: #000000;"&gt;,&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;null&lt;/span&gt;&lt;span style="color: #000000;"&gt;).ReturnValue();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mockGetScript.Modify().Args(Is.Anything,&amp;nbsp;Is.Anything).Callback(&lt;/span&gt;&lt;span style="color: #800080;"&gt;1&lt;/span&gt;&lt;span style="color: #000000;"&gt;,&amp;nbsp;&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;null&lt;/span&gt;&lt;span style="color: #000000;"&gt;).ReturnValue();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;QUnit.Equals(&lt;/span&gt;&lt;span style="color: #800080;"&gt;0&lt;/span&gt;&lt;span style="color: #000000;"&gt;,&amp;nbsp;mockView.SearchStart);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;((Dictionary)(&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;object&lt;/span&gt;&lt;span style="color: #000000;"&gt;)Window.Self)[&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;_googlewebsearchresults&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;]&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;data;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;controller.Execute();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;((Dictionary)(&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;object&lt;/span&gt;&lt;span style="color: #000000;"&gt;)Window.Self)[&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;_googlewebsearchresults&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;]&amp;nbsp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;nbsp;data;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;controller.ShowMoreResults();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;QUnit.Equals(&lt;/span&gt;&lt;span style="color: #800080;"&gt;4&lt;/span&gt;&lt;span style="color: #000000;"&gt;,&amp;nbsp;mockView.SearchStart);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mockAddShowMoreResults.Verify();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mockAddShowMoreResults.Restore();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mockRenderSearchResult.Verify();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mockRenderSearchResult.Restore();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mockGetScript.Verify();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mockGetScript.Restore();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The testing results of QUnit:&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;img alt="" src="http://images.cnblogs.com/cnblogs_com/teddyma/162781/o_googletracerqunit.png" width="726" height="299" /&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Source Code&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;You could download the latest source code of this demo from SVN: &lt;a target="_blank" title="http://nintegrate.googlecode.com/svn/trunk/jqMVCSharp/" href="http://nintegrate.googlecode.com/svn/trunk/jqMVCSharp/"&gt;http://nintegrate.googlecode.com/svn/trunk/jqMVCSharp/&lt;/a&gt;&lt;/p&gt; &lt;p&gt;or download this zip file: &lt;a href="http://files.cnblogs.com/teddyma/jqMVCSharpDemo.zip" target="_blank"&gt;jqMVCSharpDemo.zip&lt;/a&gt;&lt;/p&gt; &lt;p&gt;To open the project files in Visual Studio 2008, you should install &lt;a href="http://projects.nikhilk.net/Content/Projects/ScriptSharp/ScriptSharp2008.msi" target="_blank"&gt;Script# 0.5.6 for VS 2008&lt;/a&gt; first. &lt;/p&gt;&lt;img src="http://www.cnblogs.com/teddyma/aggbug/1769561.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/teddyma/archive/2010/07/01/1769561.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/teddyma/archive/2010/06/20/1761122.html</id><title type="text">This is jqMVC# - Definition &amp;amp; Summary</title><summary type="text">“j”  means it is about JavaScript;“q” means it is about jQuery &amp; jQuery plugins;“MVC” means it is about MVC pattern;“#” means it is about C#;“j”+”#”means it is about Script#;</summary><published>2010-06-19T17:08:00Z</published><updated>2010-06-19T17:08:00Z</updated><author><name>Teddy's Knowledge Base</name><uri>http://www.cnblogs.com/teddyma/</uri></author><link rel="alternate" href="http://www.cnblogs.com/teddyma/archive/2010/06/20/1761122.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/teddyma/archive/2010/06/20/1761122.html"/><content type="html">&lt;p&gt;Before discussing more, please realize what I&amp;#8217;m talking about is not &amp;#8220;theoretical research&amp;#8221;, but &amp;#8220;real successful practice&amp;#8221;.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Definition&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;I created the word &amp;#8211; &amp;#8220;jqMVC#&amp;#8221;, which means bunch of things:&lt;/p&gt; &lt;ul&gt;&lt;li&gt;&amp;#8220;j&amp;#8221;&amp;nbsp; means it is about JavaScript;&lt;/li&gt;&lt;li&gt;&amp;#8220;q&amp;#8221; means it is about jQuery &amp;amp; jQuery plugins;&lt;/li&gt;&lt;li&gt;&amp;#8220;MVC&amp;#8221; means it is about &lt;a href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller" target="_blank"&gt;MVC pattern&lt;/a&gt;;&lt;/li&gt;&lt;li&gt;&amp;#8220;#&amp;#8221; means it is about C#;&lt;/li&gt;&lt;li&gt;&amp;#8220;j&amp;#8221;+&amp;#8221;#&amp;#8221; means it is about &lt;a href="http://projects.nikhilk.net/ScriptSharp" target="_blank"&gt;Script#&lt;/a&gt;;&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;Another thing which is not directly represented by the word but implied is &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development" target="_blank"&gt;TDD&lt;/a&gt; in JavaScript but whose code is written in C#.&lt;/p&gt; &lt;p&gt;When all these things come together, it is jqMVC# &amp;#8211; a new practical architecture which makes enterprise-level HTML &amp;amp; JavaScript only rich UI development in B/S-architecture application become not only possible, but also as a representative of HTML5 with which we could really say &amp;#8216;Good bye!&amp;#8217; to Flash &amp;amp; Silverlight even now for most cross-browser web applications which do not really require those complex animation effects.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;In this summary, I&amp;#8217;m trying to talk a little bit more for each of the items I mentioned in previous section to make this architecture look more conceivable.&lt;/p&gt; &lt;p&gt;Firstly, our JavaScript code is written in C# with the help of &lt;a href="http://projects.nikhilk.net/ScriptSharp" target="_blank"&gt;Script#&lt;/a&gt;. &lt;a href="http://projects.nikhilk.net/ScriptSharp" target="_blank"&gt;Script#&lt;/a&gt; could compile C# source code to JavaScript automatically. The biggest benefit here is, even a junior developer who is familiar with C# but only know a little of JavaScript could write huge OO JavaScript code efficiently.&lt;/p&gt; &lt;p&gt;Secondly, the M, V, C classes implementing the MVC pattern in client-side JavaScript are written in C# and are compiled into JavaScript through &lt;a href="http://projects.nikhilk.net/ScriptSharp" target="_blank"&gt;Script#&lt;/a&gt; automatically. &lt;/p&gt; &lt;p&gt;Why MVC? &lt;/p&gt; &lt;p&gt;Two main benefits: &lt;/p&gt; &lt;p&gt;1. Separations of data, controlling and presentation; &lt;/p&gt; &lt;p&gt;2. Easier for unit testing; &lt;/p&gt; &lt;p&gt;In JavaScript, when we implementing MVC pattern, &lt;/p&gt; &lt;ul&gt;&lt;li&gt;Models are JSON comes from server-side services through &lt;a href="http://en.wikipedia.org/wiki/Ajax_%28programming%29" target="_blank"&gt;AJAX&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/JSON#JSONP" target="_blank"&gt;JSONP&lt;/a&gt; communication, which should be under &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development" target="_blank"&gt;TDD&lt;/a&gt; in server-side coding;&lt;/li&gt;&lt;li&gt;Controllers are Script# classes focus on the controlling of Models and Views, which should be under &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development" target="_blank"&gt;TDD&lt;/a&gt; in Script# coding;&lt;/li&gt;&lt;li&gt;Views are as connectors between pure HTML &amp;amp; JavaScript code and Script# based Models &amp;amp; Controllers code, which should also be under &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development" target="_blank"&gt;TDD&lt;/a&gt; in Script# coding;&lt;/li&gt;&lt;/ul&gt;   &lt;p&gt;Thirdly, &lt;a href="http://jquery.com/" target="_blank"&gt;jQuery&lt;/a&gt; &amp;amp; &lt;a href="http://plugins.jquery.com/" target="_blank"&gt;jQuery plugins&lt;/a&gt; are wrapped by Script# together with the Script# predefined libraries as the main tool libraries for client side UI rendering &amp;amp; controlling. Especially, jTemplate is used as the main HTML rendering engine. &lt;a href="http://docs.jquery.com/QUnit" target="_blank"&gt;QUnit&lt;/a&gt; is also wrapped by Script# and used as the client-side testing library to implement client-side regression testing which is the core of client-side TDD.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;To be continued&amp;#8230;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;In next post, I&amp;#8217;m going to give a simple but runnable example to demonstrate the benefits of applying jqMVC# architecture.&lt;/p&gt;&lt;img src="http://www.cnblogs.com/teddyma/aggbug/1761122.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/teddyma/archive/2010/06/20/1761122.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/teddyma/archive/2010/04/25/1720752.html</id><title type="text">Discuss some new features in WCF 4.0</title><summary type="text">This article discusses my understanding of some new features in WCF 4.0 and how NIntegrate could work with WCF 4.0.</summary><published>2010-04-25T13:28:00Z</published><updated>2010-04-25T13:28:00Z</updated><author><name>Teddy's Knowledge Base</name><uri>http://www.cnblogs.com/teddyma/</uri></author><link rel="alternate" href="http://www.cnblogs.com/teddyma/archive/2010/04/25/1720752.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/teddyma/archive/2010/04/25/1720752.html"/><content type="html">&lt;p&gt;This article discusses my understanding of some new features in WCF 4.0 and how &lt;a href="http://nintegrate.com/" target="_blank"&gt;NIntegrate&lt;/a&gt; could work with WCF 4.0.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;1. Configuration Based Activation&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;#8220;Configuration based activation&amp;#8221; means, instead of creating a physical .svc file for a WCF service be hosted in IIS, you could now set the equivalent configuration in the new &amp;lt;serviceActivations&amp;gt; element in &amp;lt;serviceHostingEnvironment&amp;gt; configuration section in the Web.config file.&lt;/p&gt; &lt;p&gt;It is an alternative of physical .svc files, and enables centrally management for the hosted WCF service addresses of a WCF host application. But it still does not support programmatically load the configuration of the &amp;lt;serviceHostingEnvironment&amp;gt; configuration section, which means, it is still&amp;nbsp; impossible to store them in a central configuration store, such as in database, for all your WCF host applications.&lt;/p&gt; &lt;p&gt;Remind you that, since even before WCF 4.0, there is one possible but limited approach to host WCF services in IIS without physical .svc files and even centrally store the addresses in anywhere, such as in database, which is through &amp;#8220;&lt;a target="_blank" href="http://msdn.microsoft.com/en-us/library/system.web.hosting.virtualpathprovider.aspx"&gt;VirtualPathProvider&lt;/a&gt;&lt;strong&gt;&lt;/strong&gt;&amp;#8221;. The limitation is it supports only HTTP bindings. In some previous version of &lt;a href="http://nintegrate.com/" target="_blank"&gt;NIntegrate&lt;/a&gt;, I used to provide one custom VirtualPathProvider implementation to manage WCF service addresses in database and deployment centrally. But I removed it in later versions of &lt;a href="http://nintegrate.com/" target="_blank"&gt;NIntegrate&lt;/a&gt; because it is not very hard to be implemented by yourself. Here I just advise you that you may want to consider this approach especially when your WCF application is RESTful only or uses HTTP bindings only.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;2. System.Web.Routing Integration&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;#8220;System.Web.Routing integration&amp;#8221; allows you to host a WCF REST service that responds to URIs without an extension easier. Even without System.Web.Routing, it is also possible to &amp;#8220;host a WCF REST service that responds to URIs without an extension &amp;#8221;, although with routing, it becomes much easier. Most of you should begin familiar with the routing ability of ASP.NET since the ASP.NET MVC framework. It is really attractive and helpful when you widely use WCF to implement RESTful services.&lt;/p&gt; &lt;p&gt;But do you even think &amp;#8220;widely use WCF to implement RESTful services&amp;#8221; is a good idea? OK, there are debates. Personally I&amp;#8217;ll not use WCF to implement RESTful services, even now in WCF 4.0, there are lots of improvements for RESTful WCF services. A better way should be &amp;#8220;use ASP.NET MVC 2 to expose RESTful services&amp;#8221;. I&amp;#8217;ll discuss more below when discussing the new features WCF 4.0 provided for RESTful WCF services.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;3. Routing Service&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;First of all, please realize that &amp;#8220;The Routing Service does not currently support routing of WCF REST services&amp;#8221;. &amp;#8220;Routing service&amp;#8221; here means a new predefined WCF service in WCF 4.0 which implements the &lt;a target="_blank" href="http://www.soapatterns.org/intermediate_routing.asp"&gt;Intermediate Routing pattern&lt;/a&gt;, generically supports routing SOAP based WCF service messages to their destinations based on a set of customizable rules. This pattern is an important enterprise SOA design pattern which solves the problem that, for large and complex service composition, it is difficult to design service contracts for all possible runtime scenarios in advance.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;4. Support for WS-Discovery&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;The Service Discovery feature enables client applications to dynamically discover service addresses at runtime in an interoperable way through WS-Discovery specification. The WS-Discovery specification is an &lt;a target="_blank" href="http://en.wikipedia.org/wiki/Organization_for_the_Advancement_of_Structured_Information_Standards"&gt;OASIS&lt;/a&gt; standard, which means it is possible to exchange metadata with 3rd party WS-Discovery implementation, although I believe most people just needs the ability to discover services at runtime. &lt;/p&gt; &lt;p&gt;&lt;a href="http://nintegrate.com/" target="_blank"&gt;NIntegrate&lt;/a&gt; provides a programmatic object model for hosting WCF services, and also for client to discover WCF services at runtime easily. So for the ability of discover services, it should be more flexible than WS-Discovery. But since the core of the the WCF 4.0 WS-Discovery implementation &amp;#8211; the discovery endpoint and behavior classes are standard WCF endpoints and behaviors, it could be used together with &lt;a href="http://nintegrate.com/" target="_blank"&gt;NIntegrate&lt;/a&gt; to give you the benefits of both.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;5. WCF REST Enhancement&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Fairly speaking, WCF 4.0 enhanced much a lot for RESTful services: output caching, dynamic response format &amp;amp; JSONP support, error handling and routing without extension. In WCF 3.5 where without the WCF 4.0 new features, using WCF to implement RESTful services is not easy. But there are some official &amp;amp; 3rd party extensions providing RESTful features such as output caching, JSON &amp;amp; JSONP response and routing without extension. Technically, WCF 4.0 makes all these features be official and easier to use, but it is still not enough reason to use WCF to implement RESTful services for me. Personally, I prefer to use ASP.NET MVC 2.&lt;/p&gt; &lt;p&gt;Below are the reasons I like ASP.NET MVC 2 more than WCF for implementing RESTful services:&lt;/p&gt; &lt;p&gt;&lt;strong&gt;1) Configuration and deployment cost&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Although in WCF 4.0, more predefined standard endpoints make it easier to use. It still needs configuration, and also needs deployment effort.&lt;/p&gt; &lt;p&gt;In ASP.NET MVC 2, no special configuration and deployment effort for each RESTful service is required.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;2) Output caching&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;WCF 4.0 has enhanced a lot here, but ASP.NET MVC 2 still has the maximal flexibility &amp;amp; extensibility for output caching.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;3) JSON &amp;amp; JSONP&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;For JSON response, ASP.NET MVC 2 is easier to be integrated with 3rd party serializers such as JSON.NET. For JSONP support, with the &lt;a target="_blank" href="http://www.cnblogs.com/teddyma/archive/2010/03/06/1679982.html"&gt;JSONPModule&lt;/a&gt; provided by &lt;a href="http://nintegrate.com/" target="_blank"&gt;NIntegrate&lt;/a&gt;, any JSON (XML response could be automatically converted to JSONP soon in next release) response services could automatically have the cross domain AJAX ability without any additional effort.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;4) Error Handling&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Error handling in WCF RESTful services is still limited&amp;nbsp; by the WCF FaultException design, while in ASP.NET MVC 2, it is much more flexible and easy to control.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;5) Routing&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;You smile when I try to compare the routing ability of WCF and ASP.NET MVC 2.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;In summary&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Any of the enhancement in WCF 4.0 for RESTful services is not as flexible and powerful than in ASP.NET MVC 2, and even leverages from ASP.NET MVC by design. Give me a reason to use WCF RESTful services then?&lt;/p&gt;&lt;img src="http://www.cnblogs.com/teddyma/aggbug/1720752.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/teddyma/archive/2010/04/25/1720752.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
