Hanselminutes.com:使用Razor将具有5年历史的VB.NET WebForms应用程序重写为ASP.NET Web Pages应用程序

I'm planning on updating the Hanselminutes.com website to look more modern and look more like this newly redesigned blog. Until then, one of the things I wanted to try was to update the application with some simpler code. The Hanselminutes podcast website is running the same VB.NET WebForms application it was on the day I started the show in January of 2006 (almost 300 episodes ago!)

我正计划更新Hanselminutes.com网站,使其看起来更现代,更像这个新近重新设计的博客。 在那之前,我想尝试的事情之一就是使用一些更简单的代码来更新应用程序。 Hanselminutes播客网站运行的VB.NET WebForms应用程序与我在2006年1月开始表演的那一天(差不多300集!)

Sure, the app runs fine and there's no real reason to update it, but I wanted to see how quickly I could do it and if the code felt cleaner. I'll probably do a post where I update this .NET 1.1 WebForms app to .NET 4 and show how to maintain it. In this case, I'm updating it to Razor just because it sounded like fun. No other reason.

当然,该应用程序运行良好,没有真正的理由对其进行更新,但是我想看看我能以多快的速度完成更新,以及代码是否更干净。 我可能会发表一篇文章,将这个.NET 1.1 WebForms应用程序更新为.NET 4,并说明如何维护它。 在这种情况下,我将其更新为Razor只是因为它听起来很有趣。 没有其他原因。

There's a few requirements I need to make sure I don't miss:

我需要确保没有错过一些要求:

  • I'd like a new URL scheme. Currently showid is the database, not the logical "show id." Rather than

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

  • I can't break any existing links, so I want to HTTP 301 redirect from the old style to the new style.

    我无法破坏任何现有链接,因此我想HTTP 301从旧样式重定向到新样式。
  • I'll keep the look and feel now, but update it after.

    现在,我将保持外观,但之后再进行更新。
  • Folks shouldn't notice the difference.

    人们不应该注意到差异。
  • I need to use the existing wacky 2004 database, it's tables and stored procedures because all of Carl's existing backend administration systems feed into it.

    我需要使用现有的古怪的2004数据库,它的表和存储过程,因为所有Carl的现有后端管理系统都可以使用该数据库。

But, it's such a basic application, it's basically a bunch of for loops. So, here's what I did tonight.

但是,这是一个基本的应用程序,基本上是一堆for循环。 所以,这就是我今晚所做的。

UPDATE: Check out the updates below as I was using inline SQL when there was no reason to! Very cool stuff in the Massive micro-ORM that I wasn't taking advantage of. Rob Conery has done a post as a result of my foolishness.

更新:在没有理由的情况下,请查看下面的更新,因为我正在使用内联SQL! 我没有利用的Massive micro-ORM中非常酷的东西。 由于我的愚蠢,罗伯•康纳里(Rob Conery)担任了职务

导入布局 (Importing the layout)

I started in WebMatrix, although I could have just as easily used Visual Studio. I created a new "Starter Site." I took the existing ASP.NET WebForms application's Hanselminutes.master and copy/pasted it into the new ASP.NET Web Pages _SiteLayout.cshtml.

我从WebMatrix开始,尽管我可以像使用Visual Studio一样容易。 我创建了一个新的“入门站点”。 我使用了现有的ASP.NET WebForms应用程序的Hanselminutes.master,并将其复制/粘贴到新的ASP.NET Web页_SiteLayout.cshtml中。

I then added a RenderBody() call where my WebForms application's master page had a ContentPlaceholder:

然后,我添加了一个RenderBody()调用,其中我的WebForms应用程序的母版页具有ContentPlaceholder:

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

I added in the existing CSS and Scripts and that got me here. A "hello world" equivalent:

我添加了现有CSS和脚本,这使我到了这里。 与“ hello world”等效:

Then, I copied the Default.aspx ASP.NET WebForms code into Default.cshtml. This ASPX file has lots of server side WebForms controls like this:

然后,我将Default.aspx ASP.NET WebForms代码复制到Default.cshtml中。 此ASPX文件具有许多服务器端WebForms控件,如下所示:

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

But since Web Pages doesn't process any of those, they are literally ignored and sent across as if they were HTML markup. They aren't valid tags so the browser ignores them. That gets me here. More is laid out but there's no data behind it, of course.

但是由于网页不处理任何这些网页,因此它们实际上会被忽略并像HTML标记一样发送出去。 它们不是有效的标记,因此浏览器将忽略它们。 那把我带到这里。 布局更多,但是背后没有任何数据。

 

Next, I need to get to the data.

接下来,我需要了解数据。

获取数据 (Getting at the Data)

I can get at the data in a number of ways, but I like lightweight and easy so I'm using Rob Conery's "Massive" micro-orm. It's 500 lines of code, so that's nice.

我可以通过多种方式获取数据,但是我喜欢轻巧易用,因此我使用的是Rob Conery的“大型”微型Orm 。 这是500行代码,所以很好。

UPDATE: Rob Conery pointed out that everyone thinks that Massive and micro-ORMs like it require inline SQL. That they just turn table rows into objects. Turns out I was using Massive in an OK way, but not the easiest way. Note below as I've added before and after details.

更新: Rob Conery指出,每个人都认为像Massive和micro-ORM一样需要内联SQL。 他们只是将表行变成对象。 原来我以一种可以的方式使用Massive,但不是最简单的方式。 请注意以下内容,因为我之前和之后都添加了详细信息。

There's a good discussion of WebMatrix.Data, WebMatrix.Data.StronglyTyped, Massive, and Simple.Data over at Mikesdotnetting. They are all very similar. Use the one that makes you happy. Also look at Dapper from Sam Saffron and friends and check out my Massive vs. Dapper Podcast.

在Mikesdotnetting上对WebMatrix.Data,WebMatrix.Data.StronglyTyped,Massive和Simple.Data进行了很好的讨论。 它们都很相似。 使用使您快乐的那一种。 还可以查看Sam Saffron和朋友们的Dapper,查看我的Massive vs. Dapper Podcast

The easiest page to do first is the Archives page. It's a for loop of links, so a good place to get started.

首先要做的最简单的页面是“存档”页面。 这是链接的for循环,因此是上手的好地方。

In WebForms the archives uses a DataGrid like this:

在WebForms中,存档使用如下所示的DataGrid:

<asp:DataGrid ID="dgArchive" ForeColor="Black" GridLines="None" CellPadding="2" BackColor="#EFEFDA"
BorderWidth="1px" BorderColor="Khaki" Width="100%" runat="server" PagerStyle-Visible="false"
AutoGenerateColumns="False" ShowFooter="false" DataKeyField="ShowID" ShowHeader="False"
PageSize="999">
<FooterStyle BackColor="Tan"></FooterStyle>
<AlternatingItemStyle BackColor="#F4F7E0"></AlternatingItemStyle>
<Columns>
<asp:BoundColumn DataField="ShowNumber" Visible="True"></asp:BoundColumn>
<asp:HyperLinkColumn ItemStyle-ForeColor="Black" DataNavigateUrlField="ShowID" DataNavigateUrlFormatString="default.aspx?showID={0}" DataTextField="ShowTitle"/>
<asp:BoundColumn DataField="ShowID" Visible="False"></asp:BoundColumn>
<asp:BoundColumn DataField="DatePublished" HeaderText="Date" DataFormatString="{0:yyyy-MMM-dd}">
</asp:BoundColumn>
</Columns>
</asp:DataGrid>

There's some color alternating going on, hard-coded colors, kind of cheesy, and some columns for the data. It's a half-decade old. I didn't write it. But as developers we inherit old code all the time.

有一些交替进行的颜色,硬编码的颜色,俗气的颜色和一些数据列。 已经有十年历史了。 我没写但是作为开发人员,我们一直都在继承旧代码。

The Massive ORM needs a connection string in the web.config, so I'll put one there.

Massive ORM在web.config中需要一个连接字符串,因此我将在其中放置一个。

<connectionStrings>
<add name="hanselminutes"
connectionString="Data Source=yada yada yada"
providerName="System.Data.SqlClient" />
</connectionStrings>

We've got a table called "Shows," so I need to let Massive know about it.

我们有一个名为“ Shows”的表格,所以我需要让Massive知道。

using System;
using System.Collections.Generic;
using System.Web;

public class Shows : DynamicModel
{
//you don't have to specify the connection - Massive will use the first one it finds in your config
public Shows():base("hanselminutes")
{
PrimaryKeyField = "ShowID";
}
}

Next, I'll reuse the same SQL Query used in 2006, except now with Massive. I'll just see if I can get the titles first with inline SQL.

接下来,除了现在使用Massive之外,我将重用2006年使用的相同SQL查询。 我只是看看是否可以使用内联SQL首先获得标题。

@{
dynamic tbl = new Shows();
var shows = tbl.Query(@"SELECT ShowID, DatePublished, ShowTitle, Description, Enabled, ShowNumber
FROM Shows
WHERE Enabled = 1
ORDER BY DatePublished DESC");

foreach(var show in shows) {
@show.ShowTitle<br/>
}
}

Cool, that works. But I've got inline SQL here and it's, well, inline SQL. Rob points out that Massive will let you use the Jon Skeet-designed named parameter syntax such that I can just do this and the same result as the inline SQL! I'm such a goof for forgetting this.

太好了,那行得通。 但是我这里有内联SQL,也就是内联SQL。 Rob指出,Massive将允许您使用Jon Skeet设计的命名参数语法,这样我就可以执行此操作,并且获得与内联SQL相同的结果! 我真是忘了这个。

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

Ok, just to be clear, here is the whole thing again, except with smarter use of Massive.

好的,只是要清楚一点,这就是整个事情,除了更明智地使用Massive。

@{    
dynamic tbl = new Shows();
show = tbl.Find(Enabled: 1, orderby: "DatePublished DESC");
forch(var show in shows)
{
@show.ShowTitle<br/>
}
}

Is nice, yes? I'll flesh out the table. Yes, a <table/>. Cause, it's, like, a table.

很好,是吗? 我会把桌子上的肉充实。 是的,<table />。 因为,就像一张桌子。

<table id="archives">
@foreach(var show in shows) {
<tr>
<td>@show.ShowID</td>
<td><a href="/@show.ShowNumber">@show.ShowTitle</a></td>
<td>@show.DatePublished.ToString("yyyy-MMM-dd")</td>
</tr>
}
</table>

Notice the href="" for the show is the ShowNumber, not the ShowID. It doesn't point anywhere yet, but I'll figure it out later.

请注意,该节目的href =“”是ShowNumber,而不是ShowID。 它还没有指向任何地方,但是我稍后会解决。

Before they were doing some server side alternating color stuff. I need to update the CSS at some point, but since I'm redesigning soon, I'll just do it with jQuery on the client side, it's easier then the server-side if and gets the same result. Again, I'll change the template when I do the redesign.

在他们进行服务器端交替颜色处理之前。 我需要在某个时候更新CSS,但是由于我将很快进行重新设计,因此我将仅在客户端使用jQuery进行操作,如果和获得相同的结果,则比服务器端更容易。 同样,我将在进行重新设计时更改模板。

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

Now I've got this page done, without the links hooked up. Nice and easy.

现在,我完成了此页面,没有链接到链接。 好,易于。

Here is the whole Archives page so far:

这是到目前为止的整个“存档”页面:

@{  
Layout = "~/_SiteLayout.cshtml";
Page.Title = "The complete Hanselminutes podcast archive";

dynamic tbl = new Shows();
var shows = shows = tbl.Find(Enabled: 1, orderby: "DatePublished DESC");
}

<table id="archiveTable" width="100%">
@foreach(var show in shows) {
<tr>
<td>@show.ShowNumber</td>
<td><a href="/@show.ShowNumber">@show.ShowTitle</a></td>
<td>@show.DatePublished.ToString("yyyy-MMM-dd")</td>
</tr>
}
</table>

<script type="text/javascript">
$(document).ready(function() {
$('#archiveTable tr:odd').css('backgroundColor','#EFEFDA');
$('#archiveTable tr:even').css('backgroundColor','#F4F7E0');
});
</script>

I can refactor the data access into a helper class and generally tidy up, but you get the idea.

我可以将数据访问重构为助手类,并且通常进行整理,但是您明白了。

On the home page I want to get the most recent show first using using inline SQL, that is, the LAST show:

在主页上,我想使用内联SQL首先获取最新的节目,即LAST节目:

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

And then again using Massive's syntax:

然后再次使用Massive的语法:

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

At this point, it's easy to setup the home page by just sprinkling some Razor could around the original HTML template. I'm replacing <asp:Label> and <asp:Literal> with @lastShow.ShowID and things like this. Here I'm calling an existing Stored Proc and getting the filename and location then building an HTML5 <audio> element for all the MP3s.

此时,只需在原始HTML模板周围撒些Razor即可轻松设置主页。 我用@ lastShow.ShowID和类似的东西替换<asp:Label>和<asp:Literal>。 在这里,我要调用现有的存储过程并获取文件名和位置,然后为所有MP3构建HTML5 <audio>元素。

@{
var files = tbl.Query("GetFilesByShowID @0", lastShowID);
foreach(var file in files) {
var filename = file.WebFilePath + file.FileName;
if (filename.EndsWith(".mp3")) {
<audio width='200' height='30' controls='controls' preload='auto'>
<source type='audio/mp3' src='@filename' />
<!-- Flash fallback for non-HTML5 browsers without JavaScript -->
<object width='320' height='240' type='application/x-shockwave-flash' data='flashmediaelement.swf'>
<param name='movie' value='flashmediaelement.swf' />
<param name='flashvars' value='controls=true&file=@filename' />
</object>
</audio>
}
}
}

Then, just 20 minutes of changing a bunch of asp:Controls to @lastShow.Something syntax and I was on my way. It's not pretty, but it's identical in functionality to the existing WebForms version.

然后,仅用了20分钟就将一堆asp:Controls更改为@ lastShow.Something语法,我就在路上。 它虽然不漂亮,但是在功能上与现有WebForms版本相同。

更改网址 (Changing URLs)

The only thing left is to make it so my URLs look like http://hanselminutes.com/1 instead of http://www.hanselminutes.com/default.aspx?showID=2.

剩下的唯一事情就是使它成为我的URL看起来像http://hanselminutes.com/1而不是http://www.hanselminutes.com/default.aspx?showID=2

There's lots of ways to do this, but the simplest is to take advantage of the URL rewriting that's built into IIS7. While i'm in there, I'll add a total of three rules.

有很多方法可以做到这一点,但是最简单的方法就是利用IIS7内置的URL重写功能。 当我在那里时,我将总共添加三个规则。

仅有数字的微型网址 (Tiny URLs with just Number)

This rule will take /123 and rewrite (not redirect) to /?showNumber=123. Because I'm redirecting such a "greedy" URL, it's important that I used the constraint of \d+ to just get numbers.

此规则将采用/ 123并重写(而非重定向)到/?showNumber = 123。 因为我要重定向这样的“贪婪” URL,所以使用\ d +的约束来获取数字非常重要。

<rule name="number">
<match url="(\d+)" />
<action type="Rewrite" url="/?showNumber={R:0}" appendQueryString="false" />
<conditions logicalGrouping="MatchAny">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
</rule>
默认文档为/ (Default Document to /)

This rule will take /default.aspx and make it /.

此规则将采用/default.aspx并将其设置为/。

<rule name="Default Document" stopProcessing="true"> 
<match url="(.*?)/?Default\.aspx$" />
<action type="Redirect" url="{R:1}/" />
</rule>
删除.ASPX扩展名(Remove .ASPX extensions)

This rule will change things like /archives.aspx to just /archives.

此规则会将/archives.aspx之类的内容更改为/ archives。

<rule name="RewriteASPX">
<match url="(.*).aspx" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Redirect" url="{R:1}" />
</rule>

But what if someone visits a link with a database ID, NOT a ShowID, like

但是,如果有人访问带有数据库ID而不是ShowID的链接,该怎么办?

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

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

Given a URL like this, I'll look up the show by showID, get the correct show number and then just redirect to /280 (the right number, given an old database ID of 300):

给定这样的URL,我将通过showID查找节目,获取正确的节目编号,然后重定向到/ 280(正确的编号,给定的旧数据库ID为300):

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

With these small changes in the web.config, plus one line of code, I'll have the same URL structure as before with 301 Permanent Redirects to the new structure that the rest of the site uses.

通过对web.config中的这些小更改,加上一行代码,我将具有与以前相同的URL结构,其中301永久重定向到了网站其余部分使用的新结构。

I'll also add one last rule to remove www. from the front of the URL which will make tweeting shows by ID nicer:

我还将添加最后一条规则以删除www。 从URL的前面开始,它将通过ID更好地显示Twitter信息:

删除www。 并规范化域名 (Remove www. and canonicalize domain name)
<rule name="Cannonical Hostname"> 
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern="^hanselminutes\.com$" negate="true" />
</conditions>
<action type="Redirect" url="http://hanselminutes.com/{R:1}" />
</rule>

So when this is up, the current (as of this writing) show is http://hanselminutes.com/285, nice and clean.

因此,当结束时,当前(截至撰写本文时)的节目为http://hanselminutes.com/285 ,既干净又干净。

I haven't put the new site live yet, but I'll do some more testing and do it this week. It took about two hours while watching Breaking Bad on NetFlix to convert this five page VB.NET ASP.NET WebForms application to ASP.NET Web Pages, and I'm well positioned now to make database changes as well as change the template for a redesign. More importantly, I had fun.

我尚未启用新站点,但本周将进行更多测试。 观看NetFlix上的Breaking Bad大约花了两个小时,将这五页的VB.NET ASP.NET WebForms应用程序转换为ASP.NET Web Pages,现在我可以很好地进行数据库更改以及为重新设计。 更重要的是,我玩得很开心。

翻译自: https://www.hanselman.com/blog/hanselminutescom-rewriting-a-5-year-old-vbnet-webforms-application-as-an-aspnet-web-pages-application-with-razor

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值