Hanselminutes.com: 重写一个5年前的VB.NET WebForm 应用程序作为带有Razor的ASP.NET 网页应用程序


原文发表地址: Hanselminutes.com: Rewriting a 5 year old VB.NET WebForms application as an ASP.NET Web Pages application with Razor

原文发表时间: 27 Sep 2011 9:44 AM

我正计划更新Hanselminutes.com 网站,来了解更多现代的像这样重新设计的博客。到那时,我想尝试用一些更简单的代码来更新应用程序。Hanselminutes播客网站仍在运行VB.NET WebForms应用程序,就是从2006年1月我开始演示的那天(大约300集前)。

当然,该应用程序运行良好,没有足够的理由要更新它,但我想看一下,如果代码清理的更干净些,它能运行多快。我也许会写一篇文章讲述一下怎么将.NET 1.1 WebForms应用程序更新至.NET 4,以及如何维护它。在这种情况下,我将它更新为Razor仅仅因为它听起来更有趣。没有其他原因。

我需要确保一些需求没有被忽略:

· 我想要一个新的URL 方案。当前showid是数据库,不是逻辑的“show id。”更不是

· http://www.hanselminutes.com/default.aspx?showID=2
我希望看到
http://www.hanselminutes.com/1 ,在这里人们认为1即表示#1。

· 我不能破坏现有的任何链接,因此我想从旧的样式重定向HTTP 301到新的样式。

· 现在我想它的外观和感觉,但稍后会更新它。

· 用户不应该注重这些差别。

· 我需要使用现有的奇怪的2004数据库,它是表格和储存过程,因为所有的Carl 的现有后台管理员系统都进入它。

但是,这是一个基本的应用程序,它基本上是一些for循环。因此,这就是我今晚所做的事情。

更新:检查下面的更新,因为我使用的是嵌入式SQL,而事实上没理由这样做!Massive micro-ORM是很酷的东西,但我没有利用它。Rob Conery写了一篇博客作为我愚蠢的结果

导入布局

WebMatrix开始, 尽管我可以轻松的使用Visual Studio,创建一个新的“开始网站”。我使用现有的ASP.NET WebForms应用程序的Hanselminutes.master,然后复制/粘贴到新的ASP.NET Web 页面_SiteLayout.cshtml。

然后我添加了一个RenderBody()方法,调用WebForms应用程序母版页里的ContentPlaceholder。

   1: <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
   2: </asp:ContentPlaceHolder>

添加到现存的CSS和Scripts中,然后就出现了这个相当于“hello world”的页面:

clip_image002

然后,我复制了ASP.NET WebForms中的Default.aspx代码到Default.cshtml中。该ASPX文件有大量像这样的服务器端WebForms控件。

   1: <asp:HyperLink ID="hPermalink" Font-Bold="True" Text="Permalink" runat="server"/>

但由于Web页面并不处理 这些,实际上他们被自动的忽略和发送,就像被当作HTML标记语言一样。他们不是有效的标签,因此浏览器会忽略他们,这些让我意识到,更多的布局在了这里,但背后没有数据。

clip_image004

接下来,我需要获得数据。

获得数据

我可以用多种不同的方法获得数据,但我喜欢用轻量并简单的,于是我使用了Rob Conery的"Massive" 。这是500行的代码,非常完美。

更新: Rob Conery指出,大家认为Massive 和micro-ORMs像它那样需要嵌入SQL,他们就是将表格行转化为对象。结果我用了一种好的方式使用Massive,但不是最简单的方式。注意下面就是我之前和之后添加的细节。

这有一个关于在Mikesdotnetting 上的WebMatrix.Data,WebMatrix.Data.StronglyTyped, Massive, 和Simple.Data好的讨论。他们都很相似。你可以使用你喜欢的任意一种。也看一下来自Sam Saffron 和朋友们的Dapper 及查看我的Massive vs. Dapper 博客.

要做的最简单页面,首先是Archives 页面。这是一个for循环连接,因此是一个很好的开始。

在WebForms中文档使用一个像这样的DataGrid:

   1: <asp:DataGrid ID="dgArchive" 
   2: ForeColor="Black" GridLines="None" 
   3: CellPadding="2" BackColor="#EFEFDA"
   4: BorderWidth="1px" BorderColor="Khaki" 
   5: Width="100%" runat="server" 
   6: PagerStyle-Visible="false"
   7: AutoGenerateColumns="False" ShowFooter="false" 
   8: DataKeyField="ShowID" ShowHeader="False"
   9: PageSize="999">
  10: <FooterStyle 
  11: BackColor="Tan"></FooterStyle>
  12: <AlternatingItemStyle 
  13: BackColor="#F4F7E0"></AlternatingItemStyle>
  14: <Columns>
  15: <asp:BoundColumn DataField="ShowNumber" 
  16: Visible="True"></asp:BoundColumn>
  17: <asp:HyperLinkColumn ItemStyle-ForeColor="Black" 
  18: DataNavigateUrlField="ShowID" 
  19: DataNavigateUrlFormatString="default.aspx?showID={0}" 
  20: DataTextField="ShowTitle"/>
  21: <asp:BoundColumn DataField="ShowID" 
  22: Visible="False"></asp:BoundColumn>
  23: <asp:BoundColumn DataField="DatePublished" 
  24: HeaderText="Date" 
  25: DataFormatString="{0:yyyy-MMM-dd}">
  26: </asp:BoundColumn>
  27: </Columns>
  28: </asp:DataGrid>

有些颜色交替显示,硬编码的颜色,有点俗的及数据的一些列。我有五年没有改写过它了。但作为开发人员我们一直继承老的代码。

Massive ORM需要在web.config中有一个连接字符串,因此我会放一个在这.

   1: <connectionStrings>
   2: <add name="hanselminutes"
   3: connectionString="Data Source=yada yada yada"
   4: providerName="System.Data.SqlClient" />
   5: </connectionStrings>

我们已经得到了一个叫“Shows”的表格,我需要让Massive知道它。

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Web;
   4: public class Shows : DynamicModel 
   5: {
   6: //you don't have to specify the connection - Massive will use the first 
   7: one it finds in your config
   8: public Shows():base("hanselminutes") 
   9: {
  10: PrimaryKeyField = "ShowID";
  11: }
  12: }

接下来,我会重用2006中使用的SQL查询语句,除了现在用Massive。我就想看能不能先用嵌入式SQL获得标题。

   1: @{
   2: dynamic tbl = new Shows();
   3: var shows = tbl.Query(@"SELECT ShowID, DatePublished, ShowTitle, 
   4: Description, Enabled, ShowNumber 
   5: FROM Shows 
   6: WHERE Enabled = 1 
   7: ORDER BY DatePublished DESC");
   8: foreach(var show in shows) {
   9: @show.ShowTitle
  10: }
  11: }

很好,它起作用了。但我在这获得了嵌入式SQL,对,就是嵌入式SQL。 Rob指出Massive会让你使用Jon Skeet设计的名为参数语法,我可以只做这个而取得用嵌入式SQL的相同的结果!我忘记这个,真是愚蠢。

   1: show = tbl.Find(Enabled: 1, orderby: "DatePublished DESC");

好了,为了更清楚点,再一次描述这整件事,除了更灵活的使用Massive。

   1: @{ 
   2: dynamic tbl = new Shows();
   3: show = tbl.Find(Enabled: 1, orderby: "DatePublished DESC");
   4: forch(var show in shows) 
   5: {
   6: @show.ShowTitle
   7: }
   8: }

非常完美,对吗?我会填充表,对,一个<table/>。当然,它像一个表。

   1: <table id="archives">
   2: @foreach(var show in shows) {
   3: <tr>
   4: <td>@show.ShowID</td>
   5: <td><a href="/@show.ShowNumber">@show.ShowTitle</a></td>
   6: <td>@show.DatePublished.ToString("yyyy-MMM-dd")</td>
   7: </tr>
   8: } 
   9: </table>

注意显示的href=""是ShowNumber,而不是ShowID。这还没在其他地方指出,但我会在稍后指出。

在他们做一些服务器端颜色交替的工作之前。我需要更新CSS的有些方面,但由于我很快会重新设计,我将只在客户端使用jQuery,如果是服务器端会更加容易,而且获得同样的结果。再次,当我重新设计时我会修改模板。

   1: <script type="text/javascript">
   2: $(function() {
   3: $('#archives tr:odd').css('backgroundColor','#EFEFDA');
   4: $('#archives tr:even').css('backgroundColor','#F4F7E0');
   5: });
   6: </script>

现在我完成了这个页面,没有链接相连。完美而且简单。

clip_image006

这是目前为止整个的文档页面。

   1: @{ 
   2: Layout = "~/_SiteLayout.cshtml";
   3: Page.Title = "The complete Hanselminutes podcast archive";
   4: dynamic tbl = new Shows();
   5: var shows = shows = tbl.Find(Enabled: 1, orderby: "DatePublished DESC");
   6: }
   7: <table id="archiveTable" width="100%">
   8: @foreach(var show in shows) {
   9: <tr>
  10: <td>@show.ShowNumber</td>
  11: <td><a href="/@show.ShowNumber">@show.ShowTitle</a></td>
  12: <td>@show.DatePublished.ToString("yyyy-MMM-dd")</td>
  13: </tr>
  14: } 
  15: </table>
  16: <script type="text/javascript">
  17: $(document).ready(function() {
  18: $('#archiveTable tr:odd').css('backgroundColor','#EFEFDA');
  19: $('#archiveTable tr:even').css('backgroundColor','#F4F7E0');
  20: });
  21: </script>

我可以用helper类重写数据访问使代码更为整洁,但你可以自己决定。

在主页面上我想使用嵌入式SQL首先获得最近使用的大多数显示,那就是,最后的显示:

   1: lastShow = tbl.Single(@"SELECT Top 1 ShowID, DatePublished, ShowTitle, 
   2: Description, Enabled, ShowNumber 
   3: FROM Shows 
   4: WHERE Enabled = 1ORDER BY DatePublished 
   5: DESC").First();

然后重新使用Massive的语法:

   1: lastShow = tbl.Single(Enabled: 1, orderby: "DatePublished DESC");

在这点上,通过使用一些能够围绕原来的HTML 模板的Razor可以更容易地设置主页面。我用@lastShow.ShowID和其他像这样的东西替换<asp:Label> 和<asp:Literal>。这里我可以调用已有的存储过程,获得文件名和存储位置,然后为所有的MP3生成HTML5 <audio>元素。

   1: @{
   2: var files = tbl.Query("GetFilesByShowID @0", lastShowID);
   3: foreach(var file in files) {
   4: var filename = file.WebFilePath + file.FileName;
   5: if (filename.EndsWith(".mp3")) {
   6: <audio width='200' height='30' controls='controls' preload='auto'>
   7: <source type='audio/mp3' src='@filename' />
   8: <!-- Flash fallback for non-HTML5 browsers without JavaScript -->
   9: <object width='320' height='240' type='application/x-shockwave-flash' 
  10: data='flashmediaelement.swf'>
  11: <param name='movie' value='flashmediaelement.swf' />
  12: <param name='flashvars' value='controls=true&file=@filename' />
  13: </object>
  14: </audio> 
  15: }
  16: } 
  17: }

然后,仅用20分钟将一群asp:Controls转换为@lastShow.Something语法。它并不漂亮,但在功能上和现有的WebForms版本是一样的。

clip_image008

更改 URL

剩下的唯一一件事是使我的URL像http://hanselminutes.com/1 而不是http://www.hanselminutes.com/default.aspx?showID=2

有多种方法来做,但最简单的是利用生成IIS7的URL重写。在这里,我会添加三条规则。

只有带数字的微小URL

这项规则会将/123重写(非重定向)为/?showNumber=123。因为我重定向如此“贪婪” 的URL,我用\d+约束只获得数字,这很重要。

   1: <rule name="number">
   2: <match url="(\d+)" />
   3: <action type="Rewrite" url="/?showNumber={R:0}" appendQueryString="false" />
   4: <conditions logicalGrouping="MatchAny">
   5: <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
   6: </conditions>
   7: </rule>

默认文档为 /

这项规则会采用/default.aspx并将它设为/。

   1: <rule name="Default Document" stopProcessing="true"> 
   2: <match url="(.*?)/?Default\.aspx$" /> 
   3:  
   4: <action type="Redirect" url="{R:1}/" /> 
   5: </rule>

移除.ASPX 扩展名

这项规则会修改一些如/archives.aspx为/archives

   1: <rule name="RewriteASPX">
   2: <match url="(.*).aspx" />
   3: <conditions logicalGrouping="MatchAll">
   4: <add input="{REQUEST_FILENAME}" 
   5: matchType="IsFile" negate="true" />
   6: <add input="{REQUEST_FILENAME}" 
   7: matchType="IsDirectory" negate="true" 
   8: />
   9: </conditions>
  10: <action type="Redirect" url="{R:1}" 
  11: />
  12: </rule>

但如果有人使用数据库ID访问一个链接,而不是ShowID,如

http://hanselminutes.com/default.aspx?showID=300

给定一个像这样的URL,我会通过showID查找显示,获得正确的显示号然后重定向至/280(正确的数字,给定一个老的数据库ID为300):

   1: Response.RedirectPermanent("/" + lastShow.ShowNumber);

通过web.config中的这些小的改变,加上一行代码,我会获得和之前一样的URL结构,和剩下的网站使用的对新结构的永久重定向。

我也会添加最后一条规则来从URL前面移除www.,这会使通过ID显示tweeting更好。

移除www.和规范化的域名
   1: <rule name="Cannonical Hostname"> 
   2: <match url="(.*)" /> 
   3: <conditions logicalGrouping="MatchAll" 
   4: trackAllCaptures="false"> 
   5: <add input="{HTTP_HOST}" 
   6: pattern="^hanselminutes\.com$" negate="true" 
   7: /> 
   8: </conditions> 
   9: <action type="Redirect" 
  10: url="http://hanselminutes.com/{R:1}" /> 
  11: </rule>

因此当这个提交,目前的(像这篇文章写到的)显示为http://hanselminutes.com/285 ,干净完美

我还没有上线这个新网站,但我会在这周做更多测试。大概花了两个小时在NetFlix上查看Breaking Bad ,将这五个页面VB.NET ASP.NET WebForms应用程序转化为ASP.NET Web页面,而且现在我也被很好的定位,来做数据库和重设计模板修改。更重要的是,我获得了乐趣。


Comments (0)

Skip to main content