Search code examples
c#.netresourcesresourcemanager

How to know if a resource file is not found without catching MissingManifestResourceException


I have .resx files located in different namespace for each client of my application. Say Resources.FirstClient.Home.resx and Resources.SecondClient.Home.resx. Most of the strings are common for each client so I want to introduce a default namespace Resources.Default.Home.resx that I can use if the strings were not found for the client.

The problem I have is that I don't know how to determine if a resource file is not found. (by example Resources.ThirdClient.Home.resx does not exist so I want to use "Resources.Default.Home.resx instead)

Right now, the only way I've found to handle it is to catch the MissingManifestResourceException thrown:

var resourceManager = new ResourceManager("Resources.ThirdClient.Home", typeof(ResourceFactory).Assembly);

string str;
try
{
    str = resourceManager.GetString("myString");
}
catch(MissingManifestResourceException e)
{
    resourceManager = new ResourceManager("Resources.DefaultClient.Home", typeof(ResourceFactory).Assembly);
    str = resourceManager.GetString("myString");
}

Is there a better way to know if the resource file is missing?


Solution

  • We ended up loading instances of ResourceManager on the start-up of the application and keeping them in a Singleton instance, then using those instances when trying to get a string.

    First, we load the resource managers to a dictionary of dictionaries, with the first key being the client name and second key being the name of the resource file (ex : resourceManagers["ThirdClient"]["Home"]). We also have a default value in the _tenants list :

    var resourceManagers = new Dictionary<string, IDictionary<string, IResourceManagerAdapter>>();
    
    foreach (var tenant in _tenants)
    {
        var resourceDictionnary = typeof(ResourceFactory)
            .Assembly
            .GetTypes()
            .Where(t => t.IsClass && t.Namespace == "Resources." + tenant)
            .ToDictionary<Type, string, ResourceManager>
                 (resourceFile => resourceFile.Name,
                  resourceFile => new ResourceManager("Resources." + tenant + "." + resourceFile.Name, typeof(ResourceFactory).Assembly));
    
        resourceManagers.Add(tenant, resourceDictionnary);
    }
    

    We keep the resourceManagers dictionary generated in a singleton instance that is injected in our services using a DI container.

    Then we use the injected dictionary to get the string or a default if it is not found :

    public string GetString(string classKey, string resourceKey)
    {
        if (!resourceManagers.ContainsKey(client) || !resourceManagers[client].ContainsKey(classKey))
            return resourceManagers[default][classKey].GetString(resourceKey);
    
        return resourceManagers[client][classKey].GetString(resourceKey, _cultureService.GetCurrentCulture())
            ?? resourceManagers[default][classKey].GetString(resourceKey);
    }
    

    From the tests we've done, this approach is about 1000 times faster than using a new ResourceManager and a try catch on every GetString.