Whenever we are starting with a Greenfield project, that is a new project from ground up, we prefer using the latest in the technology stack to the point where the team is comfortable with. However, Greenfield projects are pretty rare; enhancement and feature addition projects, often referred to as brown-field projects, are the way forward for existing applications in an Enterprise. In these projects, we are often saddled with predetermined technologies or frameworks and shoe horning the latest and greatest could mean un-necessary delays and instability. After all ‘don’t fix what’s not broken’ right?
Given this scenario, it’s common to have well established ASP.NET 2.0 Web Forms projects that need enhancements. If the enhancements are independent enough, as in creation of new Screens with new functionality, that is no way going to be ‘embedded’ in existing functionality. We can take a clean break and develop the new functionality in ASP.NET MVC by mixing and matching Web Forms with MVC.
Today we will take a look at the caveats, gotchas and the GUIDs we need to take care to make this possible.
Getting Started – Upgrading your Web Forms Solution
Let’s say our legacy application was built using ASP.NET 2.0 or 3.5 in Visual Studio 2010.First thing we would like to do is to upgrade the Solution to using Visual Studio 2012. I am using the VS Express edition here so that it makes sense to most of you who do not have a Professional or Ultimate edition.
To upgrade, first thing to do is to open the Solution in VS 2012. Unless you have very specific third party components that are strictly dependent on older versions of .NET Framework, the migration should be seamless and you should get a Migration Report similar to the one below
If you see your Solution Explorer now, the Project is all set up.
Rebuild the solution at this point to ensure everything is building fine.
Upgrading all the way to .NET 4.5
The default upgrade is to .NET 4.0. Let’s upgrade all the way to 4.5. To do this, right click on the Project folder above and select Properties. Under the Application Tab, find the Target Framework and set it to .NET Framework 4.5. It will warn that this requires closing and reopening of the solution. Go for it.Build again and run the project to see that everything is working fine.
Adding ASP.NET MVC Framework Components
Now let’s add MVC Framework support to our Application. Thanks to Nuget, this is a rather easy process. For those new to Nuget, read NuGet – The Visual Studio Sweetner.Right click on the solution and select Manage Nuget Packages. Select the Online node and search for ASP.NET MVC. The result should be similar to the following
Install the Microsoft ASP.NET MVC 4 package. Accept any additional license agreements and let the installation complete.
Next we have to install the following additional packages. We can do it from Nuget Package Manager Console as well. The commands for each are shown below
jQuery
PM> install-package jQuery -version 1.9.1
Ideally we wouldn’t specify the version, just that as of now there is a conflict between jQuery 2 and jQuery Validation framework. So we’ll stick to 1.9.1.
jQuery Validation
PM> install-package jQuery.Validation
Newtonsoft JSON
PM> install-package Newtonsoft.Json
jQuery UI Combined Library
PM> install-package jquery.ui.combined
Modernizer
PM> install-package modernizr
jQuery Unobtrusive Validation
PM> install-package Microsoft.jQuery.Unobtrusive.Validation
PM> install-package Microsoft.jQuery.Unobtrusive.Ajax
Microsoft ASP.NET Web Optimization
PM> install-package Microsoft.AspNet.Web.Optimization
WebGrease (update): The Web Optimization bundle installs WebGrease 1.0. We should updated it to the latest (1.3 at the time of writing).
PM> update-package WebGrease
Updating Web.config
Now that our dependencies are setup, let’s update the Web.config to unable WebPages 2.0, Unobtrusive Validation and also add the namespaces for MVC.The App Settings
We add the following inside the <configuration> node
<appSettings>
<add key="webpages:Version" value="2.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="PreserveLoginUrl" value="true" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
The System.Web settings
Inside the System.Web node, we add the following namespaces and the runtime framework setting.
<system.Web>
…
<httpRuntime targetFramework="4.5" />
<pages controlRenderingCompatibilityVersion="4.0">
<namespaces>
<add namespace="System.Web.Helpers" />
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization" />
<add namespace="System.Web.Routing" />
<add namespace="System.Web.WebPages" />
</namespaces>
</pages>
…
</system.Web>
The Runtime settings
Inside the <runtime> node, we add the following assembly binding redirections. Add the <runtime> node if it doesn’t exist.
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
So far so good. Build and Run the Application again to ensure nothing is broken.
Adding MVC Tooling Support to the Project
Now that we have the MVC dependencies installed, we should be able to generate MVC Controller and Views. Unfortunately when we right click on the Solution Explorer, these options do not come up. To do this, we need to Unload our project and hack a ProjectType GUID into the csproj xml.To unload the project, right click on the project node in Solution Explorer and select ‘Unload Project’. The project node goes gray and is marked unavailable. Right click on it and click on Edit <Project Name>.
Locate the <ProjectTypeGuids> node in the csproj xml.
Add the following GUID to it {E3E379DF-F4C6-4180-9B81-6769533ABE47}; at the beginning. The final <ProjectTypeGuids> node should be similar to the following
How did I get that GUID? Sumit Maitra to the rescue who reverse engineered an MVC Project Template :). Reload the Project and Right click on any node in the Web Project, you should be able see the ‘Add Controller’ shortcut.
Next up Bundles, Filters and Area
A standard MVC Project template has an App_Start folder that had static classes for initializing Bundles, Routes and Filters. Let us add these manually.1. Add the App_Start folder
2. Add BundleConfig.cs and register the jQuery Bundles and the jQuery UI CSS files as follows
public static class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
"~/Scripts/jquery-ui-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/modernizr-*"));
bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"));
bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(
"~/Content/themes/base/jquery.ui.core.css",
"~/Content/themes/base/jquery.ui.resizable.css",
"~/Content/themes/base/jquery.ui.selectable.css",
"~/Content/themes/base/jquery.ui.accordion.css",
"~/Content/themes/base/jquery.ui.autocomplete.css",
"~/Content/themes/base/jquery.ui.button.css",
"~/Content/themes/base/jquery.ui.dialog.css",
"~/Content/themes/base/jquery.ui.slider.css",
"~/Content/themes/base/jquery.ui.tabs.css",
"~/Content/themes/base/jquery.ui.datepicker.css",
"~/Content/themes/base/jquery.ui.progressbar.css",
"~/Content/themes/base/jquery.ui.theme.css"));
}
}
3. If you would like MVC to intercept unhandled errors, add this FilterConfig class. Else you can postpone it till you need to register another Filter.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
}
Tying them all up
Now that we have the startup files defined, let’s tie them up and invoke them from Application_Start in Global.asax. If you didn’t have Global.asax then add it to the root of the Web Project.The final Application_Start method looks as follows
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
It needs these additional namespaces
using System.Web.Optimization;
using System.Web.Routing;
using System.Web.Security;
using System.Web.SessionState;
Now we are all set to add new functionality using the ASP.NET MVC Framework.
Adding an Area
Right click on the Project node and select Add->Area. To learn more about Areas, check Areas in ASP.NET MVC is a Useful FeatureProvide an Area name. I am calling it v45 below indicating update to version 4.5. Feel free to name it as per your project’s requirements.
This will add the following folder structure to the Solution
As you can see, we have a class for Registering the Area and then empty Controller, Models and Views folders.
Adding ASP.NET MVC Functionality to Web Forms
Adding Entity Framework and Testing out MVC CRUD operations
Now that MVC is all set up, let’s add EntityFramework to the mix and develop a simple CRUD app to test it out. Your requirements may be more ‘involved’ to say the least.PM> install-package EntityFramework
Add a _Layout.cshtm
l
Update the file to contain a single line of markup as follows
<h2>Hello MVC</h2>
Now let's add the following Person class to the Model folder in our V45 area.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
Build the project. Now right click on the Controllers folder and select Add Controller.
Setup the Controller as above and hit Add. Run the Application and navigate to the Index view of the Person controller as follows. Note the V45/Person route.
Click on ‘Create New’ and add Person Details.
Hit Create and voila! Person is saved to DB!
We just added MVC functionality to ASP.NET WebForms project!!
Conclusion
In real world, changes to existing applications are slow to happen and rewrites almost never happen. However we have to keep moving to new version of frameworks and newer development methodologies because they tend to be more secure and better performing, not to mention the support aspect also.Keeping these maintenance issues in mind, we saw how we could move an ASP.NET Web Forms application to the latest .NET Framework and then add new functionality using the ‘newer’ ASP.NET MVC framework keeping the old functionality intact.
Depending on your existing project’s dependencies, your mileage may vary slightly, however the general steps remain the same.
Download the entire source code of this article (Github)
Very nice concept! I will surely look into it since we have a few Web Forms projects that cannot simply be converted to MVC.
ReplyDeleteI would like to ask if you noticed any performance issues when this application is in production?
Loading both Web Forms and MVC modules could lead to a less fast application?
@Tasos: Your question is subjective and depends on many factors. If you want a plain comparison in what gives better performance between WebForms and MVC, then MVC takes the prize (one reason being there's no viewstate, second its closer to HTTP)
ReplyDeleteIf you mean adding MVC to an existing WebForm application may slow things down, then my answer is No. If you feel the performance has degraded, always do a performance test on vanilla webforms first before adding MVC, so that you have benchmarks to compare.
I was asking for the second case, thank you for your answer!
ReplyDeleteWe have a large winforms vb project, I assume if we followed this methodology we couldnt use c# MVC?
ReplyDeleteCheers
Tim
@Tim
ReplyDeleteYou can't mix languages in same project. You can add VB.NET MVC to VB.NET WebForms project.
But you mention Winforms project? That's a different world.
Great article. Been needing to look into this due to a Web Forms project that's older, but need to have new stuff added to it.
ReplyDeleteSadly, I got the following when trying to nav to my new Area.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /VoterUI/Person
Any help on how I can go about debugging this would be appreciated! :)