State Street Gang
.NET, straight up

Safest Way to Get the Cache Object

May 20, 2008 11:23 by will

This is one of those "I keep forgetting this so I'd better put it in a blog post" blog posts. 

We've all seen this:

object bar = HttpContext.Current.Cache["foo"];

And we've all had null reference exceptions thrown because of it.  Usually, we duct tape it back together with the pitiful:

if (HttpContext.Current == null)
    throw new InvalidOperationException
        ("Lol you're screwed");
object bar = HttpContext.Current.Cache["foo"];

Or we add on more logic to return a non-offensive result that is incorrect, but since there is no current context (usually happens when the web server is recycling), what do we care? 

There is a better way.  One where you will always get the cache, and it will never be null:

object bar = HttpRuntime.Cache["foo"];

No matter what's going on, the HttpRuntime's cache property will never be null.  And it's the same object as that in HttpContext.Current.  If you don't believe me, just slap the following in any ASP.NET page:

Assert.IsTrue(object.Equals(HttpContext.Current.Cache,HttpRuntime.Cache);

Tags:
Categories: ASP.NET | Tips
Actions: E-mail | Permalink | Comments (1) | Comment RSSRSS comment feed

Generics and the Session State

January 10, 2008 09:50 by Will

A minor buzzfest about type safe HttpSessionState handling going on now in the upcoming story queue at DNK.  There are a couple of decent patterns being shared, so I figured I'd add in my own favorite type-safe session state handler.

The pattern uses generics (I loev generics) and works very well for data objects specifically designed to be stored in the session. 

The pattern goes like this:  A static SessionHelper class exposes a static, generic method for retrieving objects from the session state.  The method has one type parameter for the type of object that the caller wants.  The caller must supply a key under which the object is stored.  Simple, no?

Yes.  Too simple.  But here is where the magic happens.  Callers must also pass in a delegate that creates the default instance of the object.  Essentially, this delegate allows the helper to "set" the value if not found.  And the delegate uses generics, so the set logic is type safe as well.

Here's the implementation of the SessionHelper method

public class SessionHelper
{
    /// <summary>
    /// Retrieves an object from the session state or, if not found, creates it
    /// </summary>
    /// <remarks>
    /// A valid default value for a reference type is null.  It is up to the caller
    /// to determine the default value for value types.
    /// </remarks>
    /// <example>
    /// // Used to keep track of links a user clicks on
    /// public class UriHistory
    /// {
    ///    // Constructor is protected so that it can't be
    ///    // accessed by outside callers
    ///    protected UriHistory() { }
    ///    // lots of implementation here
    ///    // left out for brevity
    ///    // Retrieves Uri history for the current user
    ///    public static UriHistory CurrentUriHistory
    ///    {
    ///        get
    ///        {
    ///            SessionHelper session = new SessionHelper();
    ///            return session.Get<UriHistory>("UriHistory",
    ///                delegate
    ///                {
    ///                    return new UriHistory();
    ///                });
    ///        }
    ///    }
    /// }
    /// </example>
    /// <typeparam name="T">The type of object to retrieve</typeparam>
    /// <param name="key">A string key for the item</param>
    /// <param name="createDefault">A <seealso cref="CreateDefault"/> delegate used to
    /// create the default value for an item if it does not exist in the session</param>
    /// <returns>The object if found, the </returns>
    public T Get<T>(string key, CreateDefault<T> createDefault)
    {
        T retval;
        // if all is well, grab from the session
        // otherwise use the createDefault delegate
        if (SessionHelper.Session != null
            && SessionHelper.Session[key] != null
            && SessionHelper.Session[key].GetType() == typeof(T))
        {
            retval = (T)SessionHelper.Session[key];
        }
        else
        {
            retval = createDefault();
            SessionHelper.Session[key] = retval;
        }
 
        return retval;
    }
 
    /// <summary>
    /// The current session
    /// </summary>
    private static HttpSessionState Session
    {
        get
        {
            // HttpContext can be null when the website is shutting down
            if (HttpContext.Current != null)
                return HttpContext.Current.Session;
            return null;
        }
    }
}

And here is the delegate:


/// <summary>
/// Used by <seealso cref="SessionHelper.Get{T}"/> in order to create
/// the default value for a session item
/// </summary>
/// <remarks>This method may return null for reference types.</remarks>
/// <typeparam name="T">The type of item to be created</typeparam>
/// <returns>A default value</returns>
public delegate T CreateDefault<T>();

Notice the example in the XML comments.  It shows how the second part of this pattern can be used to create a pseudo-singleton pattern using the session helper, rather than the CLR, to make sure only one instance for a particular object is available within the current context.  With a singleton, a singleton object only has one instance per AppDomain.  With this pattern, only a single instance of each object is allowed per Session. 

Here's a nicer formatted version of the example class for easier viewing:

// Used to keep track of links a user clicks on
public class UriHistory
{
    // Constructor is protected so that it can't be
    // accessed by outside callers
    protected UriHistory() { }
    // lots of implementation here
    // left out for brevity
    // Retrieves Uri history for the current user
    public static UriHistory CurrentUriHistory
    {
        get
        {
            SessionHelper session = new SessionHelper();
            return session.Get<UriHistory>("UriHistory",
                delegate
                {
                    return new UriHistory();
                });
        }
    }
}

The nicest thing about this pattern is that it prevents session state management concerns from becoming a dependency nightmare.  The logic for managing session state is kept within the SessionHelper.  The logic for creating default implementations of your objects rests within the objects themselves and not some third object that stands between the session and your business objects.  Plus, its type safe and uses generics, which I love.

The only sore point I have with this code is the manner in which the current HttpSessionState is retrieved.  I know that there is a better way to do this that works even when the website is shutting down and that facilitates unit testing, but I can't remember the pattern nor can I find it anywhere.  If you happen to know, please put it in the comments.  Thanks!


Tags:
Categories: ASP.NET
Actions: E-mail | Permalink | Comments (3) | Comment RSSRSS comment feed

...Or does it???

December 28, 2007 02:00 by Will

Well, I finally solved the mystery of what was causing wildcard mapping of aspnet_isapi in IIS 6 to fail so horribly.

It appears that checking that simple little box just breaks the ever living crap out of the wildcard mapping.  But, you say, I know aspnet_isapi.dll is there, so what does having this fine checkbox checked hurt?  From TechNet:

Select Verify that file exists to instruct the Web server to verify the existence of the requested script file and to ensure that the requesting user has access permission for that script file. If the script does not exist or the user does not have permission, the appropriate warning message is returned to the browser and the script engine is not invoked.

Yes, that checkbox instructs IIS to check the existance of the requested item prior to handing things off to ASP.NET.  Bugger.

This is one of those situations where skipping a line in a multipage instruction manual is a BAD thing.  Here's a hint:  When you're writing instructions and are trying to get across a very simple, yet extremely important step in the process,

MAKE SURE YOU PUT SOME EMPHASIS ON IT.

It doesn't hurt you, and it may save some poor slob a couple weeks worth of cursing.

So, it may be that Frontpage 2002 Server Extensions doesn't break URL rewriting after all.  I'm going to back up my server tomorrow and try some experimentation on it.  I'll post the results here later on.


Frontpage 2002 extensions breaks UrlRewriting.NET

December 26, 2007 18:06 by Will

So I have my lovely little DotNetKicks-based website up and running (its LOLKicks, by the way).  Took me a week to get it working properly, primarily because of URL Rewriting.

What is URL Rewriting, you ask?  TL;DR version:  It makes ugly URLs like

http://foo.com/bar/default.aspx?user=123&fail=true

into nice looking URLs like

http://foo.com/user/123/fail

This is primarily used for search engine optimization, and to look all cool and stuff.

Anyhow, DotNetKicks makes extensive use of URL rewriting through a nice little project called UrlRewriting.NET.  It generally works perfect, but apparently in my case it has a weakness...

My server is a virtual machine running the 64 bit version of Windows Server 2003.  Now, as it comes, the server has pretty much everything you need to host an ASP.NET web site.  However, it is preconfigured with some nonsense that requires IIS to run in 32bit compatability mode.  Usually, that isn't an issue.  Hell, it still might not be.

Because its a 64 bit box, it doesn't come with Frontpage 2002 Extensions installed.  FPE help make modifying websites easier by providing extensions that programs like Visual Studio and Expression Web can use to directly modify files on the web server over the internet.  That's a nice thing; something I would like to take advantage of, moreso now because I want to skin my DNK site. 

It seems pretty obvious what you do at this point:  You download the Frontpage 2002 Extensions and install them on the web server.  The 32bit version won't install on a 64 bit machine, so you have to install the 64 bit version, even though IIS is running in 32bit compat mode.  Fine.

Installing FPE is quick and easy and it appears to work.  Great.  Except for one problem...  Once you install them on a web server, UrlRewriting.NET won't work.  Not even if you uninstall FPE.

I've learned that UrlRewriting.NET won't work if you install FPE 64bit before you create your website, after you create your website, and if you uninstall FPE in some vain hope of fixing your broken website.

Currently, I have found no solution other than wiping the machine to fix the issue.  I have a post about it over at the UrlRewriting.NET discussion group here.  It kinda sucks because I'm going to end up having to wipe this server, most likely, in order to fix my error. 

Update:  Restoring IIS' configuration to a previous state did not fix the break.  I'm going to have to wipe tomorrow.  Not that I don't normally wipe, I'm just sayin'...


Tags:
Categories: ASP.NET | Debug
Actions: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed