Click here to Skip to main content
15,861,172 members
Articles / Web Development / ASP.NET

ASP.NET WebForms and ASP.NET MVC in Harmony

Rate me:
Please Sign up or sign in to vote.
4.85/5 (37 votes)
19 Aug 2009CPOL11 min read 217.8K   3.2K   106   36
Integrating ASP.NET MVC in existing ASP.NET WebForms applications.

Table of Contents

Screenshots

The simple demonstration in action. The first screenshot is served from the classic webform with the URL: http://localhost:41650/Src/ClassicWebforms/Home.aspx.

The second is served from the MVC application by the "Home" Controller's Index" action. The URL is: http://localhost:41650/src/Home/Index.

The web form page

The ASP.NET MVC page:

The web form page

Introduction

The ASP.NET MVC bug is quickly catching up with many developers because of the cool features of the framework like:

  • Convention over configuration
  • Don't repeat yourself (a.k.a. the DRY principle)
  • Pluggable architecture

The side effect of this is the framework supports Test Driven Development a.k.a. TDD. This article specifically talks about making ASP.NET MVC work with the current WebForms applications. This is not a tutorial for learning ASP.NET MVC. For learning ASP.NET MVC, refer to the "References" section.

The Need

Every solution should have a pertinent problem that every developer is trying to solve. Here are a few of the reasons why you might want to run both the project types together:

  • Web forms are very good at encapsulating view logic into components. You may be comfortable developing complex solutions using technology you already know.
  • The ASP.NET MVC pluggable architecture allows TDD. In some organizations, the code coverage policy may be very strict. And, what better way to fulfill this policy requirement better than writing tests?
  • You may be working with an existing complex ASP.NET application and may be in the migration phase. The migration needs to happen incrementally without impacting the existing functionality.

There is no code demonstration as the code is not central to the purpose of this article. The core of the article is the configuration activities which enables you to work with WebForms and MVC in harmony.

The Solution Overview

The ASP.NET MVC framework has a high degree of support for pluggability and extensibility. The crux of the ASP.NET MVC framework is the following three assemblies shown in the figure which extends the System.Web:

Core Assemblies

In addition to this, these assemblies work in medium trust server environments and are bin deployable. This essentially means that the assemblies do not need to be installed into the GAC (Global Assembly Cache). You can simply add a reference to the assemblies, and you are done.

The 4 Step Process

Integrating MVC in existing WebForms applications is a four step process as shown in the diagram below:

4 Step Process

Step 1 - Referencing the Required Libraries

The first step is to find the libraries that are required for ASP.NET MVC to work.

  • The System.Web.Mvc.dll assembly is stored in: [Installation Directory]\Microsoft.NET\ASP.NET MVC\Assemblies. Note: [Installation Directory] in this case is c:\[windows].
  • The System.Web.Abstractions.dll and System.Web.Routing.dll assemblies are stored in [Installation Directory]\Reference Assemblies\Microsoft\Framework\v3.5. Note: [Installation Directory] in this case is generally [%Program Files%].

The example in this article stores these files in the "Reference_Assembly" directory. Once added, you need to reference the three core assemblies in your application by using the Project/Add Reference dialog. Refer to the figure below:

Solution Structure

Note: The classic WebForms are located in the "ClassicWebForms" folder, and the ASP.NET MVC Views are located in the "Views" folder.

Step 2 - Creating the Necessary Directories

As mentioned earlier, the ASP.NET MVC follows certain conventions (doing things in a prescribed way). One of the core conventions of ASP.NET MVC is the naming of the directories where the project's Controllers and Views are kept.

For this example, we will use the directories "Controllers" and "Views" for storing the respective controllers and views.

We create the HomeController.cs and put it in the Controller directory in App_Code. In big solutions, the controller may belong to its own respective assembly. The HomeController view file Index.aspx is located in the Views/Home folder.

Step 3 - Updating the Web.config

The third step is to update the web.config file. Please refer to the following diagram to add the required assemblies:

Image 6

The first three assemblies are the ASP.NET MVC assemblies.

The next thing is to add a namespace reference to the system.web/pages section. Doing this allows access to the System.Web.Mvc helpers, System.Linq, and System.Collections Generics from the ViewPage.

Pages Configuration

The only namespaces that are required here are System.Web.Mvc, System.Web.Mvc.Html, and System.Web.Mvc.Ajax. But the other ones are pretty useful if you wish to work with LINQ/Generics.

Finally, you need to register UrlRoutingModule. This module is responsible for matching the URL being requested to the proper Route (Controller/Action).

Assembly Configuration

Step 4 - Registering the Routes

Finally, you need to map the routes in the Application_Start event of the Global.asax.

C#
public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        "Default",                      // Route name
        "{home}/{action}/{id}",         // URL with parameters
        new { controller = "Home", action = "Index", 
              id = "" }                 // Parameter defaults
    );
}

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);
}

IIS Deployment

Hosting an ASP.NET MVC web application: If you or your web hosting provider have access to your web server's settings, a wildcard script map can be created in order to have full routing support. A wildcard script map enables you to map each incoming request into the ASP.NET framework. Be aware that this option passes every request into the ASP.NET framework (even images and CSS files!), and may have performance implications. If you do not have access to the web server's settings, you can modify the route table to use file extensions. Instead of looking look like this: /Home/Index, URLs would look like this: /Home.aspx/Index. This way, no configuration of the web server is required. It is, however, necessary to make some modifications to the application's route table.

You do not have to configure anything if your IIS 7.0 server is operating in Integrated mode.

Creating a wildcard script map in IIS 7.0

Here is how you can enable a wildcard script map in Internet Information Services 7.0:

  1. Launch the IIS Manager.
  2. In the Connections tree-view, select an application.
  3. In the bottom toolbar, make sure that the Features view is selected.
  4. Double-click on the Handler Mappings shortcut.
  5. In the Actions window, click on the Add Wildcard Script Map button.
  6. Enter the path to the aspnet_isapi.dll file, which is usually located in: %windir%Microsoft.NETFrameworkv2.0.50727aspnet_isapi.dll.
  7. Enter the name ASP.NET MVC.
  8. Click on the OK button.

After doing this, any request for this specific web site will be executed by the ASP.NET engine.

Creating a wildcard script map in IIS 6.0

Here is how you can enable a wildcard script map in Internet Information Services 6.0:

  1. Launch the Internet Information Services IIS Manager.
  2. Right-click on a web site and select Properties.
  3. Select the Home Directory tab.
  4. Near Application settings, click on the Configuration button.
  5. Select the Mappings tab.
  6. Near Wildcard application maps, click on the Insert button.
  7. Enter the path to the aspnet_isapi.dll file, which is usually located in %windir%Microsoft.NETFrameworkv2.0.50727aspnet_isapi.dll.
  8. Uncheck the Verify that file exists checkbox.
  9. Click on the OK button.

After following these steps, any request for this specific web site will be executed by the ASP.NET engine.

Getting Visual Studio to Offer MVC Items

There is actually one difference between a native MVC web application project and your “upgraded” WebForms project. When you want to add a view template using the Add New Item dialog, Visual Studio won’t offer you an MVC View Page or any of the other MVC-specific items. And, when you right-click inside an action method, you don’t get the option to add a view. That’s because Visual Studio doesn’t realize you’ve got an ASP.NET MVC project.

To resolve this, you need to add a project type hint for Visual Studio. This is pretty simple if done with a few precautions.

Caution: Before venturing out for this, backup your project file (i.e., the one with the .csproj extension). If you edit the project file incorrectly, Visual Studio will be unable to open it.

In Solution Explorer, right-click your project name and choose Unload Project. Right-click the project name again, and choose Edit <yourproject>.csproj (replace the angled bracket content with your project name). You’ll now see the .csproj XML file. Find the <projecttypeguids> node which contains a semicolon-separated series of GUIDs, and add the following value in front of the others: {603c0e0b-db56-11dc-be95-000d561079b0};. Do not add any extra spaces or line breaks. If you don’t want to type in the GUID by hand, you can copy and paste it from the corresponding section of any valid ASP.NET MVC .csproj file you might have elsewhere. Save the updated .csproj file. Then, reload the project by right-clicking its name in Solution Explorer and choosing Reload Project.

If you get the error “This project type is not supported by this installation”, then either you have mistyped the GUID, or you haven’t installed the MVC Framework on your PC.

If you get the error “Unable to read the project file”, then simply click OK and choose Reload Project again. It seems to sort itself out, for reasons unknown to me.

You should now find that MVC-specific items appear in the Add New Item dialog, alongside the usual WebForms items, and you’ll be able to right-click inside an action method and choose Add View.

Migration Strategy

The following points should be taken care of while migrating an existing ASP.NET application to ASP.NET MVC:

  1. Plan the process. Do not migrate at one go. Take an area and do a migration.
  2. ASP.NET MVC2 (preview) allows applications to be created in "Areas". Areas provide a means of grouping controllers and views to allow building subsections of a large application in relative isolation to other sections.
  3. Design routes for your new areas.
  4. Migrate the views. WebForms applications have code-behind files, and this is discouraged in ASP.NET MVC. But in scenarios like this, I think you can keep the code-behind files and make the application work.
  5. As a practice, create controllers and add actions for each of the events that is raised in the view.
  6. Since abstraction is the key in the MVC design, you can write Unit Test cases as the functionality is being migrated.
  7. For Unit Tests, abstract away all concrete dependencies in the code-behind and move those to the controller. Some of the dependencies are Session, Cache, HttpContext (related methods), Repository etc.
  8. Once the test passes, use your good judgement whether you want to get away with the code-behind.
  9. Continue the process for the rest of the functionality.

Handling Legacy URLs

In case you are migrating your existing site to ASP.NET MVC, you may require to support the existing URLs that your customers may have bookmarked. There may be many reasons for this. Your customer may be a premium customer and you don't want' to make him unhappy just because you upgraded your site.

I was aware of this technique, but the example was adapted from Mike's website. So, due credit to him for this. Please find the referenced URL in the References section.

This can be done by creating a custom route. All routes derive from RouteBase. Derive your legacy route from RouteBase and override the GetRouteData(..) method. For example, say you have a URL http://yourdomain.com/profile.aspx?id=specialid. Here's a rudimentary example to understand the process:

C#
using System;
using System.Web;
using System.Web.Routing;

namespace CoolControls.Routes
{
  public class LegacyUrlRoute : RouteBase
  {
       public override RouteData GetRouteData(HttpContextBase httpContext)
       {
          const string status = "301 Moved Permanently";
          var request = httpContext.Request;
          var response = httpContext.Response;
          var name = "";
          var legacyUrl = request.Url.ToString();
          var newUrl = "";
          var id = request.QueryString.Count != 0 ? request.QueryString[0] : "";
          
          if (legacyUrl.ToLower().Contains("profile.aspx"))  
          {
                var rep = new ProfileRepository();
                var pro = rep.GetProfile(Convert.ToInt32(id));
                
                name = pro.Name;
                
                newUrl = "profile/" +  name;
                response.Status = status;
                response.RedirectLocation = newUrl;
                response.End();
          }
            return null;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, 
                                    RouteValueDictionary values)
    {
          return null;
    }
  }
}

The HTTP status code informs user agents (browsers and search engines) that the resource they are looking for has been moved to another location. It should make no difference to the existing links.

If the requested URL does not contain "profile.aspx", null is returned so that the UrlRoutingModule can continue to try to match the URL to other routes within the RouteTable.Routes collection.

One final task, and that is to register the LegacyUrlRoute in the application's RouteTable. That's done right at the beginning of the method in Global.asax:

C#
public static void RegisterRoutes(RouteCollection routes)
{
     ......
     routes.Add(new LegacyUrlRoute());
     .......
}

Now, if a request is made to an old URL, such as http://mysocialnetwork.com/profile.aspx?id= 101, it is automatically redirected to http://mysocialnetwork.com/profile/rajesh with the correct header sent to the user agent. You can check this through an HTTP debugger like Fiddler.

Points of Interest

The harmony between Web Forms and ASP.NET MVC wasn't as complicated as I thought. Overall, it was an excellent learning, and would hopefully be useful in some of the migration projects which I may do in future.

Future Articles

The next series of articles will focus on building an end-to-end application with supposedly best practices using a basic test driven approach.

Conclusion

Some of the readers may not agree with some points like the use of code-behind files in ASP.NET MVC. Even I am not in favor of it. But migration is one area where this has its place and leaves the choice with the developer/designer to migrate it based on need and time constraints. So, please don't take this as "word written on stone". Use your good judgment.

References

History

  • August 19, 2009 - Added support for legacy routes.
  • August 11, 2009 - Added Getting Visual Studio to Offer MVC Items in Menus.
  • August 7, 2009 - Added Migration Strategy.
  • August 6, 2009, 3:30 PM - Added IIS Deployment.
  • August 6, 2009 - Created the first version of the article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Founder Algorisys Technologies Pvt. Ltd.
India India
Co Founder at Algorisys Technologies Pvt. Ltd.

http://algorisys.com/
https://teachyourselfcoding.com/ (free early access)
https://www.youtube.com/user/tekacademylabs/

Comments and Discussions

 
QuestionHow to get VS to offer MVC items if website doesn't have .csproj file? (it's file based) Pin
Leonardo Crespo28-Oct-09 18:49
Leonardo Crespo28-Oct-09 18:49 
GeneralShort and Sweet !!! Pin
Khan Irfan19-Aug-09 18:40
Khan Irfan19-Aug-09 18:40 
GeneralRe: Short and Sweet !!! Pin
Rajesh Pillai20-Aug-09 2:32
Rajesh Pillai20-Aug-09 2:32 
GeneralVery Nice Rajesh Pin
Abhijit Jana15-Aug-09 20:45
professionalAbhijit Jana15-Aug-09 20:45 
GeneralRe: Very Nice Rajesh Pin
Rajesh Pillai15-Aug-09 20:57
Rajesh Pillai15-Aug-09 20:57 
QuestionWhat if I have no .csproj file? (File System based site) Pin
Scott Fraley15-Aug-09 13:45
Scott Fraley15-Aug-09 13:45 
AnswerRe: What if I have no .csproj file? (File System based site) Pin
Rajesh Pillai15-Aug-09 18:32
Rajesh Pillai15-Aug-09 18:32 
GeneralRe: What if I have no .csproj file? (File System based site) Pin
Scott Fraley17-Aug-09 13:10
Scott Fraley17-Aug-09 13:10 
Ok, well, I'm not sure if I missed something or what, but when I try to go to my newly created 'Home | Index.aspx' file, I get a 404 error.

When I start my app (in development mode), my URL typically looks like this: http://localhost:55787/PTP220/Default.aspx

So I thought ok, I need to go directly to the new page. So I backed it up and tried ...localhost:55787/PTP220/Home/Index.aspx and ...localhost:55787/Home/Index.aspx and still got 404 errors. Any chance you can tell me what I missed?


Thanks again for the great article,
Scott

--
Scott K. Fraley
scott dot k dot fraley at gmail dot com
http://www.linkedin.com/in/scottkfraley
http://profile.to/scottkfraley


Any comments or opinions are mine and mine alone. Smile | :)

GeneralRe: What if I have no .csproj file? (File System based site) Pin
Rajesh Pillai17-Aug-09 17:02
Rajesh Pillai17-Aug-09 17:02 
Generalmaking the 301 permanent redirect to the site home from custom route. Pin
Sagar Singh Biswakarma13-Aug-12 20:20
Sagar Singh Biswakarma13-Aug-12 20:20 
GeneralRe: What if I have no .csproj file? (File System based site) Pin
vrules21-Aug-12 20:27
vrules21-Aug-12 20:27 
GeneralNice work. Pin
Shivprasad koirala6-Aug-09 3:57
Shivprasad koirala6-Aug-09 3:57 
GeneralRe: Nice work. Pin
Rajesh Pillai6-Aug-09 4:40
Rajesh Pillai6-Aug-09 4:40 
GeneralMy vote of 1 Pin
Drazen Dotlic5-Aug-09 21:58
Drazen Dotlic5-Aug-09 21:58 
GeneralRe: My vote of 1 Pin
Rajesh Pillai5-Aug-09 22:45
Rajesh Pillai5-Aug-09 22:45 
GeneralRe: My vote of 1 Pin
laverrod10-Aug-09 22:51
laverrod10-Aug-09 22:51 
RantRe: My vote of 1 Pin
charleslbryant24-Aug-09 9:24
charleslbryant24-Aug-09 9:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.