例如View和Layout(更ASP.NET的说法是Page与Master Page)之间共享同一个Model。更进一步,两者之间其实是多对多的关系,如果我们为每个ViewPage定义了强类型的Model,又如何应对同一个ViewPage套用不同ViewMasterPage的情况呢?这个问题最自然的解决方式便是使用接口。一个Model对象不可以有多个父类,但是完全可以实现多个接口。因此,我们往往为强类型的ViewMasterPage指定一个接口作为其泛型参数。从示例中您也可以发现,Site.Master的类型为ViewMasterPage<ISiteMasterModel>,而每个ViewPage的Model类型都实现了ISiteMasterModel接口。
使用ViewData的另一个坏处是必须使用字符串作为键进行访问。字符串是什么?是常量。分散在各处的常量是维护性的大敌,而使用ViewData则几乎无可避免地将字符串常量分散在控制器和视图两个地方——您可能会觉得,使用枚举不就解决这个问题了吗?如果这么做,相当于为每个视图定义不同的枚举类型,那么我们为什么不直接构造强类型的Model呢?
使用字符串作为键的另一个坏处是无法在编译期发现问题:如果您一不小心拼写错误了怎么办呢?您可能会觉得,原本aspx就必须到运行时才会动态编译,不过现在ASP.NET MVC的模板已经增加了相关的MSBuild Task,或者使用ASP.NET的预编译功能,都可以在编译时对视图进行检查了。这也是我建议大家使用aspx,而不是另一种DSL来构建视图的原因——aspx的周边支持实在是太丰富了。
不过在视图中,字符串常量并非只出现在ViewData的访问上。例如,使用ASP.NET MVC框架模板创建项目之后,Site.Master中生成导航栏链接的代码是这样的:
<li><%= Html.ActionLink("Home", "Index", "Home")%></li>
<li><%= Html.ActionLink("About", "About", "Home")%></li>
</ul>
代码使用了Html.ActionLink这个辅助方法生成一个导向至某个Action的链接,只可惜这里又出现了字符串参数表示的Controller名和Action名。事实上,如果您下载了ASP.NET MVC RC的源代码之后,能够发现其中的MvcFutures项目中包含了“强类型”的ActionLink辅助方法,于是我们的代码就能修改为如下模样(参见《非常好的实践》的示例):
<li><%= Html.ActionLink<HomeController>(c => c.About(), "About") %></li>
<li><%= Html.ActionLink<AccountController>(c => c.Register(), "Register") %></li>
<li><%= Html.ActionLink<AccountController>(c => c.List(1), "Admin") %></li>
这下我们便可以使用Lambda Expression这个强类型的表示方法来“告诉”ActionLink方法究竟该生成导向至哪个Action方法的链接。甚至我们可以在“调用”Action方法的同时指定其参数——例如最后一行代码,指定将查看“第一页”内容,而这些数据在交给URL Routing的配置后便能得到我们想要的URL。
MvcFutures中还定义了其他类似的辅助方法,例如UrlHelper中的辅助方法则会直接生成一个URL链接——而不是一个<a />元素。可惜目前MvcFutures中的这些辅助方法编写的并不完全“正确”,因为它直接把方法名作为Action的名称来使用,而ASP.NET MVC从某个预览版开始引入了ActionNameAttribute,可以为一个Action方法指定一个不同的Action名称。因此如果您使用ActionNameAttribute来改变Action名,则很可能您需要构建自己的辅助方法。不过这条准则依旧成立:使用强类型的表示法生成URL地址。
如果您要使用MvcFutures中的内容,则必须自行编译MvcFutures项目。有一点值得注意,如果您使用直接编译ASP.NET MVC RC解决方案所得到的Microsoft.Web.Mvc.dll,就会发现它依赖的是“System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”,而您开发时所使用的,也就是安装在系统中的程序集是“System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35”,两者不同自然难以兼容。
幸运的是,您只要手动修改一下MvcFutures项目的程序集引用就可以了,对您来说这一定不是问题。