.NET Framework v2.0 SP2, which will be included with .NET Framework v3.5 SP1, is scheduled to release this summer.  This release contains a long awaited ASP.NET Cache feature, which will notify you when an entry becomes invalid and leave the stale entry in the cache while you generate a replacement in the background.  Therefore, requests are not blocked attempting to access the cache entry while it is being updated, and the update is done by a single thread.  Here's the specification:

 

 

A new Cache.Insert overload:

 

void Cache.Insert( String key,

                   Object expensiveObject,

                   CacheDependency dependencies,

                   DateTime absoluteExpiration,

                   TimeSpan slidingExpiration,

                   CacheItemUpdateCallback onUpdateCallback

                 );

 

This method inserts an object into the cache, with optional dependencies and expiration policy, and a callback that is invoked before the entry is removed.  Objects inserted into the cache with this method should be expensive to generate.  You should not use this method for objects that are inexpensive to generate, because there is overhead in the implementation.  When the callback is invoked, the application developer should generate a new object, with new dependencies and expiration policy, and return it via the callback’s out parameters.  The stale cache entry will be accessible while the callback is executing and the replacement object is generated.

 

Cache entries inserted by this method are not subject to eviction by the cache memory manager.  These objects only become invalid when they expire or a dependency changes, and therefore these objects MUST have some form of invalidation (i.e., it is not possible to insert the entry with a null dependency, NoAbsoluteExpiration, and NoSlidingExpriation).

 

 

A new update callback:

 

void CacheItemUpdateCallback( String key,

                              CacheItemUpdateReason reason,
                              out Object expensiveObject,

                              out CacheDependency dependencies,

                              out DateTime absoluteExpiration,

                              out TimeSpan slidingExpiration

                            );

 

When the cache entry becomes invalid, ASP.NET invokes this callback, allowing the application developer to generate a replacement cache entry.  The developer is passed only two pieces of information:  the key for the cache entry, and an enum value indicating why the entry is invalid.  The rest of the arguments to the callback are output-only parameters, allowing references to the newly generated object, dependencies, and expiration policy to be passed back and subsequently inserted into the cache by ASP.NET.

 

Do not modify the existing cache entry.  The stale cache entry is still alive and potentially being used by other threads.  It is not included in the callback arguments in order to minimize the likelihood of a developer accidentally updating portions of the cache entry.  Although this pitfall exists today in the cache API (e.g. a cache entry could be modified in a non-thread-safe manner by an ASP.NET developer), we think leaving the cached value out of the callback arguments will reduce the likelihood of a developer stumbling into this.

 

To remove the cache entry, simply set the argument expensiveObject to null.  Otherwise, set it to the newly generated replacement object.  The developer MUST also specify expiration and/or dependency information using the other out parameters.  As stated above, the cache entry is not subject to eviction by the cache memory manager, and therefore must have some form of invalidation.  It is an error to pass back a non-null value for expensiveObject while setting dependencies to null, absoluteExpiration to NoAbsolteExpiration, and slidingExpriation to NoSlidingExpiration.  The cache entry will be removed if this occurs.

 

If the developer’s callback throws an exception, ASP.NET will suppress the exception and remove the cache entry.  The exception needs to be suppressed because cache item updates occur in the background where there is no call path for routing the exception back to user code.  The cache entry is removed because without a new cache value, ASP.NET has no way of knowing what the correct behavior is for updating the cached item.

 

The update callback will not be called if the cached item was explicitly removed via a call to Cache.Remove, or a call to Cache.Insert (Insert removes a pre-existing entry).  These operations indicate the developer’s intent to remove the item, so the update callback will not be triggered.

 

 

A new update reason enumeration:

 

public enum CacheItemUpdateReason {

    // The item was removed from the cache because either

    // the absolute or sliding expiration interval expired.

 

    Expired = 1,

 

    // The item was removed from the cache because an

    // associated cache dependency was changed.

 

    DependencyChanged = 2

}

 

Unlike the CacheItemRemovedReason enumeration, the update reason enumeration does not include values for Removed (see explanation above) or Underused (as mentioned above, the cache entry is not subject to eviction by the cache memory manager).

 

Regards,

Thomas