Trying to implement IImplicitResourceProvider
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
- Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
- Resolving Paths To Server Relative Paths in .NET Code
- Map Physical Paths with an HttpContext.MapPath() Extension Method in ASP.NET
- Getting the Client IP Address in ASP.NET Core
- Back to Basics: Rewriting a URL in ASP.NET Core
The Voices of Reason
# re: Trying to implement IImplicitResourceProvider
I'm building a custom resource provider. It works in run time (as you described), but when building from VS2005, it throws errors "Object reference not set to an instance of an object. ..... StartProfileCodeSummary.ascx"
The error points to the meta:resourceKey tag:
<asp:Literal ID="litTitle" runat="server" Text="{PC_TITLE}" meta:resourcekey="litTitleResource1"></asp:Literal>I tried implementing IImplicitResourceProvider as you described above, but it actually increased the number of these errors. (Before it only pointed to a fist meta:resourceKey tag in weach file, now it points to every tag on every file).
Any help would be appreciated.
# re: Trying to implement IImplicitResourceProvider
For what it's worth I've posted a complete article on how to implement a resource provider complete with full samples and an online resource editor:
http://www.west-wind.com/weblog/posts/117287.aspx
# re: Trying to implement IImplicitResourceProvider
I managed to solve the problem (without actually implementing IImplicitResourceProvider).
The problem was that i used HttpContext.Current.Cache in GetResourceCache to cache my custom data. I suspect that HttpContext.Current is null during build. So i after removing any reference to HttpContext.Current.Cache it builds without any errors.
# re: Trying to implement IImplicitResourceProvider
You will need to implement IImplicitResourceProvider if you want meta:ResourceKey lookups to work. Without it those keys will never be retrieved and ASP.NET will just use the default values instead.
# re: Trying to implement IImplicitResourceProvider
in EnsureGetPageResources()
{
ArrayList list1 = (ArrayList)this._implicitResources[key1.KeyPrefix];
}
you must use
if (!this._implicitResources.containsKey(key1.KeyPrefix))
It's the same thing in
GetImplicitResourceKeys
{
return (ICollection) this._implicitResources[keyPrefix];
}
it's better if you write
if (this._implicitResources.containsKey(keyPrefix))
return (ICollection) this._implicitResources[keyPrefix];
else
return null;
Else it's OK.
dgavarin
# re: Trying to implement IImplicitResourceProvider
Even though my pages work as expected, I have an issue with my resource provider. When I compile my website, the compiler gives errors "the resource object with key 'AccountTeamEmail' not found' error.
I use explicit resource expression for all my resource access. The compiler generate one error message for each expression. What I have missed?
here are my expressions:
<code land="c#"><asp:Label ID="label1" runat="server" Text="<%$ Resources:UIText, UT~TEST1 %>"></asp:Label><br />
<asp:Label ID="label2" runat="server" Text="<%$ Resources:UIText, HTMLOrderApprovalHeading %>"></asp:Label><br />
<asp:Label ID="label3" runat="server" Text="<%$ Resources:UIText, HTMLShoppingCartManyItems %>"></asp:Label><br /><br />
<code>
Thanks,
Hua
# re: Trying to implement IImplicitResourceProvider
I've been looking at implementing the ResourceProviderFactory myself and have come across your site a couple of times.
It seems as though you dont need to implement the IImplicitResourceProvider interface unless you want to modify the behaviour of DefaultImplicitResourceProvider. If your resource provider doesn't implement the IImplicitResourceProvider interface, the DefaultImplicitResourceProvider gets loaded and uses the ResourceReader Property of your resource provider to get the resource keys.
Hope this helps
Rich
# re: Trying to implement IImplicitResourceProvider
are you using a Web Application Project or a simple web site ? In Web Application Project, the IImplicitResourceProvider methods seem to be never called... apparently, the DefaultImplicitResourceProvider is always used.
Could you try that ?
Jérémie
# re: Trying to implement IImplicitResourceProvider
# re: Trying to implement IImplicitResourceProvider
# re: Trying to implement IImplicitResourceProvider
Does anyone have this same problem? I've tried implementing IImplicitResourceProvider members in my provider and also not implementing them so that it uses the default. What is really strange is that somtimes at compile time it picks up the resources for the user control pages and somtimes it doesn't.
Any help much appreciated.
# re: Trying to implement IImplicitResourceProvider
I have a problem though, the GetObject fires twice for each control, when I believe it should fire once. I tried stepping through the code several times, its quite difficult to isolate why it does that.
The way I want my code to work, is that I already decided the culture before the provder gets called, so I don't need the culture invariant one (the one it uses as fallback), so inside GetObject, I set the culture to what I want and ignored the culture invariant one which it passes in.
Even though it didn't throw an error, it actually called the GetObject twice. I am starting to think perhaps, ASP.NET always create a copy of resources that it will use as fall back regardless of whether you want it or not, even if the two copies points to the same culture.
I will appreciate any help. thanks a lot.
# re: Trying to implement IImplicitResourceProvider
I think I did something wrong in the implementation, I rewrote the code, took out several indirection and it stopped calling twice.
However, I discover something about the GetObject of the IImplicitResourceProvider, it does not fire at runtime, at load time, GetImplicitResourceKeys fire for each implicit resourceKeys and somehow build up the list (in its head somewhere) of those it can find resourcevalues for . Subsequently, each implicit resourcekeys call go to the IResourceProvider GetObject with full qualified names.
What I am still struggling to understand is that is this correct behavior or I am still missing something.
Any help will be appreciated. Thanks.
# re: Trying to implement IImplicitResourceProvider
The app gets resource strings from a DB. Running SQL Profiler during a clean and build cycle on the project containing the IResourceProvider shows up in the profiler trace. The SQL queries that are executed return data.
Debugging does not show where the output (my assumption is that .resx files must eventually be created by this process) ends up. Is my assumption correct here? Do you know what should happen after resource data has been retrieved from the DB?
Thanks in advance,
Mike Godfrey.
# re: Trying to implement IImplicitResourceProvider
# re: Trying to implement IImplicitResourceProvider
Could you please explain why you do that because I don't see why?
According to the documentation, https://msdn.microsoft.com/en-us/library/system.web.compilation.iimplicitresourceprovider(v=vs.110).aspx there is no need for that:
"If you create a custom resource provider, you do not need to provide customized support for implicit resource localization. Implicit localization will work with your resource provider."
And according to this post the implicit resources are resolved runtime from IResourceReader on the IResourceProvider
https://msdn.microsoft.com/en-us/library/aa905797.aspx
# re: Trying to implement IImplicitResourceProvider
The code Brett sent simplified the implementation (removing the caching and nested dictionaries) which a) made it easier to see what's going on and more importantly b) caused the code to work by creating the list on the spot and returning it. Less indirection, although it's probably a little slower. Then again this code should only fire once per page with ASP.NET caching the Implicit resource keys once they've been returned so the overhead is not anything to worry too much about.
This is all a little free form in this post, but I'm just kinda brain dumping as I'm going. I'll eventually clean this up and write this up in full so it makes a little better sense, but in the meantime this serves as scratchpad for me as well as an avenue to solicit some input!
Thanks for listening <g>...