We have this big application known as the app with the sprocs, it has been a source for quite a lot of blog fodder already. The app has a long history; it originally started as a classical 2-tier Client Server application. A Delphi frontend running against a sql server backend. A lot of the business logic was (and still is) in stored procedures. My first shot was converting it to a web application. Armed with Visual Studio 2003 and sql reporting services we got it up and running. Originally intended to bridge a couple of years till the "big centralized app to cover all needs" was going to take over. As usual such an information system never gets further than the meeting table our app will live for many more years and will require quite a lot more updates.
The main emerging problem was that's it's still based on ASP.NET 1.1. The newer versions of the .net framework have a lot of nice new things to offer; especially asp.net is so much better than 1.1. But my main problem with 1.1. is the tooling. As you might remember the web page editor in VS 2003 is a nightmare. There is Resharper and nunit for VS 2003 but Resharper completely pales compared to the 2005 version. So it would be great to convert the app to ASP.NET 2.0 before doing the next update. When VS 2005 came out I had done a test conversion. Which just didn't work. We decided to give it another shot. The goal was to move to asp.net 2.0 (moving from 2.0 to later version is no big deal) and do a full Resharper code review.
In the end it all worked out, now everything reads green. In the process I met several small glitches worth mentioning
Components and Datasets
The database is wrapped up in an assembly. Database access is done using loads and loads of TableAdapters grouped on Components and data is published in typed datasets. All very much 1.1 style, in the days I built it that was a quite logical sequel to the Delphi datamodules. With a far better implementation. Converting this assembly to 2.0 went without any problems. Resharper helped me to tidy up the code and fixing some obsolete methods.
But now new functionality will be based on plain domain objects and I am going to leave the actual access to the database to nHibernate. The nice things is that this approach can co-exist with the old-school dataset style. The app doesn't care whether it's data is in a dataset object or in a plain domain object. The database doesn't care whether the sql is issued by a tableadapter or by hibernate. But the application developer, me, does care how I can organize the logic of my application.
Convert to web app
My first attempts to convert the app had been in the days prior to VS 2005 sp1. In an 1.1 app all web pages were in one assembly; in the first version of asp.net 2.0 every page had it's own assembly. Converting an app meant ripping it apart. My app died in that process. Since 1.1. the single assembly app is back. Now VS will convert your 1.1. app into a single assembly app.
But it doesn't always convert your web pages right. In 2005 the class behind a web page is split in 2 partial classes, one with the generated code and one with your code. This split is not (always ?) done on the converted pages. You have to convert each page by hand. To do this right click the page in the solution explorer and choose convert to web application. David has a very nice post with good screenshots demonstrating this.
SmartNavigation
There are several ideas what smart navigation actually stands for. One has to do with focus and scroll position of a page over postbacks. The other with the history of pages in the browser. Without smartnavigation set every roundtrip of a single page is added to the browser history. With smartnavigation set every discrete page visit enters the history only once. Our app, being used for a lot of data entry, does a lot of roundtrips; so it's pretty important for smart navigaton to work right.
In 1.1 SmartNavigation was a property of the Page class. Which had it's flaws. In 2.0 the property is considered obsolete; the members MaintainScrollPosition and the SetFocus are suggested. These do what their name suggest. But the thing we needed smartnavigation for seemed to be forgotten. To keep the history of the pages visited clean now requires a setting in the web.config
<system.web>
<pages smartNavigation="true"></pages>
Control ID
Every webcontrol on a page has an Id. You set the Id property but there is also the generated Id which is used when the webpage lives in the browser and in the form variables when the page posts back. To guarantee an unique id it is generated from the id of the control and the grid, user control or panel the control is in. You need this Id on several occasions. One is to find out which control was responsible for the postback. The good thing is that you calculate the id by hand, the bad thing is that the separator for the parts of the name has changed. In 1.1 it was a :, in 2.0 it is a $. In 1.1 a control id was something like DataGrid1:_ctl1:_ctl3, in 2.0 DataGrid1$_ctl1$_ctl3.
Client side script
Injecting client side script from the server requires the RegisterClientScript method. The 1.1 signature is considered obsolete, the new version expects a type in an extra parameter. It is hard to find any solid background information on this. What works well is passing the type of the usercontrol or page class which is injecting the script.
Our client side script wrote some content in textboxes. When the page was posted back the server side code could use these values. In 2.0 these values are no longer passed into the textbox controls. To get to any updates done by script you have to read these yourself from the form variables in the request. Here you can use the ClientId property of the control containing the textbox to get the textbox Id.
So instead of
TextBox txb = (TextBox)Panel1.FindControl("TextBox" + i);
if (txb.Text == "X")
result += match;
You now have to code
if (Request.Form[string.Format("{0}:TextBox{1}", ClientID, i)] == "X")
result += match;
Loading CSS and images
The app uses forms authentication to control access. Which forces an user to successfully log on before (s)he can access a page. In 1.1 this perfectly shields off all aspx pages. In 2.0 this also shields of other content like images and stylesheets. The unauthenticated user is redirected to the login screen. After conversion that no longer showed the app's logo (an image) nor applied any style. Fixing required an extra entry in the web.config.
</appSettings>
<location path="images">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
<system.web>
The images directory contains the logo and the stylesheet. Everybody may see that.
Winding down
Having fixed these things I'm quite happy with the conversion. Now I can release the full potential of Resharper to do some big and needed refactorings. And the HTML editor in VS >= 2005 is so much nicer. So far the working of the pages in the app are based on the webforms API. As the Id fiddling showed this is not the easiest API. But it does work well; when you understand it. But I just can't wait to move on to .NET 3.51 and use asp.mvc for the new stuff.