Running ASP.NET Webforms and ASP.NET MVC side by side
One of the nice things about ASP.NET MVC and its older brother ASP.NET WebForms is that they are both built on top of the ASP.NET runtime environment. The advantage of this is that, you can still run them side by side even though MVC and WebForms are different frameworks.
Another point to note is that with the release of the
ASP.NET routing in .NET 3.5 SP1, we are able to create SEO
friendly URLs that do not map to specific files on disk. The
routing is part of the core runtime environment and
therefore can be used by both WebForms and MVC.
To run both frameworks side by side, we could easily
create a separate folder in your MVC project for all our
WebForm files and be good to go. What this post shows you
instead, is how to have an MVC application with WebForm
pages that both use a
common master page and
common routing for SEO friendly URLs.
A sample project that shows WebForms and MVC running
side by side is attached at the bottom of this post.
So why would we want to run WebForms and MVC in the same
project?
WebForms come with a lot of server controls that
provide a lot of rich functionality. One example is the
ReportViewer control. Using this control and client report
definition files (RDLC), we can create rich interactive
reports (with charting controls). I show you how to use the
ReportViewer control in a WebForm project here :
Creating an ASP.NET report using Visual Studio 2010. We can create even more advanced reports by using SQL
reporting services that can also be rendered by the
ReportViewer control.
Moving along, consider the sample MVC application I blogged
about titled :
ASP.NET MVC Paging/Sorting/Filtering using the MVCContrib
Grid and Pager. Assume you were given the requirement to add a UI to the
MVC application where users could interact with a report and
be given the option to export the report to Excel, PDF or
Word. How do you go about doing it?
This is a perfect scenario to use the ReportViewer
control and RDLCs. As you saw in the post on creating the
ASP.NET report, the ReportViewer control is a Web Control and is designed
to be run in a WebForm project with dependencies on, amongst
others, a ScriptManager control and the beloved
Viewstate.
Since MVC and WebForm both run under the same runtime,
the easiest thing to is to add the WebForm application files
(index.aspx, rdlc, related class files) into our MVC
project. We will copy the files over from the WebForm
project into the MVC project.
Create a new folder in our MVC application called
CommonReports. Add the index.aspx and rdlc file from the
Webform project
Right click on the Index.aspx file and convert it
to a web application. This will add the
index.aspx.designer.cs file (this step is not required if
you are manually adding a WebForm aspx file into the MVC
project).
Verify that all the type names for the
ObjectDataSources in code behind to point to the correct
ProductRepository and fix any compiler errors. Right click
on Index.aspx and select “View in browser”. You should see a
screen like the one below:
There are two issues with our page.
It does not use our site master page and the URL is not
SEO friendly.
Common Master Page
The easiest way to use master pages with both MVC and
WebForm pages is to have a common master page that each
inherits from as shown below.
The reason for this is most WebForm controls require
them to be inside a Form control and require ControlState or
ViewState. ViewMasterPages used in MVC, on the other hand,
are designed to be used with content pages that derive from
ViewPage with Viewstate turned off. By having a separate
master page for MVC and WebForm that inherit from the Root
master page,, we can set properties that are specific to
each. For example, in the Webform master, we can turn on
ViewState, add a form tag etc.
Another point worth noting is that if you set a
WebForm page to use a MVC site master page, you may run into
errors like the following:
A ViewMasterPage can be used only with content pages that
derive from ViewPage or ViewPage<TViewItem>
or
Control 'MainContent_MyButton' of type 'Button' must be
placed inside a form tag with runat=server.
Since the ViewMasterPage inherits from MasterPage as seen
below, we make our Root.master inherit from MasterPage,
MVC.master inherit from ViewMasterPage and Webform.master
inherits from MasterPage.
We define the attributes on the master pages like so:
-
Root.master
<%@ Master Inherits="System.Web.UI.MasterPage" … %> -
MVC.master
<%@ Master MasterPageFile="~/Views/Shared/Root.Master" Inherits="System.Web.Mvc.ViewMasterPage" … %> -
WebForm.master
<%@ Master MasterPageFile="~/Views/Shared/Root.Master" Inherits="NorthwindSales.Views.Shared.Webform" %>
Code behind:
public partial class Webform : System.Web.UI.MasterPage {}
We make changes to our reports aspx file to use the
Webform.master. See the source of the master pages in the
sample project for a better understanding of how they are
connected.
SEO friendly links
We want to create SEO friendly links that point to our
report. A request to /Reports/Products should render the
report located in ~/CommonReports/Products.aspx. Simillarly
to support future reports, a request to /Reports/Sales
should render a report in ~/CommonReports/Sales.aspx.
Lets start by renaming our index.aspx file to
Products.aspx to be consistent with our routing criteria
above.
As mentioned earlier, since routing is part of the
core runtime environment, we ca easily create a custom route
for our reports by adding an entry in Global.asax.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//Custom route for reports
routes.MapPageRoute(
"ReportRoute", // Route name
"Reports/{reportname}", // URL
"~/CommonReports/{reportname}.aspx" // File
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
} // Parameter defaults
);
}
With our custom route in place, a request to
Reports/Employees will render the page at
~/CommonReports/Employees.aspx. We make this custom route
the first entry since the routing system walks the table
from top to bottom, and the first route to match wins.
Note that it is highly recommended that you write unit
tests for your routes to ensure that the mappings you
defined are correct.
Common Menu Structure
The
master page in our original MVC project had a menu structure
like so:
<ul id="menu">
<li>
<%=Html.ActionLink("Home", "Index", "Home") %></li>
<li>
<%=Html.ActionLink("Products", "Index", "Products") %></li>
<li>
<%=Html.ActionLink("Help", "Help", "Home") %></li>
</ul>
We want this menu structure to be common to all pages/views
and hence should reside in Root.master. Unfortunately the
Html.ActionLink helpers will not work since Root.master
inherits from MasterPage which does not have the helper
methods available.
The quickest way to resolve this issue is to use
RouteUrl expressions. Using RouteUrl expressions, we can programmatically
generate URLs that are based on route definitions. By
specifying parameter values and a route name if required, we
get back a URL string that corresponds to a matching route.
We move our menu structure to Root.master and change
it to use RouteUrl expressions:
<ul id="menu">
<li>
<asp:HyperLink ID="hypHome" runat="server" NavigateUrl="<%$RouteUrl:routename=default,controller=home,action=index%>">Home</asp:HyperLink></li>
<li>
<asp:HyperLink ID="hypProducts" runat="server" NavigateUrl="<%$RouteUrl:routename=default,controller=products,action=index%>">Products</asp:HyperLink></li>
<li>
<asp:HyperLink ID="hypReport" runat="server" NavigateUrl="<%$RouteUrl:routename=ReportRoute,reportname=products%>">Product Report</asp:HyperLink></li>
<li>
<asp:HyperLink ID="hypHelp" runat="server" NavigateUrl="<%$RouteUrl:routename=default,controller=home,action=help%>">Help</asp:HyperLink></li>
</ul>
We are done adding the common navigation to our application.
The application now uses a common theme, routing and
navigation structure.
Conclusion
We have seen how to
do the following through this post
- Add a WebForm page from a WebForm project to an existing ASP.NET MVC application
- Use a common master page for both WebForm and MVC pages
- Use routing for SEO friendly links
-
Use a common menu structure for both WebForm and MVC.
The sample project is attached below.
Version: VS 2010 RTM
Remember to change your connection string to point to your
Northwind database
-