I have a web application that I have been tinkering with for years – about 6 years; it’s built in ASP.NET Web Forms with VB. Every so often, I jump into this app and create some new features or fix some bugs, and over the past few years I’ve been itching to get it converted over to C# and ASP.NET MVC. But the application has about 100 screens (distinct views – often a single ASPX will have multiple screens) and there are tens of thousands of lines of Web Forms event-driven code, so I’ve been intimidated by the idea of a rewrite. This weekend though, I wanted to integrate a fancy new UI design for the application – one that is so drastically different from the old that I will have to touch every screen. With this ahead of me, I decided to embark upon finding a way to start a migration to the new and fun style of web application development: ASP.NET MVC with C# and the Razor syntax.
Existing Application Characteristics
Again, the existing code is Web Forms and it’s your average Joe’s Web Forms coding style: event-driven forms with <asp:Placeholder> tags, stateful <asp:Textbox> controls, Session usage, Forms Authentication, etc. I would bet that this application is representative of how 95% of developers created their Web Forms apps.
- Project Type: Web Application Project (Web Forms)
- Language: VB (although other referenced projects such as the data layer are in C#)
- Forms Authentication
- Reliant upon Session state
- Standard, event-driven Web Forms implementation
- Lots of <table> tags and otherwise poor HTML design
- Virtually no AJAX or other client-side script (clue: the app references jQuery 1.2.6)
- Hosted in IIS, even locally during development
Desired Characteristics for New Development
As I create new screens in the application, I want to migrate to a new setup. Just as I’ve opportunistically deleted Stored Procedures over the last few years, replacing them with LINQ-based code, I want to gradually shift away from VB/Web Forms into the new world.
- Project Type: ASP.NET MVC3 Application
- Language: C#
- View Engine: Razor (cshtml)
- Many fewer <table> tags (although I think they still have their time and place) and a better HTML design throughout
- A lot more client-side script based on the latest and greatest in the jQuery world
Ground Rules
Because I have a pretty large existing application, there were some ground rules for the introduction of MVC.
- Existing screens must continue to work as-is
- New screens recognize the same auth token as the existing app
- New screens created using C# controllers with cshtml (Razor) views
- All of the awesome MVC 3 tooling needs to work when working in the MVC bits
- Controllers will need to access the same Session state used by the legacy Web Forms screens
- The MVC app should remain sanitized, free of artifacts specific to this strange legacy hybrid setup. Perhaps one day everything will get migrated over… if so, I should just be able to cut the cord and have a clean MVC app.
Things I tried that ultimately failed
While the end solution that I’ll illustrate in a moment is insanely simple, it wasn’t the first thing I tried. Along the way, I considered a few other approaches that all failed.
- Having the MVC application as a “sub web” where a new Application is hosted under either a physical or virtual directory
This ultimately failed because I couldn’t get session state shared between the two applications. I went as far as starting to move session state to SqlServer model, but decided that was just too drastic of a change. - Compiling the MVC application into the bin folder of the Web Forms application
I didn’t go far with this because I realized I have two global.asax files, and this just wasn’t going to work for several other reasons too. - Creating the MVC application as an entirely new site in IIS, and using a subdomain to differentiate
I considered this but didn’t even start, because A) the session state issue would still be there, and B) any SSL certificates would be invalid.
Super Simple Solution
I can’t decide if I should be upset that I didn’t come up with the end solution right off the bat. It’s not entirely obvious, but man was it simple.
- Create a new MVC 3 Web Application project as a sibling to the existing Web Forms application. This will yield a project structure like this:
- Open the Global.asax.cs file in the new MVC application and edit the routes.
- Add a fake folder name as a prefix to your controllers, so that no routes ever conflict with existing pages. If you are worried about permalinks, consider how you will deal with this long term. I chose the App folder as my prefix.
- Add the .aspx extension to your controllers. This is only needed if hosting in a “Classic Mode” IIS application pool.
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // Route name "App/{controller}.aspx/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults ); }
- Build your new C# project
- Add a reference to this new C# project from within the Web application
- Open the Global.asax.vb file in the existing Web application
- Change the Inherits statement from HttpApplication to point to the MvcApplication class within the new MVC application.
- Delete the existing Application_Start method from the Global.asax.vb to avoid the name conflict. If you have existing code in there, you can make the method in the MvcApplication class virtual, and then override it and call the base method within the override.
Now, your code is ready to roll, but we have a little more work to do. If you try browsing out to an MVC route within your Web Forms project, you’ll get a nasty error like this:
This is happening because the View that the Home controller’s Index action is trying to render cannot be found. The Views folder is within the MVC application, not the Web Forms (running) application, so that makes sense. All we need to do is find a way to make the MVC Views folder available within the Web Forms app. Let’s do that… I am using IIS on the local machine to address the problem. I suspect the same can be done with IIS Express, but I tried creating the virtual directories manually within the applicationhost.config file and it didn’t work.
- Prerequisite: You need to have IIS installed and working on your machine
- Open the project properties for the Web Forms application and go to the Web tab
- Change the “Servers” option to “Use a Local IIS Web server” and turn off “Use IIS Express” and click the Create Virtual Directory button
Now we need to go add a couple of virtual directories to the Web Forms app, one for the Views folder and one for Content. These are folders used within the MVC application that need to be rendered from the expected path but within the Web Forms application.
- Open IIS from your start menu
- Navigate to the IIS Application that was created for the Web Forms application
- Right-click and Add Virtual Directory. Name it “Views” and select the Views folder from the MVC application’s folder as the target.
- Create another Virtual Directory for “Content” in the same way, selecting the Content folder from the MVC application.
At this point, everything is fully configured. But something that kept tripping me up is that I need to rebuild the solution after updating IIS in order for ASP.NET to pick up the configuration changes. So go back into Visual Studio and rebuild your solution. You’re now done.
Now you can browse back to the same URL as before and see the marvelous Index page.
Review
While there are a bunch of steps involved to get this going, it really is simple once you set back and review.
- Edit the routes in your MVC application so that they can work within the hosting application
- Add a reference to the MVC application from the Web Forms application
- Make the Web Forms application derive from the MVC application within Global.asax
- Using IIS, create virtual directories for the MVC Views and Content folders so that they can be found within the Web Forms application
- Rebuild the solution any time you reconfigure the virtual directories in IIS – this kept tripping me up, making me think the solution was flawed, but with remembering this step, it’s working great
So far, I’m thrilled with the setup. I’ve already migrated some screens over to MVC, but I am able to have the two working together really seamlessly.