Skip navigation.

Are ASP.NET Themes Worth The Trouble?All recent postsShow Some Respect For Your Users

So How Do You Set The Last-Modified Header?

What happens you want to set the Last-Modified HTTP header and all you have is a UTC date? Most likely you’ll get an exception.

The culprit here is the sloppily designed interface of HttpCachePolicy. It so happens that you should pass to the SetLastModified a non-UTC date because here’s what happens inside the method:

public void SetLastModified (DateTime date)
{
   DateTime utcDate = DateTimeUtil.ConvertToUniversalTime(date);
   this.UtcSetLastModified(utcDate);
}

What if I only have a UTC date? As an aside, handling time zone differences in .NET is very problematic. It’s a big grey area. I’ve seen several products which used local dates for convenience—a practice which eventually came back to bite them in the rear. Having been burned before, we now store all dates in UTC and then recalculate them to each user’s time zone preferences as needed.

It’s odd that MSDN doesn’t mention the fact that SetLastModified expects a local date. Converting a UTC date to a local date only to have it converted back is idiotic. Worse still, it throws an exception saying the utcDate parameter is wrong. Talk about confusion.

It also turns out there’s a UtcSetLastModified, but someone, in his/her infinite wisdom, made it protected. Why? With a little reflection you can still get to it:

MethodInfo m = Response.Cache.GetType ().GetMethod (
 "UtcSetLastModified",
 BindingFlags.NonPublic | BindingFlags.Instance);

m.Invoke (
 Response.Cache, 
 BindingFlags.NonPublic | BindingFlags.Instance, 
 null, new object[] { someUtcDate }, null);

My beef with the interface itself

In a shameless appeal to authority, I’ll quote Steve McConnell’s seminal book Code Complete where he talks about “semantic violations of encapsulation” and gives a few examples very similar to the problem at hand.

“The problem [with the said examples] is that they make the client code dependent not on the class’s public interface, but on its private implementation. Anytime you find yourself looking at a class’s implementation to figure out how to use the class, you’re not programming to the interface; you’re programming through the interface to the implementation. If you’re programming through the interface, encapsulation is broken, and once encapsulation starts to break down, abstraction won’t be far behind.”

And along the same lines:

“If a class offers an incomplete set of services, other routines might find they need to read or write its internal data directly. That opens up the class, making it a glass box instead of a black box, and it virtually eliminates the class’s encapsulation.”

HttpCachePolicy is a good example of a poorly designed interface. It has more leaks than the navy.

  • It raises an exception disclosing an implementation detail.
  • It doesn’t offer a complete set of services.
  • It forces you to hand it a local date (bad assumption!).
  • It’s not self-documenting.

Looking back, I realize I had to dive into the .NET Framework source code on too many occasions. For example, you can’t understand how .NET collections work without a bottle and Reflector (read Collection Madness part 1 and part 2).

My faith in and patience with the .NET framework designers fades by the day.

Comments

Comment permalink 1 Josh Stodola |
That is confusing! And a pain. You should attempt to maintain the faith in .NET though; this issue ain't no show-stopper.

Emails and Notifications

Would you like to be notified when somebody responds to this post?  Would you like to have these comments emailed to you?

TrackBacks

Sorry, TrackBacks are not allowed.

Submit your comment

Please enter only text since all HTML tags except hyperlinks will be stripped. Hyperlinks will become live links. Any comments with flaming or offensive language will be deleted. Be courteous to other posters. Thank you.

Your name (required):
Your email (optional):
Your site's URL (optional):
Enter this number
Type in the number above:
Comment (required):