Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs
Contact   •   Articles   •   Products   •   Support   •   Search
Ad-free experience sponsored by:
ASPOSE - the market leader of .NET and Java APIs for file formats – natively work with DOCX, XLSX, PPT, PDF, images and more

Resource Unloading in a custom ASP.NET Localization Resource Provider


:P
On this page:

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:

  1. Create a static LoadedProviders property  and a static UnloadProviders() method somewhere (I used a Configuration object)
  2. Create a custom interface so the unload functionality can be accessed generically across multiple providers
  3. Implement the add code in the constructor of each Provider
  4. 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>...

Posted in ASP.NET  Localization  

The Voices of Reason


 

Speednet
July 24, 2007

# re: Resource Unloading in a custom ASP.NET Localization Resource Provider

Fantastic technique! I will definitely be using this on the next project I build requiring resources.

I also liked the linked technique to unload the domain. I must have missed that one last year.

I created a VB.NET version:

''' <summary>
''' Restarts the Web Application. Requires either Full Trust (HttpRuntime.UnloadAppDomain) or Write access to web.config.
''' </summary>
Public Function RestartWebApplication() As Boolean

    Try
        HttpRuntime.UnloadAppDomain()
    Catch

        Try
            Dim ConfigPath As String = httpContext.Current.Request.PhysicalApplicationPath + "\web.config"
            System.IO.File.SetLastWriteTimeUtc(ConfigPath, DateTime.UtcNow)
        Catch
            Return False
        End Try

    End Try

    Return True
End Function

mizokunimito
March 14, 2013

# re: Resource Unloading in a custom ASP.NET Localization Resource Provider

Hi Rick,
what about ResourceReader cache, have you found any way to invalidate the resources keys list of page?
If you plan to add a new resource, the ResourceManager ignores it until the ResourceReader is aware of its presence.

KA
October 06, 2014

# re: Resource Unloading in a custom ASP.NET Localization Resource Provider

Do you have any other idea how this can be solved.
I don't like it this way. Because all the ResourceProviders with a lots of resources already cached are kepth in memory.
This way the app doesn't scale.

internal static List<IwwResourceProvider> LoadedProviders this is the ugly part. The static property :)

Rick Strahl
October 06, 2014

# re: Resource Unloading in a custom ASP.NET Localization Resource Provider

@KA - You tell me. Why do you say the static is bad in this situation? Just because you've read somewhere that static is evil and not easily testable in some situations?

Resources are one time loaded entities and that's exactly the problem a static is supposed to solve - it's a reliable way to have a singleton object that is shared across threads.

Loading resources happens once per resource set, so this is not a high impact operation since it only happens once (per set). After that initial load, they're just parked in memory. What's not scalable about that? That's exactly how the .NET resource managers work, precisely because it IS scalable and works for a multithreaded environment.
 

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2019