Monday, September 26, 2011

Host MVC App on IIS 5.1 (XP Professional)


Host MVC App on IIS 5.1 (XP Professional)

Using the ASP.NET routing for MVC IIS 7.0 is supposed to work very nicely. Sadly, we don’t have the budget for Windows 2008 server yet. So, I had to look around for ways to kludge the routing for my XP machine.
Fortunately Microsoft has some really clear directions for IIS 6. You can see them here: http://www.asp.net/learn/mvc/tutorial-08-cs.aspx
Translating that to IIS 5.1 is not too hard. To start with, create your MVC app. Test it, make it work in the Visual Studio debugger, etc.


1. Create a virtual directory in IIS. To do this, right click on your Default Website, select “New”, then select “Virtual Directory”. Walk through the virtual directory wizard.
2. Now, you need to create an application mapping for your MVC application. Right click on the virtual directory and select “Properties”.This will load the properties for the virtual directory.Click on the “Virtual Directory” tab  and then find the “Configuration” button … click that to load the application configuration applet.
3. Click on the “Mappings” tab, then click on the “Add” button to add an application extension mapping.The executable needs to point at the aspnet_isapi.dll. That is found in the C:WINDOWSMicrosoft.NETFrameworkv2.0.50727 directory.
    There is some wierd little bug in that form so you have to click back inside the executable text box after you navigate to the .dll and select it. Otherwise the “OK” button stays grayed out, i don’t know why it does it or why click back into the box works, but it does. For extension, put “.*” and make sure that “Check that file exists” is unchecked.


That was all i needed to do. My Global.asax RegisterRoutes function looks like this when i begin my app.

public static void RegisterRoutes(RouteCollection routes) 
 { 
      routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 
      routes.MapRoute( "Default",
                                  "{controller}/{action}/{id}",  
                                  new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                                ); 
 }

References : 
http://blog.davidrenz.com/?p=418

Thursday, September 15, 2011

How to convert an ASP.NET Web Forms web application into ASP.NET MVC 3

Convert an ASP.NET Web Forms web application into ASP.NET MVC 3

So, you have a traditional web forms web application and you are inspired by the new ASP.NET MVC 3 are you? Well this is definitely doable. In this how to guide I will tell you step by step exactly how you can add your new ASP.NET MVC 3 work into your existing ASP.NET web forms project.

1. Add references

Right click on your web root project in Visual Studio (2010 in my case) and add the following references:
  • System.Web.Routing
  • System.Web.Abstractions
  • System.Web.Mvc
  • System.WebPages
  • System.Web.Helpers
2. Configuration

Make your web.config look like this. Obviously not exactly like this, don't just paste this over the top of your glorious web.config. I'm just highlighting the necessary points that need to be fitted into your existing web.config:

<appSettings>
  <add key="webpages:Version" value="1.0.0.0"/>
  <add key="ClientValidationEnabled" value="true"/>
  <add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
  
<system.web>
  <compilation debug="true" targetFramework="4.0">
    <assemblies>
      <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    </assemblies>
  </compilation>
  <pages>
    <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.Routing" />
      <add namespace="System.Web.WebPages"/>
    </namespaces>
  </pages>
</system.web>
<system.webServer>
  <modules>
    <remove name="UrlRoutingModule-4.0" />
    <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" />
  </modules>
</system.webServer>
<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
      <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.0.0.0" />
    </dependentAssembly>
  </assemblyBinding>
</runtime>


Update: I have changed my recommendation against using <modules runAllManagedModulesForAllRequests="true"> in favour of adding the UrlRoutingModule-4.0 module with a blank precondition as explained in article Don't use runAllManagedModulesForAllRequests="true" for MVC routing.

However, if you disagree with me then you can make the modules section simply look like this:


<system.webServer>
  <modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>


3. Routing

You will need to add a global.asax file to your web application if you haven't already got one. Right click > Add > Add New Item > Global Application Class:
Now add these lines:

using System.Web.Mvc;
using System.Web.Routing;


public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
  
    //ignore aspx pages (web forms take care of these)
    routes.IgnoreRoute("{resource}.aspx/{*pathInfo}");
  
    routes.MapRoute(
        // Route name
        "Default",
        // URL with parameters
        "{controller}/{action}/{id}",
        // Parameter defaults
        new { controller = "home", action = "index", id = "" }
        );
}
  
protected void Application_Start(object sender, EventArgs e)
{
    RegisterRoutes(RouteTable.Routes);
}
4. Add some standard folders to the solution
Add a folder named 'Controllers' to your web project.
Add a folder named 'Views' to your web project.
Add a folder named 'Shared' to that Views folder.
Add a web configuration file to the Views folder (web.config).
Open this web.config in the Views folder and ensure make its contents as follows:

<?xml version="1.0"?>
  
<configuration>
  <configSections>
    <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
      <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
    </sectionGroup>
  </configSections>
  
  <system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
      </namespaces>
    </pages>
  </system.web.webPages.razor>
  
  <appSettings>
    <add key="webpages:Enabled" value="false" />
  </appSettings>
  
  <system.web>
    <httpHandlers>
      <add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
    </httpHandlers>
  
    <!--
        Enabling request validation in view pages would cause validation to occur
        after the input has already been processed by the controller. By default
        MVC performs request validation before a controller processes the input.
        To change this behavior apply the ValidateInputAttribute to a
        controller or action.
    -->
    <pages
        validateRequest="false"
        pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
        pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
        userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <controls>
        <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
      </controls>
    </pages>
  </system.web>
  
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
  
    <handlers>
      <remove name="BlockViewHandler"/>
      <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
    </handlers>
  </system.webServer>
</configuration>

5. Get Visual Studio 2010 to recognise your MVC skills
If you right click the Controllers folder and select Add, you'll notice there is no Controller class to add. You need to make a change to your web project file.
Using Windows Explorer find the web project file (Web.csproj in my case) and open it in a text editor. You will need to add "{E53F8FEA-EAE0-44A6-8774-FFD645390401};" to the ProjectTypeGuids element.
E.g. mine now looks like this:
<ProjectTypeGuids>{E53F8FEA-EAE0-44A6-8774-FFD645390401};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

6. Optional: Add a Controller
Back to Visual Studio 2010 and right click the Controllers folder > Add > Controller... call it HomeController.

using System.Web.Mvc;
  
namespace Web.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Message = "This is MVC";
            return View();
        }
    }
}

7. Optional: Add a View
Right click where it says return View(); and select Add View... then click Add.
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<head runat="server">
    <title>Index</title>
</head>
<body>
    <div>
        <%: ViewBag.Message %>
    </div>
</body>
</html>

8. Optional: Try it
Now run you site and go to http://{localhost}/home

I hope this saved you the hours it took me! Please comment if you have any feedback. It would be much appreciated