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?
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
.