Microsoft’s recent announcement that ASP.NET will soon get an alternate engine in the ASP.NET Model View Controller framework (MVC-see last month’s editorial) has brought out some renewed discussion of the merits and shortcomings of Web Forms and the potential need for an alternate mechanism of building Web applications that provide a closer and purer model to raw Web development. This is true especially in light of the different development style that AJAX/Web 2.0 applications are introducing and some of the complications that it raises with the Web Forms development lifestyle.
The criticism that’s now slowly coming out is leveled mainly against the Web Forms engine that sits on top of the core ASP.NET Runtime. Much of the dissent regarding Web Forms comes from the ALT.NET end of the developer spectrum and especially from developers who are focusing on agile development, but there’s also a bit of grumbling from hard core ASP.NET developers in general who have hit some of the boundary conditions of the ASP.NET page model that Web Forms introduced. Web Forms are the most prominent feature of the ASP.NET engine. They provide an application model that abstracts away many of the Web’s stateless and HTML semantics and this somewhat leaky abstraction has both its strong sides and weaknesses.
Web Forms Strengths
Personally I’ve used Web Forms since Microsoft originally released .NET and I’ve been fairly content with this implementation. Sure I’ve had a few problems and I’ve cursed certain features of Web Forms-and I’ll get to those later on in this article-but overall, Web Forms have been a good model to work with over all those years. However, the discussion about potential alternate frameworks has also made me think a bit more about what works and what doesn’t in Web Forms and what I want out of an alternate framework. So let me first list a few strengths and weaknesses of Web Forms from my perspective. I’ll start with the highlights of the good stuff.
Powerful, Scalable, Stable, and Mature
ASP.NET Web Forms have been around in production now for more than five years and they have been proven as a scalable and stable platform for building Web applications. Many high-end applications use ASP.NET and Web Forms to run high-volume Web sites and ASP.NET has been nothing but stable in these environments. For me personally, ASP.NET has been extremely reliable compared to the COM-based environments I dealt with previously. ASP.NET and Web Forms have been rock solid for me for years with no major issues in scalability or stability in all these years of running all sorts of different applications both on my servers and for customer sites with applications large and small. I find it hard to put a value on the peace of mind this gives me.
Easy to Get Started With
ASP.NET is clearly an easy concept for developers to get started with because it automates a number of repetitive tasks for you. The postback model makes it easy to maintain POST data in controls without having to manually re-assign them on each and every POST operation. If you enable ViewState you can maintain non-POST data control state automatically. Microsoft designed the entire Visual Studio Web eco-system to provide an easy development experience with drag and drop functionality on a visual designer with property sheet support. Developers write code to respond to events, which provides a fairly straightforward model for hooking up application logic that feels natural if somewhat un-Web-like. This model provides a high level of abstraction that is great for getting started, but it’s also problematic because it separates the developer from the low level Web mechanics in many ways. I’ll come back to this flip side a little later.
Extensible, Extensible, Extensible
One of the main attractions of Web Forms is the rich control model that has resulted in a huge proliferation of custom controls from third parties that provide all sorts of additional functionality to ASP.NET. The control model is powerful, relatively easy to work with, and provides a full eco-system from development experience to run-time capabilities and the ability to hook and extend the Web Form architecture at almost any point. This is also a strength and weakness in that the Page framework APIs are very rich, but also very complex. Many of the classes in the framework (like Page) have huge interfaces and for more advanced scenarios, you need to have an intimate familiarity with the Page event model. Becoming truly proficient with Web Forms beyond the basics can be a daunting task.
This is an abbreviated list but I can’t stress enough all of what this short list encompasses-stability, scalability, and flexibility with an established and stable API all of which are the cornerstones of a solid Web platform.
Concerns About Web Forms
If you’ve worked with Web Forms for a while and you’ve gotten reasonably proficient with the model, you’ve probably hit some of its limitations and quirks. Overall Web Forms work well, but I curse when I run into a few of these issues.
Web Forms and Leaky Abstraction
Web Forms provide an attractive platform for most developers, especially those coming from desktop development. Postback tracking, ViewState, event routing, and single-page metaphor are all based around a forms-based interface that resembles a Windows Forms style interface. The operative word here is resembles!
Criticism with ASP.NET’s Web Forms model comes mostly from hard core Web developers who see Web Forms as a leaky abstraction layer that doesn’t truly capture the Web paradigm. Web Forms essentially try to emulate a Windows Forms/desktop implementation that hides the stateless nature of the Web. Web Forms provide a high level of abstraction of the core Web implementation by using a sophisticated control and event-based engine that handles request routing, access, and rendering that mostly hides HTML and HTTP semantics from developers.
This is both a blessing and a curse. Web Forms allow developers to rapidly create applications simply by dragging and dropping controls and handling page-level events for both the page and the controls on the page. This works well, but it’s a high-level of abstraction and many developers completely forget-or never learned-how the HTML layout actually works behind the scenes. As a result, it’s common to end up with non-validating HTML, or bloated and hard-to-manage HTML layout that is very designer unfriendly. Add to that a huge amount of ViewState if you don’t effectively manage ViewState properly and you can easily end up with pages that are much bigger than they need to be and slow as molasses.
One downside of the Web Forms framework is that behind this abstraction layer, Microsoft built a very complex engine that has many side effects in the Page pipeline. If you’ve ever built complex pages that contain many components on the page it can sometimes get very difficult to coordinate the event sequence for data binding, rendering, and setup of the various controls at the correct time in the page cycle. Do you load data in the Init, Load or PreRender events or do you assign values during postback events? Web Forms need to run through a single server-side form so they can’t easily be broken up into smaller logical units. In complex forms, event handlers can get very bulky with the tasks they need to handle and often in ways that can’t be easily refactored so you end up with code that is difficult to maintain and impossible to test.
I often run into scenarios such as how do Change events affect the binding and assignment process. When does one control initialize in relation to another is another common scenario. If you’ve built ASP.NET Web Forms applications of any complexity you have probably run into these sequencing problems more than a few times. These problems can bedevil even experienced ASP.NET developers, and downright infuriate new developers.
ViewState: The Evil Within
Personally, my biggest beef with Web Forms lies with ViewState. I hate ViewState and honestly I wish Microsoft would have never created it. In fact, I turn ViewState off in just about all of my Web Forms pages as a matter of initialization (but even then I can’t get away from it entirely). Why? Because ViewState bloats page content something fierce, has a bit of performance overhead (serialization), and in AJAX applications especially, ViewState corruption caused by client-side updates to Page data can be a royal pain to deal with.
I work with a variety of customers, see a fair amount of varying ASP.NET code, and I’m often called in to help diagnose performance problems with Web applications. In a large percentage of the cases, I find performance issues directly related to rampant use of ViewState and developers who don’t understand its impact on page size and performance.
The big problem with ViewState is that it’s non-deterministic and that control developers are encouraged to store all property state inside of ViewState, which is both inefficient and bloats the page content. There are ways around the issue. You can do as I do and turn off ViewState for any content that doesn’t need it-or more ambitiously on the entire page although that’s not always possible as some controls-especially third-party ones-often can’t operate without ViewState. All of the stock ASP.NET controls that come in the box actually work with ViewState off although you may have to set a few additional properties explicitly and you may end up with a few extra code snippets to track certain settings, but you will generally end up with lighter and snappier pages that are also more AJAX friendly.
ViewState bloat mattered enough to me that I wrote a PreserveProperty Control (http://www.code-magazine.com/Article.aspx?quickid=0703051) that provides a more deterministic way of storing state content (either in ControlState, a separate hidden variable, Cache or Session) which in my opinion would have been a much better way to provide page-level persistence. Rather than encouraging every property and sub control to be rooted in ViewState, a more deterministic approach would require more restraint and focus on the key aspects that need to be persisted rather than dumping everything into ViewState automatically.
Event Management Complexity
Web Forms are all about events. Page pipeline events, postback events, and change events all are at the core of Web Forms. While events are part of what makes Web Forms easy they can also make things complicated because ASP.NET fires a ton of events as part of each and every request. Figuring out exactly when each piece of code in your Page fires as well as when every control or user control fires takes a solid understanding of the full page model and control tree architecture which is not trivial and requires a bit of experience and time.
Another annoying overly helpful feature in Web Forms is the change event management. Raise your hand if you’ve ever struggled with ListBox SelectedValue issues where you needed to handle the actual SelectedIndexChanged event and you also need to assign the SelectedIndex property explicitly. Whenever change events are hooked up it’s very common to get unexpected behavior with these events firing from seemingly unrelated operations from control initialization and direct access.
This comes back to the specific implementation of the page framework and how and when events fire. Developers have to be intricately familiar with the event sequence which is no trivial matter since there are many, many events that fire as part of the page process both on the page and on each control.
Granted this is not one of the most frequent pain points, but they do happen and when they do happen they tend to be a huge time sink. I often find myself spending an inordinate amount of time experimenting around with different implementations as I move code back and forth between events only to end up removing the event code entirely and checking for a Request.Form variable for the button or postback event explicitly and then routing to a custom method instead. I end up cringing at the resultant messy and unmaintainable code I’ve just embedded into my application.
PostBacks
Web Forms are based on postbacks. Everything you do in a Web Forms page except the first page access tends to be a postback where the page always calls back and posts to the same page. Postbacks fired off anything but a button require some client-side JavaScript code that explicitly fires the PostBack event back to the same page and routes the request to a specific event. PostBack events are what makes the event-driven functionality of Web Forms possible, providing the WinForms-like eventing model that is both so easy to use and yet so irksome to some.
The first and maybe most serious issue is that most postback operations are driven through JavaScript. For example, if you have a GridView on a page and you have the grid set up with paging enabled, each page change causes __doPostBack() client script to execute. Problem: These JavaScript links are not search engine friendly since search engines will not follow JavaScript links. The same is true for LinkButtons, and AutoPostBack controls and just about all other server controls that support postback events. If browsers have JavaScript turned off (which is not as uncommon as you might think in some large organizations) your app also could be dead in the water if it can’t be navigated without these JavaScript postbacks.
If you want to trigger postbacks from the client side with JavaScript you have to generate the __doPostBack() calls on your own and there’s no easy way to generate the event signatures. The syntax for these callbacks is a bit cryptic and the links are generated through server-side code (GetPostBackEventReference()) that embeds control-specific information into the scriptcalls. It’s no fun trying to fire a client request like this dynamically from JavaScript writing unmaintainable code like this:
javascript:__doPostBack('ctl00$Content$
dgItemList$ctl14$ctl01','')"
INamingContainer and ClientID Voodoo
The script code above is whacky mainly because of the long control id. Web Forms-generated client and unique IDs are a big problem when working with client-side AJAX code that needs to access a specific control. Web Forms generate ClientIDs based on containership which often results in very lengthy and cryptic control IDs that are difficult to reference from client script code. It’s always fun trying to reference a control ID like this from a simple control stored in a master page like this:
<input name="ctl00$Content$txtQty"
type="text"
value="1"
id="ctl00_Content_txtQty" />
What do you do here if you need to use this control in client script? Reference the full cryptic ID which might change if you rename the container and then breaks your client code? Or use code like this:
var ctl = document.getElementById("<%=
this.txtQty.ClientID %>");
which is ugly and unwieldy and won’t even work if you separate out your JavaScript code into a separate .js file. The long cryptic ids are also unfriendly to designers who often use CSS tags based on IDs (#controlId) to style layout.
AJAX and Web Forms
Client-side scripting and Web Forms in general don’t go together all that well. As I mentioned previously there are issues surrounding ViewState consistency if you use client script to update POST data sent back to the server. This makes it often difficult to work with frameworks other than ASP.NET AJAX, which is the only framework that explicitly understands the Web Form model and ViewState.
To me, Microsoft AJAX in general is not a great client-side library solution in the first place. It’s big, not very modular, and provides only limited functionality for the client-side developer with the client framework being more of a core framework/reference implementation than a useful API library that provides usable functionality for client-side scripting. If you compare it to the lean and mean and highly functional approach of libraries like jQuery, Prototype, MooTools, and so on, Microsoft AJAX feels like it’s seriously lacking practical functionality. Add to that the rigidity and complexity of the control creation model and it’s hard to justify using Microsoft AJAX for anything but the server-side controls provided in UpdatePanel and the AJAX Toolkit controls.
Using alternative libraries with Web Forms is possible but there are some issues you need to watch out for. Specifically, ViewState corruption is a problem when updating client control content that is already stored in ViewState and then sending it back to the server. EventValidation as well can cause all sorts of problems with pages posting back to the server and you’ll want to likely turn this feature off on pages that use AJAX and modify control content.
Lack of Separation of Concerns
One of the big criticisms raised against Web Forms has been that it’s difficult to separate UI, controller, and business logic. With Web Forms it’s very easy-too easy maybe-to fall into the trap of putting all code into an ASPX page and its CodeBehind source code. The problem is that ASPX pages and the controls that are provided have so many ease of use and drag and drop features that just invite you to make bad architectural decisions, mixing your application and user interface logic in one place. While working this way is easy at first it often leads to very hard to maintain applications where hunting down logic issues can become a major Easter egg hunt.
That doesn’t mean that Web Forms automatically have to be badly designed. If you use business objects to handle all business-related operations and data access, you are already separating out the largest chunk of code using the Page’s CodeBehind only as a controller for the code. In addition, if you move most of your code out of the actual event handlers and create very specific and refactored methods that handle specific tasks in the page you can go a long way towards separation of concerns. The problem is that it requires strict restraint to actually do this because the Page model doesn’t encourage it in any way. But even if you do, it’s still difficult to separate your user interface and the controller code entirely because of the event-driven nature of the Page model, which often closely couples the view and page controller logic code. In effect, ASPX pages are mixing both the user interface and the controlling/driving code very closely rather than more explicitly separating the view from the processing logic. This is an important issue when testing is thrown into the mix.
No Support for Efficient Testing
Web Forms are difficult to unit test effectively. There are a number of reasons for this, primarily that there’s no good way to programmatically test ASPX pages and get effective feedback whether the page rendered properly. Sure you can run a HTTP-based test on pages, but the results out of those tests are not always conclusive as you are testing a high level non-code construct. HTTP-based testing also has a lot of overhead and can be very slow compared to direct assembly-level testing that is typically used with unit tests.
There’s also no effective way to directly instantiate a Web Forms Page class and Web Forms and ASP.NET don’t support interfaces on the intrinsic objects like Context, Request, Response, Session etc., which would allow easier implementation of mock objects for testing. Further, the event-driven interface model also makes it difficult to even devise effective tests for more complex pages, as page logic is often not isolated in single events/methods but spread over several events and methods. Also the surrounding page state may or may not be present in the test environment which further complicates matters. In short, Web Forms don’t lend themselves well to testing either as a whole or in isolation.
The Lack of the ASP.NET Cool Factor
Finally there’s growing concern that ASP.NET is losing its coolness factor. While a very subjective issue, the lack of cool seems to be the main reason that there are very few high profile, hip new public applications using ASP.NET these days. If you search around for Web applications that people are actively using today you will rarely find one that is running an ASP.NET backend. You’ll be much more likely to find a PHP, Ruby or even Java backend, especially for new startups. ASP.NET has lost some of its progressive image over the years, especially with the agile crowd which seems to be driving most of the new wave of Web applications popping up on the Web today.
Part of the reason for this probably has to do with ASP.NET’s ties to the Windows platform which some perceive as more expensive to run on. But there’s also the perception that ASP.NET is a stodgy platform that’s used for business applications and not for more forward-thinking and progressive applications. I say perception here because I don’t think that this is actually a valid point-ASP.NET happens to be highly flexible platform and you can just about build any type of Web application on it without issues or concerns using either Web Forms or, if you’re so inclined, by building an alternate framework that that provides a more specific platform. I’ve built a number of custom handler implementations that solved very specific business scenarios in a cleaner way than Web Forms do and the fact that this can be done effectively speaks well for the flexibility of the ASP.NET core engine.
Talking About ASP.NET MVC
One reason that this discussion of the adequacy of Web Forms has come to the forefront is that Microsoft has announced that they will release a new framework for ASP.NET based on the Model View Controller (MVC) pattern some time after the release of Visual Studio 2008, probably as part of SP1 for that product. Microsoft has aimed this framework at some of the shortcomings in Web Forms and to provide an alternative for developers who are heavily into agile and test-driven development. MVC is not meant as a replacement for Web Forms but as an alternative-Microsoft will develop the two side-by-side.
It’s very, very early in the MVC Framework lifecycle, and at the time of this writing Microsoft has not even released a public preview of the technology although they’ve shown a number of public demos. The cornerstone of this new framework is that it provides clear separation of concerns and testability out of the box.
Microsoft implemented MVC as an alternate Http Handler framework so it’s not based on the Page class and bypasses a lot of the complexities of the Web Forms framework. One of the key features is the ability to create custom views that are responsible for rendering output for specific tasks. The controller is responsible for using the model to perform core application processing and then feeding result data to the view to render. It’s quite conceivable to build re-usable views for rendering RSS feeds or other XML output or a specific page display view like a login form or error page for example. By default, however, ASPX pages cane still be used to render a view’s output using a ViewPage class. This class is based on the Page class, but it’s not called directly as an HTTP handler but rather just as a renderer from within the MVC engine which is still much more lightweight than running through the full page framework that a typical ASPX page runs through at runtime.
One issue with this abbreviated Page cycle is that you cannot use any controls that require postbacks since the processing that provides the postback and ViewState mechanics are not part of MVC. I suspect that this default PageView will be a temporary holdover until Microsoft delivers a different and more tightly integrated control architecture for the MVC framework, but that’s just a guess at this point. In the meantime you can use the Page engine using ASP-style scripting (using <%= %> or <% %> server tags) as well as server controls for render-only operation. Many things like Master Pages, user controls and most render only controls work with this view. It’s possible to use controls like a Repeater or the new ListView for example, but some features for more advanced controls like the GridView’s Paging or Sorting aren’t going to work because they rely on postbacks. This lets you apply some of your existing ASP.NET skill set, and save some time building some layouts without creating HTML purely through ASP-style script markup. Realize though by using things like Master Pages or user controls you may still incur some of the shortcomings like INamingContainer rendering funky control names and some loss of control over HTML rendering.
You can, of course, create a custom view that provides whatever rendering you’d like to implement from scratch and I’d wager that there will be many different view engines to choose from various third parties by the time the MVC framework releases.
MVC’s model works through URL routing to specific controller methods and any operation routes back to a specific method in the controller. Microsoft based the routing engine on a Ruby-like mechanism that parses clean URLs (like http://localhost/Customers/Show/1) and routes these URLs to specific controllers based on the URL scheme. The routing mechanism-like most things in the MVC framework-can be overridden easily so you can use different URL semantics to route a URL to a controller.
Controllers then use the model-which can be an explicit implementation as part of the project or a separate set of business objects-to access application-specific or business logic. The controller then manipulates the model to provide the result data that is rendered into the view.
Views are interface based and anything that implements the IView interface can act as a renderer and produce output. The interface is pretty basic with only a RenderView method that is passed a context and off you go to implement your own renderers. The beauty of this approach is that it will be possible to build very customized view renderers for things like plain data pages that are passed a an EntityRecord or a collection of objects for example, and the view can then decide to render this output. It brings a potentially much more reusable mechanism for rendering output in a more generic way.
The MVC framework is quite a different way of building Web interfaces than Web Forms. It’s much more modular and the explicit separation of concerns means that code is scattered around a few source files. However, this separation makes it much easier to test the individual components of the Web application explicitly using your unit testing framework of choice.
Is MVC the Answer?
It’s interesting to hear this discussion given that Web Forms has been the darling of Web development on the .NET platform. In fact, I find it somewhat ironic to see that MVC is in some ways reverting back to more ASP classic-style ways of working with script-like HTML rendering and raw manual output, but with a much more strictly regimented approach to enforce methodology and code logic. I also remember the early days of ASP.NET when the ASP.NET Nazis were going around disdainfully condemning any use of script tags and inline script or assigning explicit URLs instead of using postbacks, yet some of those same folks undoubtedly are the ones welcoming MVC with open arms now. Ironic, ain’t it?
It’s too early to tell whether MVC will actually address the needs of most Web developers. Certainly from what’s been shown so far it looks like MVC will require a lot more code to build even simple pages. I’ve seen an early build of the framework and it’s not really clear to me how to effectively handle more complex pages without at some point getting mired into huge case statements to route code to decide which portion of the page needs updating. I have built several Web frameworks in the past that use a similar approach to MVC and while you can easily do many simple things, in my experience this approach really starts falling apart when you have pages that contain many different and somewhat independent components. Managing this complexity through a controller-based approach that manages no state for you is much more difficult than what Web Forms provides today. We’ll have to wait and see how the framework shapes up and what patterns emerge to handle more complicated tasks. It probably helps to look at some of the Java or even some of the existing .NET frameworks that have been around for a while for doing MVC-based Web development and compare notes.
But it’s early and there’s lots of opportunity for Microsoft (and the community) to come up with powerful view implementations that provide the best of what Web Forms offer and mix it with a more lightweight and low-level approach that MVC appears to be shooting for. The key, in my opinion, will be how well the View implementation compares to what Web Forms does today. The concepts of controllers and models are clear winners in my opinion, but the view architecture is going to be the determining factor of how well this technology will fare with the general developer community.
If nothing else I look forward to seeing how MVC will evolve and I think about opportunities to extend the framework. New frameworks always end up bringing new opportunities for doing things differently and MVC represents the process of a new framework evolving from a perspective where a lot of people are familiar with the underlying platform (ASP.NET) and can provide feedback and input to Microsoft. It ought to be an interesting journey.
On the other hand, I also think that it might well be a good time for Microsoft to explore a new development path since Web Forms have become somewhat stale at least from the perspective of Microsoft’s development for it. .NET 3.5 includes almost no new features in ASP.NET and beyond the introduction of Microsoft AJAX, hardly anything has changed in ASP.NET since the release of version 2.0. A new framework might throw a new spark into the development effort and also provide the community and third parties the ability to expand and build on top of a new platform. The potential is there, but it’s just too early to tell where things are headed.
In the meantime we’ll have to wait and see. For the moment I’m not quite ready to throw out the baby with the bathwater and put all of my hopes on a new unproven framework. There are a lot of good things going for Web Forms and just because Microsoft has decided to try something new doesn’t mean we all have to jump to it. After all, MVC frameworks have been around for some time and they haven’t made any significant inroads today. MVC will appeal to a specific set of developers immediately-for the rest of us, Microsoft will have to build a kick-ass framework to make it at least comparable in flexibility, extensibility, and usability as Web Forms. There are some interesting times ahead. I’ll be watching with interest…