Scott Hanselman

ASP.NET, Caching, and Cartesian Products

January 22, 2004 Comment on this post [4] Posted in ASP.NET | XML
Sponsored By

I'm a HUGE believer in caching and unfolding data.  If you have a little extra RAM on your Web Servers, take advantage of it and cache.  When caching on the Web Server, the form of the data you cache should "look" as much like the data the end user would see.  In other words, if you have VERY normalized data in the database, but the HTML that will eventually be rendered to the user is a very unfolded, flat version of that data, then the data you cache should look more like the latter than the former.

When Patrick Cauldwell, Joe Tillotson, and Javan Smith worked on 800.com and Gear.com during the boom (800.com was bought by Circuit City and Gear.com was bought by Overstock.com) we built a series of multi-level caches that unfolded (de-normalized) the closer they got to the point of rendering, until we finally cached rendered HTML.

For sites that follow a regular navigation scheme (often that scheme is described by an XML file or in a Database...you know, nav.config, etc...we've all written one) the HTML of the headers, footers and navigation UI element (trees, pulldowns, tabs) should be cached if they are shared between more than one user.  Meaning, that if every user has a unique navigation, of course the ASP.NET Cache object isn't the place for them. 

In a site I'm working on now, there are (names changed to protect the innocent) Gold and Silver users.  Gold users see one set of navigation tabs, Silvers see another.  Additionally Gold and Silver users can be enrolled in additional programs, like Plan-A and Plan-B.  Whether a user is enrolled in Plan-A or Plan-B is not related to their membership in the Gold and Silver groups.

Additionally, the navigation tabs are drawn based on the current page (in Request.RawUrl).  Some navigation schemes that I am no longer a fan of are those that include techniques like Page.aspx?nav=tab1&subnav=subtab4&somethingsecret=somethingsilly.  I prefer to use liberal use of Url Rewriting and "simulated pages," like changing sitename/book.aspx?isbn=123 to sitename/123.book, etc.

For this site, we are just using page names and indicating in a navigation config XML file what tabs belong with what page.  For example:

<NAVIGATION>
   <ROLE name="Gold">
      <MENUITEM name="Accounts">
         <SUBMENUITEM name="Balances">
              <PAGE role="Plan-A">balances.aspx</PAGE>
              <PAGE>transfers.aspx</PAGE>
              <PAGE role="Plan-B">somethingelse.aspx</PAGE>
          </SUBMENUITEM>
       </MENUITEM>
    </ROLE>
</NAVIGATION>


...yada, yada, yada.  Of course, it's much more complex that this.  Each page also includes context sensitive help, user customizable links from a dropdown and a list of links that are related to the page their are on that the user may find interesting.  All of these are inter-related, making the XML file fairly normalized and complicating things.  When the file is finally deserialized, a series of hashtables and lookuptables are cached in memory for efficiency and used when rendering the menu.  The menu can render itself as a series of Tabs and SubTabs or a Tree, or whatever. 

The header/renderer is an ASCX file that asks the "NavigationService" for the details of the current navigation scheme, based on Context, in this case HttpContext.  A series of tests are done, checking .NET's role-based security for the user's roles.  Gold and Silver are mutually exclusive and Plan-A an Plan-B are not. 

That means that given n pages, x mutually exclusive roles and y non-mutually-exclusive roles, there can be:

n * x * 2y combinations of rendered headers

like:

balances.aspx : Gold
balances.aspx : Silver
balances.aspx : Gold : Plan-A
balances.aspx : Silver : Plan-A
balances.aspx : Gold : Plan-B
balances.aspx : Silver : Plan-B
balances.aspx : Gold : Plan-A : Plan-B
balances.aspx : Silver : Plan-A : Plan-B

and on and on.  So, if you look at the 1*2*4=8 strings above, you can imagine them as keys in a HashTable.  We can cache Header two different ways (actually dozens, but let's make it simple):

1. As Control objects in the Control Tree during the OnLoad.  If we see the same key again (the same page is visited with the same roles) we grab the Control objects from the HashTable, add them to the Control Tree.  Then the Control Tree will be turned into HTML in OnRender. 

Cons: This would take some more memory than caching just the HTML; it takes more CPU to Render the Controls every page view. 

Pros: If the Creation of the Control Tree that represents the navigation is expensive (more~ than ~50% of the totaly time it takes to fetch, build and render) then it's an easy change to implement if you're already building the navigation with HtmlControls in the code-behind.

2. Using the <%OutputCache%>directive with the VaryByCustom parameter like this:  <%OutputCache VaryByParam="None" VaryByCustom="PageAndRolesKey" Duration="180" %>.  Then, in the Global.asax you override GetVaryByCustomString, which will be automatically called by the Pages.   That's your opportunity to provide a KEY.  Not the HTML to cache, but rather the KEY by which to cache the rendered HTML.

override public String GetVaryByCustomString(HttpContext current, String arg)
{
   switch(arg)
   {
      case "PageAndRolesKey": return GeneratePageAndRolesKey(current);
   }
}

In this example, the GeneratePageAndRolesKey() function we'd look at the current page and current roles and build a key like: "balances.aspx : Silver : Plan-A."

The rendered HTML is then stored in the Cache using the Key returned.  If the page is visited again, based on the key, the rendered HTML is retrieved from the Cache and all the slow generation code is bypassed. 

To help me visualize and conceptualize, I like to say that there is one instance of a rendered header for each possible key. 

Pros: Easy to type, easy to implement incorrectly. :)

Cons: Possibly hard to conceive of the cartesian explosion of 'flags.'  It's always useful to write out the equation and a table of key combinations.  The OutputCache directive caches the entire UserControl (ASCX) so you can't just cache a small portion of the UserControl.  The UserControl is the 'atom.'  You CAN, however, have multiple UserControls and cache each differently.  Be aware though that that can cause another combinatoric explosion if you're not careful.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

MVP = SPAM?

January 21, 2004 Comment on this post [10] Posted in ASP.NET | XML
Sponsored By

Wow, the MVP Announcements are out.   Lots of nice folks and friends have become members of what I call the "Microsoft House of Representatives" or more commonly known as the Microsoft Most Valuable Professional (MVP) Program. 

Here's what Microsoft says about MVPs:

  • Recognized: Microsoft MVPs are acknowledged by peers and also by Microsoft for their active participation in Microsoft technical communities around the globe.
  • Credible: Microsoft MVPs have demonstrated practical expertise providing the highest quality information and content.
  • Accessible: Microsoft MVPs are active technical community leaders sharing their experience with peers.

Recognized?  Yes, I'd know DonXml if I was behind him in line. (Which I was in NYC recently going into a dive bar)

Credible? Yes, Sam Gentile knows C#, my friends.

Accessible? Jeff Julian and John Bristowe are on MSN Messenger enough to be considered officially accessible.

I was poking around my never read SPAM folder, which contained 1002 emails this evening and found this:

"It is with great excitement that I can inform you that you have been awarded as a MVP in ASP.NET for your community contributions in the past year."

Well, holy crap and happy birthday! Good thing I'm not the MVP for Outlook, because Outlook said this good news was SPAM.

Thanks to whoever nominated me.  As a fiscally conservative ASP.NET developer, I promise to vote my conscience during the caucuses. :)

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

On Turning 0x1E and looking back at 2003

January 20, 2004 Comment on this post [3] Posted in ASP.NET | TechEd | Speaking | PDC | Africa | Tools
Sponsored By

I'm turning 30 on Thursday, which is a nice round number, until I turn 0x30 "again" in 18 years.  It's got me very nostalgic.  When I was in my early 20s I always expected to look at 30 as a big deal, and this may seem fairly geek of me, but once you grok number bases, it's hard to get as excited about numbers that are round in one base. :)

I'm pretty happy with the way things in general are going, looking back on 2003, here's some highlights:

So, here's to turning 30, 0x1E, 36 (octal), and 0001-1110 all in the same day.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Corillian Case Study on the Bank of Stockton

January 17, 2004 Comment on this post [0] Posted in Corillian
Sponsored By

We've just had another case study published on Microsoft.com, this time on The Bank of Stockton solution.  As you probably know, we dig .NET in a big way over here.  We've got some slick things cooking.  Expect to see more interesting technical stuff coming out of Corillian.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

New York City .NET Users Group and the Cold

January 17, 2004 Comment on this post [1] Posted in INETA | DasBlog | TechEd | Speaking
Sponsored By

It's cold, man.  We're in NYC to talk for INETA at the NYC .NET User's Group and crap, it's cold.  It was cold and crazy in Portland last week...around 21F and I thought THAT was cold.  We were out last night in Manhattan around 11:00pm and it was 1F. Yes, that's 1.  They said you can get frostbite in 30 minutes at the temperature.  Madness. 

Got to hang out with the legendary Stephen Forte, and my buddy Andrew Brust.  They have shown us a great time and had $300 worth of pizza to please the group!  This makes the 2nd continent I've spoken with Stephen on during our plans to take over the world when he's not climbing Mt. Everest.  Thanks to the guys and INETA for having me over.

If you want to see this very same presentation when I did it at TechEd, you can watch it online (with Audio and Video of the Demonstrations!) from TechEd 2003 and download the Powerpoint as well.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.