MultiView control: should you use it?
Today I was called to investigate a performance problem with a page of a web application. It was a simple page, with several tabs where each tab loaded several items from the database. According to the guys that wrote the app, things started getting worse with each tab that got added to the page and, since they didn't have a lot of experience with ASP.NET, they didn't understand what was going on. They were also little bit intrigued by the fact that the site took almost 2 minutes to load in production (compared to the 20 seconds it took on the development machine).
Here's what I found when I looked at the page (just a simple example):
<asp:MultiView runat="server" ID="mview">
<asp:View>
<uc1:Demo1 ID="Demo11" runat="server" />
</asp:View>
<asp:View>
<uc2:Demo2 ID="Demo21" runat="server" />
</asp:View>
</asp:MultiView>
And then, each User Control had similar code to this one:
<%@ Control Language="C#" ClassName="Demo1" %>
<script runat="server">
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
//assume you have code here that gets something from bd and
//binds it to a control
}
</script>
<div>This is user control 1. Assume this is getting data from someplace</div>
By now, you must have noticed the bold on the previous snippets...So, can you see anything wrong here? At first sight, it really looks like there's nothing wrong with the previous example. However, there really are lots of things wrong with this approach.
The MultiView control keeps a list of Views and will only render the active one. To the programmer that is using the control and doesn't know its internals, it really looks like a cool control: without almost no work at all, you've just built an interface which will let you toggle the current view that is shown to the user. And have I said that you can do athis without writing ny code (ok, you do need to write at least one line to change the active view, but how difficult can it be to write a single simple line of code, right?)! Wow! This control is really great! Do you agree with this? I really hope not...
To see why, lets use our old friend reflector to take a look at the MutiView class. Lets see the current implementation of the Views property (responsible for keeping the list of views that is defined on the page):
[WebSysDescription("MultiView_Views"), PersistenceMode(PersistenceMode.InnerDefaultProperty), Browsable(false)]
public virtual ViewCollection Views
{
get
{
return (ViewCollection) this.Controls;
}
}
Now, if you're like me, you should be seeing alarms firing all over the place :) So, when you use this control, all the views (which are just simple controls) are loaded and added to the control collection of the MultiView control. What this means is that all those controls (views) will go through the page life cycle like if they were added directly on the form control. The only difference is that the MultiView control overrides the Render method so that it renders only the active view (by default, all controls maintained on the Controls property are rendered into the page that is sent to the client):
protected internal override void Render(HtmlTextWriter writer)
{
View activeView = this.GetActiveView();
if (activeView != null)
{
activeView.RenderControl(writer);
}
}
Btw, if you're thinking about the minutes vs seconds (when comparing the production site with the development site) the answer was also simple: the database was on a different machine (which meant, that the page had to make several remote calls to get the data it needed for rendering each view, even though only one would be rendered to the client). Fixing this was simple: they changed the code so that the active user control was loaded dynamically by the page.
Moral of the story: well, this example demonstrates one of the problems of the web forms approach: too much abstraction will end up hurting you!