In ASP.NET 2.0 ResourceProviders allow extension of the native resource mechanism with your own resource backing store by implementing a custom ResourceProvider. I've recently published an extensive article that talks about this process in detail and why this can be quite useful.
One of the issues that I've been struggling with is that the ASP.NET ResourceProvider(s) are not directly accessible from your ASP.NET code. ASP.NET basically gives you access to HttpContext.GetGlobalResourceObject() and HttpContext.GetLocalResourceObject() but that's about as close as you can get to the loaded providers ( ASP.NET actually loads many instances of providers - one for each resource set).
Since I built a provider that allows live resource editing as part of the application, and so it's only logical that you would want to see the changes in the resource provider either immediately or after explicitly refreshing the resources. As it turns out this is not easy to do because ASP.NET hides the Providers from you. No unloading for you!
In the past I've used a hack to accomplish this: Basically force the AppDomain to unload by either calling HttpRuntime.UnloadAppDomain() or even more hacky by touching web.config causing the timestamp to change and thus forcing ASP.NET to reload the application. Both of these actually work, although both have security issues you need to worry about. The former requires full trust, the latter that you have write file access rights to Web.config neither of which is guaranteed to be available.
A better way: Hang on to your Hats - er, Providers
So as I was wrapping up my article I got to thinking about this problem again. With a custom provider implementation it's actually possible to keep track of the providers as they are loaded. Once you have a list of the providers, unloading the resources is really not difficult - since resources are typically cached in Hashtables/Dictionaries in memory it's easy to clear them out by releasing each of the dictionaries holding the resource data.
So here's what I did in my provider (which otherwise is fairly standard implementation so this should be easy to apply to any custom implementation).
I set up a static LoadedProviders property (List<>) that keeps track of all the loaded provider instances, and a method called UnloadProviders() to loop through the list and then call a custom interface method that unloads the providers.
To do this I have to:
- Create a static LoadedProviders property and a static UnloadProviders() method somewhere (I used a Configuration object)
- Create a custom interface so the unload functionality can be accessed generically across multiple providers
- Implement the add code in the constructor of each Provider
- Implement the the custom interface to unload the resources
So let's see how to do this. The first step is to create the static holding container and unload routine for the Provider. I chose to add these static members to my provider's Configuration object which is already hosting a bunch of static objects. I didn't want to put it on the actual provider classes themselves because I actually have two specific implementations - I only want one place to call UnloadProviders on, without branching logic.
So on my wwDbResourceConfiguration class I have these static members:
/// <summary>
/// Keep track of loaded providers so we can unload them
/// </summary>
internal static List<IwwResourceProvider> LoadedProviders = new List<IwwResourceProvider>();
/// <summary>
/// Allow unloading of all loaded providers
/// </summary>
public static void UnloadProviders()
{
foreach (IwwResourceProvider provider in LoadedProviders)
{
provider.ClearResourceCache();
}
}
Next implementing the actual interface which just has a single method called ClearResourceCache() (actually a recommended method and implemented by .NET providers themselves although apparently not used much <s>):
public interface IwwResourceProvider
{
/// <summary>
/// Interface method used to force providers to register themselves
/// with wwDbResourceConfiguration.LoadProviders
/// </summary>
void ClearResourceCache();
}
Next I have to make sure the provider is added to the LoadedProviders in the constructor of the Provider:
public wwDbSimpleResourceProvider(string virtualPath, string className)
{
_ResourceSetName = className;
wwDbResourceConfiguration.LoadedProviders.Add(this);
}
Finally implement IwwResourceProvider.ClearResourceCache() on the Provider:
public void ClearResourceCache()
{
this.resourceCache.Clear();
}
This implementation will vary depending on how you implement your resource manager, but if you follow the 'stock' model of cached dictionaries (and there's really no reason not to since it works well!) that's basically all it takes.
If you're using a ResourceManager in your Provider for the implementation you can use code like this:
public void ClearResourceCache()
{
// *** Just force the resource manager to be completely reloaded
this.ResourceManager.ReleaseAllResources();
}
but you may have to override the ResourceManager.ReleaseAllResources method in your custom ResourceManager implementation.
So now from anywhere in my UI I can call:
wwDbResourceConfiguration.UnloadProviders();
to refresh the resource cache. In my online resource editor sample this is amounts to a button:
which immediately and very quickly forces a refresh of the application's resources from the database. Compared to the old approach that forced an App Refresh this feels much smoother and it works in without any security implications.
And that's it! Reloadable resources. It's not terribly clean but it works fine. It sure would be a lot easier if ASP.NET provided some high level control over its internal Provider list. Certainly unloading resources could be handy.
Pretty cool - I'm stoked, but a little dismayed that this didn't occur to me a bit earlier <s>...
Other Posts you might also like