ASP.NET MVC is seeing a lot of enthusiasm from the community right now, but not everyone is fortunate enough to be able to make the switch right away.

Some of you may be maintaining large applications where a full rewrite is not possible, or at least not in the immediate future.

That doesn’t mean there aren’t steps you can take to get your code ready for an eventual transition to MVC.

A word of warning: some of these suggestions may make you squeamish if you’re an experienced Web Forms developer who is used to using that model and accustomed to following its associated best practices.

If you like Web Forms, by all means, continue using that model to its fullest extent and take advantage of all the features it has to offer.

However if you’re looking to make the switch then keep reading and see how many of these steps you can implement in your project.

Step 1: Move Code Out of Code-Behind Files

The easiest step you can make towards weaning yourself off Web Forms is to separate your Web-Forms-specific code from your general business rules, logic, data access, etc.

If you find any of this code lying around in your code-behind files you should move this to a generic class that can be re-used anywhere. Make sure this generic code does not reference things like the Request or Response object (you should still access these from within your Web Forms code-behind).

If you’re not sure where to put this code you can create a static class somewhere and organize it as you go. You can also move your code to a separate class library DLL to help keep things organized.

Step 2: Stop Using ViewState

Despite what you may have heard in the past there are no situations which *require* you to use ViewState in a Web Forms app. That’s right: zero.

Even for times when you want to re-display data that a user entered into a form between post-backs there is no need for ViewState; that data is already in the Request.Form collection. Controls like asp:textbox can read their previous values just fine without ViewState (try it!).

You might find that there are cases where you use ViewState to store data that was fetched from the server during the initial “non-postback” rendering. In general I would say that this is a bad practice. The user’s browser shouldn’t be used to cache data needed by the web server. Any time you see code like this:

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        label1.Text = MyDatabase.GetWidget(1).WidgetName;
    }
}

You can simply replace it with this:

protected void Page_Load(object sender, EventArgs e)
{
    label1.Text = MyDatabase.GetWidget(1).WidgetName;
    if (!Page.IsPostBack)
    {
        
    }
}

You can turn off ViewState at a control level, a page level, or globally. I’d recommend starting at the control level for larger projects, then working your way up to page level, and then globally to prevent future pages from being created that use ViewState.

.Net controls have a property named EnableViewState which you can set false. You can also set this property on the page directive at the top of the aspx file. To disable ViewState globally you need to set enableViewState=”false” on the system.web/pages node in the web.config.

Step 3: Stop Using Post-Back

Let’s be clear: posting an actual form to the server is perfectly fine, however you should generally avoid using the HTTP POST method in most other circumstances.

Most often post-back is abused in order to change the output of a webpage based on user interaction. If you look in your code-behind and see events wired up for things besides a form’s “submit button click” then you should start cleaning this up by differing your output based on querystring instead of by post-back events.

Post-back to QueryString

For example let’s say you have a link button control with a click event that changes the value of a label when it is fired:

<asp:LinkButton ID="showAllLink" runat="server" OnClick="showAllLink_Click">Show All</asp:LinkButton>
protected void showAllLink_Click(object sender, EventArgs e)
{
    label1.Text = "Showing All";
}

In this case we can replace the link button with a standard hyperlink that points to the current page with a querystring of showall=true.

You can keep the event function, but rename it and use it as a standard function:

protected void ShowAll()
{
    label1.Text = "Showing All";
}

Then in the page load you can call this function based on reading the querystring:

if (Request.QueryString["showall"] == "true")
{
    ShowAll();
}

Your users will love you for this change because now they can add a bookmark directly to this page state rather than having to click the asp:LinkButton and trigger a post-back each time!

They can also refresh the page to see the latest data without having to click confirm on the “Do you want to resend data?” prompt that browsers will display during post-back refreshes.

Another Option: Use JavaScript

For advanced scenarios consider whether you can alter the content of the page using JavaScript, which would allow you to skip doing the page refresh altogether. If having a bookmarkable URL concerns you there are JavaScript libraries that can help you with this as well.

Remember that SEO-critical sections may not be a candidate for JavaScript, and of course you have to ensure that it is okay to require JavaScript of all end-users.

People working on sites that must meet accessibility guidelines may also want to be careful about jumping into a JavaScript solution as well.

Step 4: Ditch the Global Form Tag

Now that you’ve weaned yourself off of ViewState and post-back it should be relatively easy to remove the <form runat=”server”> tag from your master pages. It isn’t required if you aren’t taking advantage of ViewState.

Make sure to still include this tag around actual forms in your site. See this great article for more information about removing the form tag.

Step 5: Centralize Your Code

You’re asked to investigate why certain data is being displayed on a specific page in your app. Where do you look to find out where the data is being fetched? The code-behind of the aspx file? The front-end? The code-behind for a control on the page? The ascx file? How about the master page code-behind? Master page front-end? HTTP module? Global.asax?

Projects can get messy, and while MVC isn’t a cure-all it does do a much better job of coaxing people to put all of their “fetch” code in controllers, while the views stay “dumb” and merely read the results of the controller’s processing.

There’s no reason you couldn’t start organizing your code like this in a Web Forms project, too.

Avoid making controls that go out and fetch their own data, and instead try to do processing in the code-behind for your aspx file and pass that data along to any controls that would need it.

Don’t let your aspx or ascx files make direct calls to functions themselves, keep this in the code-behind and bind the result or expose the result via properties (see the next step for more on this).

Try to keep your master pages light and don’t treat them as a pseudo base-class for your files (they aren’t).

Not only will your project be closer to the MVC model, but you’ll have a much easier time debugging issues when you know that you can always look in the code-behind of the aspx file (or perhaps one of its base classes) rather than hunting down the offending code all over the project.

Step 6: Stop Using Controls

Now that you aren’t using ViewState or post-back and you’ve got all of your processing logic in your aspx code behind, you could question why you should continue to use ASP.NET controls at all.

This will make some experienced Web Forms developers feel uncomfortable because they’ve been taught for so long that the main distinction between platforms like ASP.NET and “lesser” frameworks like PHP or classic ASP is that ASP.NET controls allow for a separation between server code and HTML rendering.

But you don’t need to use controls in order to separate rendering logic from server-side code. We can continue to use the code-behind to invoke all of our data access code or business rules code (which should be in separate classes by now), as well as to access the request/response objects as necessary.

All the front-end really needs are properties to be exposed by the code-behind which give us the result of these operations. Read-only access, in other words.

For example instead of setting the text on a label control:

label1.Text = MyDatabase.GetWidget(1).WidgetName;

We can now set a property with this information and output it on the front-end (make sure the property is public or at least protected):

public partial class _Default : Page
{
    public string WidgetName { get; set; }

    protected void Page_Load(object sender, EventArgs e)
    {
        this.WidgetName = MyDatabase.GetWidget(1).WidgetName;
    }

}

And on the front end we can output this value in place of using a label tag:

<%= this.WidgetName %>

Remember that “this” works in both places since your front end is in reality a class which inherits from your code-behind class.

The same principal applies to “data binding”. You don’t need to data bind anywhere. Just set a property containing your records, and loop through it on the front end:

<ul>
    <% foreach (var dinner in Model) { %>
        <li>
            <%= dinner.Title %>
            on
            <%= dinner.EventDate.ToShortDateString() %>
            @
            <%= dinner.EventDate.ToShortTimeString() %>
        </li>
    <% } %>
</ul>

It’s not as nice as using Razor in MVC, but it’s a step in the right direction if you’re thinking ahead about all the front-end code you’ll eventually need to port over. It’s far easier to convert the aspx “percent-sign/equal-sign” syntax to Razor “at-sign” markup than it is to convert a control like a ListView or a GridView, for example.

Step 7: Use Models

Now that you’ve got your master pages setting properties to be read by the front-end code, why not store all of these properties into a single “Model” property? It was a good idea for MVC, and it’s a good idea for Web Forms, too.

Instead of writing code like <%= this.WidgetName %> or simply <%= WidgetName %> you could write <%= Model.WidgetName %>.

This would force you to create model objects for each of your unique views just like MVC, which you could re-use as you convert your project. If you choose to go this route you can also put your models in a folder called “Models” ahead of time for consistency with MVC best practices.

Step 8: Route Your aspx Pages

ASP.NET 3.5 introduced routing for Web Forms pages, and 4.0 made it even easier to use.

In .net 4.0 you can add code to your global.asax to specify the route:

public void Application_Start(object sender, EventArgs e)
{
    RegisterRoutes(RouteTable.Routes);
}
private void RegisterRoutes(RouteCollection routes)
{
    routes.MapPageRoute(
        "ProductDetail",
        "products/product{productID}",
        "~/Views/ProductDetail.aspx",
        true,
        null,
        null
    );		
}

Then you can retrieve route parameters in the code-behind by using: RouteData.Values["productID"].

I also recommend keeping all of your routed pages in a special directory like “Views” with it’s own web.config. You can add entries to the web.config in this directory to prevent direct access to these pages:

<?xml version="1.0"?>
<configuration>
    <system.web>
        <httpHandlers>
            <add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
        </httpHandlers>
    </system.web>
</configuration>

This ensures that the user is always going to get to the page through the route rather than directly accessing the aspx page, and that the RouteData will always be populated.

It’s possible to update your entire web forms project to use this approach over time, which would make for a smooth conversion to MVC without users noticing a change in URL structure.

If you worry about users or search engines having links to the old URLs I’d recommend setting up a handler to emit permanent redirects to your new pages, or if possible you can use IIS7 Route entries to do the same thing.

Step 9: Add MVC into your Web Forms Project

You might think we’ve practically turned our Web Forms app into MVC-lite at this point, but we’re not done yet: let’s just add the real thing!

That’s right: you can add MVC code directly into your existing Web Forms project and run them side-by-side in the same project. They can use the same DLLs, the same session, same authentication, etc. No virtual directories are needed, either.

If you’re thinking to yourself “I bet this process is completely automated, painless and well-documented”, then you haven’t been a .NET developer for very long, have you? :-)

Regardless, if you’re up for the challenge (and can get your project up to .net 4.0) then Scott Hanselman has a blog post telling you how to add MVC into your existing Web Forms project.

Some of you may feel ready to add MVC to your project and start creating MVC pages right away, but I think that for most projects you’ll be better suited getting the code a little more structured first using the steps outlined above before taking the plunge into a conversion.

Final Thoughts

I expect that as you read this there are product managers around the globe who are not comfortable enough to convert their half-a-million lines of code Web Forms site into MVC overnight and I couldn’t blame them.

However, if a conversion to MVC is a future goal then I would suggest following as many of these steps as you can (or feel comfortable with) now in order to make that conversion less painful.

If you feel that parts of your site can be converted more easily, or if you’re creating brand new sections which are somewhat isolated from the rest of the project, then go ahead and add MVC to your site and see if you can get the new pages running in MVC side-by-side with the Web Forms pages.

Keep in mind that you won’t get to re-use your master pages, so this could be problematic depending on how eager you are to duplicate template code between MVC and Web Forms. If things look too ugly then you might be better served by waiting for a full all-or-nothing conversion, possibly after a little bit of prep work to get the Web Forms code more aligned with the eventual MVC code.

Is your team looking to make the move to MVC? How are you planning to migrate your code? Let me know in the comments, and good luck! -R