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

Trying to implement IImplicitResourceProvider


:P
On this page:

 

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?

Posted in ASP.NET  Localization  

The Voices of Reason


 

Rick Strahl
October 07, 2006

# re: Trying to implement IImplicitResourceProvider

So after a lot of frustration and a kindly soul who sent me an example (Brett Jacobsen) i managed to get IImplicitResourceProvider to work. I snagged the above code out of the DefaultImplicitResourceProvider implementation, and I plugged it into my code, stepping through it etc. It all looked right but somehow it apparently didn't do the right thing.

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.

public ICollection GetImplicitResourceKeys(string keyPrefix)
{
    List<ImplicitResourceKey> keys = new List<ImplicitResourceKey>();
    if (keyPrefix.StartsWith("LinkButton1"))
        keyPrefix = keyPrefix;

    foreach (DictionaryEntry dictentry in this.ResourceReader)
    {
        string key = (string)dictentry.Key;

        if (key.StartsWith(keyPrefix + ".", StringComparison.InvariantCultureIgnoreCase) == true)
        {
            string keyproperty = String.Empty;
            if (key.Length > (keyPrefix.Length + 1))
            { 
                int pos = key.IndexOf('.');
                if ((pos > 0) && (pos == keyPrefix.Length))
                {
                    keyproperty = key.Substring(pos + 1);
                    if (String.IsNullOrEmpty(keyproperty) == false)
                    {
                        Debug.WriteLine("Adding Implicit Key: " + keyPrefix + " - " + keyproperty);
                        ImplicitResourceKey implicitkey = new ImplicitResourceKey(String.Empty, keyPrefix, keyproperty);
                        keys.Add(implicitkey);
                    }
                }
            }
        } 
    }
    return keys;
    //Debug.WriteLine("GetImplicitResourceKeys:" + keyPrefix);

    //ICollection col = this.EnsureGetPageResources(keyPrefix);

    //return col;  //(ICollection) this._implicitResources[keyPrefix];
}


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

Localization
October 08, 2006

# ASP.NET Forums - IResourceProvider.GetObject() - Culture is always Invariant


Rick Strahl's Web Log
October 24, 2006

# Custom Localization ResourceProviders and Compiler Errors - Rick Strahl's Web Log

I’ve been struggling with building a custom ResourceManager and hooking it into ASP.NET over the last few days as part of a localization engine that is data driven rather than through Resources. I’ve had a separate engine for this for sometime, but it hasn’t been hooked into the .NET or ASP.NET...

# DotNetSlackers: Trying to implement IImplicitResourceProvider


ASP.NET Forums
June 13, 2007

# IResourceProvider.GetObject() - Culture is always Invariant - ASP.NET Forums


Mikhail
July 23, 2007

# re: Trying to implement IImplicitResourceProvider

Hi there,
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.

Rick Strahl
July 23, 2007

# re: Trying to implement IImplicitResourceProvider

There's probably a problem in your provider implementation. The error is coming from inside the provider most likely so you'll need to step into into it. Creating a resource provider can be tricky and any error in your code will ripple back up to the ASP.NET page UI, so you have be very defensive with your code. And you need to decide how to capture errors and deal with things like missing resources in your resource store (which is the most likely cause of your problems).

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

Mikhail
July 24, 2007

# re: Trying to implement IImplicitResourceProvider

Thanks, Rick

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.

Rick Strahl
July 24, 2007

# re: Trying to implement IImplicitResourceProvider

You wouldn't want to use Cache even if it wasn't for the compile time issue. Cache is not guaranteed to stay cached under memory pressure, so you DEFINITELY need a permanent store.

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.

dgavarin
August 02, 2007

# re: Trying to implement IImplicitResourceProvider

I think that your code error is

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

Hua Yang
August 05, 2007

# re: Trying to implement IImplicitResourceProvider

I created a SQL simple resource provider. It works fine at runtime, I can use it to retrieve resources from my database for my pages.
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

Richard Perry
August 06, 2007

# re: Trying to implement IImplicitResourceProvider

Hi Rick,

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

Skup
November 13, 2007

# re: Trying to implement IImplicitResourceProvider

Hi,

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

Rick Strahl
November 13, 2007

# re: Trying to implement IImplicitResourceProvider

I didn't try that however, if it isn't called that just means that your main provider's logic is accessed so this should still work regardless as long as your provider works and can find the requested resource keys.

Rick Strahl
November 13, 2007

# re: Trying to implement IImplicitResourceProvider

@Richard - yes you are correct. It looks like there's a default implementation of IImplicitResourceProvider that handles the implicit resource key look ups. I'm not sure if something has changed along these lines or whether I just did something wrong when I originally did this but I couldn't get implicit resources to work unless I implemented the IImplicitResourceProvider a while back. Since then I have tried removing the provider from the the full resource provider and things still work so it's clear that the default implementation can take care of this. <shrug>

Tim
June 10, 2008

# re: Trying to implement IImplicitResourceProvider

Hi I have the same problem GetObject not getting called for implicit resources but strangely this is only for controls within user control .ascx pages.

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.

Suleman
August 18, 2008

# re: Trying to implement IImplicitResourceProvider

I picked up quite a great stuff here and combined with other sites, I implemented a custom resource provider that's swtichable btw xml and sql.

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.

Suleman
August 19, 2008

# re: Trying to implement IImplicitResourceProvider

Hi All,

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.

Mike Godfrey
April 26, 2011

# re: Trying to implement IImplicitResourceProvider

I am looking at a legacy application which uses a Resource Provider, a lot of what I have read in this series of blog posts rings true.

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.

Rick Strahl
April 26, 2011

# re: Trying to implement IImplicitResourceProvider

@Mike - a Resource Provider loads up resources into memory. Once loaded these resources are not unloaded usually. Resx files are not required, although you can use a resource provider to create .Resx files and then use those for localization instead.

Hans Melin
August 14, 2015

# re: Trying to implement IImplicitResourceProvider

A few years later but still relevant since I see that you still implement this interface in your library, https://github.com/RickStrahl/Westwind.Globalization.

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

Rick Strahl
August 14, 2015

# re: Trying to implement IImplicitResourceProvider

@Hans - the code that implements IImplicitResourceProvider is legacy code that hasn't been updated in ages. The resource provider implements it and it still works - in the past it was required in order to get Visual Studio's resource creation and design time experience to work. I think the runtime functionality never required it. I know it still works, but not sure if it would work without it now.

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