I’m back working on this Localization provider and thanks to some helpful comments I was able to make the base provider work right. I had originally gone down the path of creating a custom ResourceManager and everything that goes along with which is a fair bit of code and is kinda hard to follow. After the recent comments from the previous code I decided it’s easier to just implement a provider directly and I managed to get that working quickly enough.
So ASP.NET works a bit differently than Windows Forms in this respect. Windows Forms apps talk directly to a ResourceManager object, while ASP.NET internally talks to the Resource Provider. These providers are pretty close in their interface to the ResourceManager, but there’s a little less fuss involved in creating a provider. It seems odd that .NET doesn’t expose the a ResourceManager interface that could have been easily swapped out instead of ASP.NET having to invent a whole new model ontop of the ResourceManager, which is the provider model.
Anyway, implementing a basic ResourceProvider is fairly straight forward and it will handle explicit expressions and anything that normally fires through Page.GetLocalResourceObject/GetGlobalResourceObject which in turn pass on their work to the underlying provider.
So by implementing a basic Resource Provider explicit resources are handled, which I described in my last post:
<asp:LinkButton ID="LinkButton1" runat="server"
Text="<%$ Resources:LinkButton1Resource1.Text %>" />
Here ASP.NET goes out and looks for an exact resource match in the resource provider. The way this works is similar to the way that DataBinding works – it’s an ASP.NET Expression that generates an explicit code expression into the page code
private LinkButton __BuildControlLinkButton1()
{
LinkButton button1 = new LinkButton();
base.LinkButton1 = button1;
button1.ApplyStyleSheetSkin(this);
button1.ID = "LinkButton1";
button1.Text = Convert.ToString(base.GetLocalResourceObject("LinkButton1Resource1.Text"), CultureInfo.CurrentCulture);
}
That works fine now after some wrangling with the runtime/design time issues.
But the problem I face now is that the meta:ResourceKey evaluations (Implicit Resources) aren’t working. These Implicit resources are the key to the designer surface in Visual Studio and in general are a nice way that resources are pulled into the application.
<asp:LinkButton ID="LinkButton2" runat="server"
Text="Original Text" meta:resourcekey="LinkButton2" />
The meta:ResourceKey causes ASP.NET to go looking for any properties on that particular resource key and automatically assigns it to the properties. So if the resource file (or database in my case) include includes LinkButton2.Text and LinkButton2.ToolTip both of those properties will be set from the appropriate resources. This is a pretty cool feature especially since the designer can go out and look for any properties of controls that are marked with the [Localizable] attribute and can generate the appropriate Meta tags into the markup as well as into the resource provider (although it only does this for the Invariant Culture not all the other resx files that are available which is kind of a severe omission IMHO).
Anyway, the meta:resourcekey tags are serviced through the IImplicitResourceProvider interface. I haven’t found any decent documentation on this interface or any idea on how to hook this up. The interface is simple enough, but I’m not quite sure where the interface needs to be implemented. In fact, I dug through System.Web.Compilation trying to figure where ASP.NET uses this interface and I could only find a DefaultImplicitResourceProvider object that implements, but could not find anyplace it’s used.
Sooo… I simply tried to implement the interface on the main ResourceProvider and that does seem to work – sort of – it’s getting called, but it’s not working correctly. What I did is look at DefaultImplicitResourceProvider and more or less copy some of the implementation code from it, which ends up looking like this:
public class wwDbSimpleResourceProvider : IResourceProvider, IImplicitResourceProvider
// IResourceProvider Implementation here
#region IImplicitResourceProvider and related Members
public ICollection GetImplicitResourceKeys(string keyPrefix)
{
lock(this._SyncLockObject)
{
if (this._implicitResources == null)
this.EnsureGetPageResources();
}
if (this._implicitResources == null)
return null;
return (ICollection) this._implicitResources[keyPrefix];
}
public object GetObject(ImplicitResourceKey implicitKey, CultureInfo culture)
{
string ResourceKey = ConstructFullKey(implicitKey);
string CultureName = null;
if (culture != null)
CultureName = culture.Name;
else
CultureName = CultureInfo.CurrentUICulture.Name;
return this.GetObjectInternal(ResourceKey, CultureName);
}
Hashtable _implicitResources = null;
internal void EnsureGetPageResources()
{
IResourceReader reader = this.ResourceReader;
if (reader != null)
{
this._implicitResources = new Hashtable(StringComparer.OrdinalIgnoreCase);
foreach (DictionaryEntry entry1 in reader)
{
ImplicitResourceKey key1 = ParseFullKey((string)entry1.Key);
if (key1 != null)
{
ArrayList list1 = (ArrayList)this._implicitResources[key1.KeyPrefix];
if (list1 == null)
{
list1 = new ArrayList();
this._implicitResources[key1.KeyPrefix] = list1;
}
list1.Add(key1);
}
}
}
}
private static ImplicitResourceKey ParseFullKey(string key)
{
string text1 = string.Empty;
if (key.IndexOf(':') > 0)
{
string[] textArray1 = key.Split(new char[] { ':' });
if (textArray1.Length > 2)
{
return null;
}
text1 = textArray1[0];
key = textArray1[1];
}
int num1 = key.IndexOf('.');
if (num1 <= 0)
{
return null;
}
string text2 = key.Substring(0, num1);
string text3 = key.Substring(num1 + 1);
ImplicitResourceKey key1 = new ImplicitResourceKey();
key1.Filter = text1;
key1.KeyPrefix = text2;
key1.Property = text3;
return key1;
}
private static string ConstructFullKey(ImplicitResourceKey entry)
{
string text1 = entry.KeyPrefix + "." + entry.Property;
if (entry.Filter.Length > 0)
{
text1 = entry.Filter + ":" + text1;
}
return text1;
}
#endregion
The GetImplicitResourceKeys interface method indeed gets called and the code looks like it’s working in that it ends up pulling out the appropriate collections for each of the keys requested. However, the GetObject method is never actually called.
The last two methods are pretty much straight out of Reflector and DefaultImplicitResourceProvider with a couple of small adjustments. The rest of the code just uses the existing ResourceReader implementation to actually parse through the list of resources.
All of that seems to be working but I suspect there’s some sort of problem either in what gets returned in GetImplicitResourceKeys() or the format of the values stored isn’t quite right. It doesn’t help that I haven’t found anything that documents this. Or ResourceManager for that matter, but at least for ResourceManager there are a number of samples out there from Microsoft and otherwise.
Has anybody implemented IImplicitResourceProvider and can shed some light on where the interface needs to be hooked up and what the official formatting for GetImplicitResourceKeys() is? Or can anybody see why GetObject() on the IImplicitResourceProvider is not called after the values are passed back for what I think is correct?
Other Posts you might also like