load resources (string-table) from database

  • Thread starter Thread starter SSchoenb
  • Start date Start date
S

SSchoenb

hi,

i like to load the resources (specially the string-table)
from a database table. is this possible ?
e.g. with a custom resource-loader
i know there is such a concept in asp.net (resource provider factory)
but i have nothing
related found for winforms.

thanks,
stefan
 
Hi Stefan,

Try deriving classes from System.Resources.ResourceManager and ResourceSet. There are a few protected, virtual methods that you can
override to provide custom functionality. One method of interest would be ResourceManager.InternalGetResourceSet, which you can use
to provide an instance of your custom ResourceSet. Another would be ResourceSet.ReadResources, which you can use to read the
strings from a database.

Here's a working example that you can use. Feel free to extend it or change its behavior completely if you'd like. I added some
caching functionality as well to decrease database hits:

// Here is how you can use the DatabaseResourceManager class, which is defined afterwards:
string message = null;

// english-United States
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");

DatabaseResourceManager dbResManager = new DatabaseResourceManager();
message = string.Format("{0} {1}", dbResManager.GetString("hello"), dbResManager.GetString("world"));

Console.WriteLine(message);

// spanish-Spain
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("es-ES");

message = string.Format("{0} {1}", dbResManager.GetString("hello"), dbResManager.GetString("world"));

Console.WriteLine(message);

// french (will choose neutral language)
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("fr");

message = string.Format("{0} {1}", dbResManager.GetString("hello"), dbResManager.GetString("world"));

Console.WriteLine(message);

// invariant
System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture;

message = string.Format("{0} {1}", dbResManager.GetString("hello"), dbResManager.GetString("world"));

Console.WriteLine(message);

Prints:
hello world
hola mundo
hello world
hello world


Here's my code for the DatabaseResourceManager class:

using System;
using System.Collections.Generic;
using System.Text;
using System.Resources;
using System.Globalization;
using System.Collections;

namespace Synthesoft.Data
{
public class DatabaseResourceManager : ResourceManager
{
public DatabaseResourceManager()
{
}

protected override ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
{
if (culture == null)
culture = CultureInfo.InvariantCulture;

return new DatabaseResourceSet(culture);
}
}

public class DatabaseResourceSet : ResourceSet
{
private readonly CultureInfo culture;
private static readonly Dictionary<string, Hashtable> cachedResources = new Dictionary<string, Hashtable>();

public DatabaseResourceSet(CultureInfo culture)
{
if (culture == null)
throw new ArgumentNullException("culture");

this.culture = culture;

ReadResources();
}

protected override void ReadResources()
{
if (cachedResources.ContainsKey(culture.Name))
// retrieve cached resource set
{
Table = cachedResources[culture.Name];
return;
}

// TODO: grab strings from db based on culture.Name
switch (culture.Name)
{
case "es-ES": // spanish-Spain
// my knowledge of Spanish is very limited ;)
Table["hello"] = "hola";
Table["world"] = "mundo";
break;
case "en-US": // english-United States
default: // invariant language neutral
Table["hello"] = "hello";
Table["world"] = "world";
break;
}

cachedResources[culture.Name] = Table;
}
}
}
 
Hi Dave,

Thank you for your comprehensive answer.
Now, i know how to create a derived ResourceManager. This is Part 1 of
the Problem.
Part 2 is to find a mechanism to get the win-forms framework using this

derived ResourceManager.
When you look at the Designer generated WinForms code (when Localizable
in Form enabled) you see following Line:
System.ComponentModel.ComponentResourceManager resources = new
System.ComponentModel.ComponentResourceManager(typeof(UCSondertagDetail));

I don't know of a way to get our derived ResourceManager used ( instead
or beside this
ComponentResourceManager).

Any ideas ?

Thanks
Stefan

Dave said:
Hi Stefan,

Try deriving classes from System.Resources.ResourceManager and ResourceSet. There are a few protected, virtual methods that you can
override to provide custom functionality. One method of interest would be ResourceManager.InternalGetResourceSet, which you can use
to provide an instance of your custom ResourceSet. Another would be ResourceSet.ReadResources, which you can use to read the
strings from a database.

Here's a working example that you can use. Feel free to extend it or change its behavior completely if you'd like. I added some
caching functionality as well to decrease database hits:

// Here is how you can use the DatabaseResourceManager class, which is defined afterwards:
string message = null;

// english-United States
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");

DatabaseResourceManager dbResManager = new DatabaseResourceManager();
message = string.Format("{0} {1}", dbResManager.GetString("hello"), dbResManager.GetString("world"));

Console.WriteLine(message);

// spanish-Spain
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("es-ES");

message = string.Format("{0} {1}", dbResManager.GetString("hello"), dbResManager.GetString("world"));

Console.WriteLine(message);

// french (will choose neutral language)
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("fr");

message = string.Format("{0} {1}", dbResManager.GetString("hello"), dbResManager.GetString("world"));

Console.WriteLine(message);

// invariant
System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture;

message = string.Format("{0} {1}", dbResManager.GetString("hello"), dbResManager.GetString("world"));

Console.WriteLine(message);

Prints:
hello world
hola mundo
hello world
hello world


Here's my code for the DatabaseResourceManager class:

using System;
using System.Collections.Generic;
using System.Text;
using System.Resources;
using System.Globalization;
using System.Collections;

namespace Synthesoft.Data
{
public class DatabaseResourceManager : ResourceManager
{
public DatabaseResourceManager()
{
}

protected override ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
{
if (culture == null)
culture = CultureInfo.InvariantCulture;

return new DatabaseResourceSet(culture);
}
}

public class DatabaseResourceSet : ResourceSet
{
private readonly CultureInfo culture;
private static readonly Dictionary<string, Hashtable> cachedResources = new Dictionary<string, Hashtable>();

public DatabaseResourceSet(CultureInfo culture)
{
if (culture == null)
throw new ArgumentNullException("culture");

this.culture = culture;

ReadResources();
}

protected override void ReadResources()
{
if (cachedResources.ContainsKey(culture.Name))
// retrieve cached resource set
{
Table = cachedResources[culture.Name];
return;
}

// TODO: grab strings from db based on culture.Name
switch (culture.Name)
{
case "es-ES": // spanish-Spain
// my knowledge of Spanish is very limited ;)
Table["hello"] = "hola";
Table["world"] = "mundo";
break;
case "en-US": // english-United States
default: // invariant language neutral
Table["hello"] = "hello";
Table["world"] = "world";
break;
}

cachedResources[culture.Name] = Table;
}
}
}

--
Dave Sexton

hi,

i like to load the resources (specially the string-table)
from a database table. is this possible ?
e.g. with a custom resource-loader
i know there is such a concept in asp.net (resource provider factory)
but i have nothing
related found for winforms.

thanks,
stefan
 
Hi Stefan,

The Localizable property is used in the designer so that you can enter localized strings directly into the Form by setting the
Language property to the culture of choice. It wouldn't make sense to switch the designer's ResourceManager with your own since you
want the strings to come from a database.

Instead, set the values of the properties that you want localized by your ResourceManager in code.

private readonly DatabaseResourceManager dbResources = new DatabaseResourceManager();

public Form1()
{
// designer code
InitializeComponent();

lblHello.Text = dbResources.GetString("hello");
lblWorld.Text = dbResources.GetString("world");
}

protected override void Dispose(bool disposing)
{
if (disposing)
dbResources.ReleaseAllResources();

base.Dispose(disposing);
}

--
Dave Sexton

stefan said:
Hi Dave,

Thank you for your comprehensive answer.
Now, i know how to create a derived ResourceManager. This is Part 1 of
the Problem.
Part 2 is to find a mechanism to get the win-forms framework using this

derived ResourceManager.
When you look at the Designer generated WinForms code (when Localizable
in Form enabled) you see following Line:
System.ComponentModel.ComponentResourceManager resources = new
System.ComponentModel.ComponentResourceManager(typeof(UCSondertagDetail));

I don't know of a way to get our derived ResourceManager used ( instead
or beside this
ComponentResourceManager).

Any ideas ?

Thanks
Stefan

Dave said:
Hi Stefan,

Try deriving classes from System.Resources.ResourceManager and ResourceSet. There are a few protected, virtual methods that you
can
override to provide custom functionality. One method of interest would be ResourceManager.InternalGetResourceSet, which you can
use
to provide an instance of your custom ResourceSet. Another would be ResourceSet.ReadResources, which you can use to read the
strings from a database.

Here's a working example that you can use. Feel free to extend it or change its behavior completely if you'd like. I added some
caching functionality as well to decrease database hits:

// Here is how you can use the DatabaseResourceManager class, which is defined afterwards:
string message = null;

// english-United States
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");

DatabaseResourceManager dbResManager = new DatabaseResourceManager();
message = string.Format("{0} {1}", dbResManager.GetString("hello"), dbResManager.GetString("world"));

Console.WriteLine(message);

// spanish-Spain
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("es-ES");

message = string.Format("{0} {1}", dbResManager.GetString("hello"), dbResManager.GetString("world"));

Console.WriteLine(message);

// french (will choose neutral language)
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("fr");

message = string.Format("{0} {1}", dbResManager.GetString("hello"), dbResManager.GetString("world"));

Console.WriteLine(message);

// invariant
System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture;

message = string.Format("{0} {1}", dbResManager.GetString("hello"), dbResManager.GetString("world"));

Console.WriteLine(message);

Prints:
hello world
hola mundo
hello world
hello world


Here's my code for the DatabaseResourceManager class:

using System;
using System.Collections.Generic;
using System.Text;
using System.Resources;
using System.Globalization;
using System.Collections;

namespace Synthesoft.Data
{
public class DatabaseResourceManager : ResourceManager
{
public DatabaseResourceManager()
{
}

protected override ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
{
if (culture == null)
culture = CultureInfo.InvariantCulture;

return new DatabaseResourceSet(culture);
}
}

public class DatabaseResourceSet : ResourceSet
{
private readonly CultureInfo culture;
private static readonly Dictionary<string, Hashtable> cachedResources = new Dictionary<string, Hashtable>();

public DatabaseResourceSet(CultureInfo culture)
{
if (culture == null)
throw new ArgumentNullException("culture");

this.culture = culture;

ReadResources();
}

protected override void ReadResources()
{
if (cachedResources.ContainsKey(culture.Name))
// retrieve cached resource set
{
Table = cachedResources[culture.Name];
return;
}

// TODO: grab strings from db based on culture.Name
switch (culture.Name)
{
case "es-ES": // spanish-Spain
// my knowledge of Spanish is very limited ;)
Table["hello"] = "hola";
Table["world"] = "mundo";
break;
case "en-US": // english-United States
default: // invariant language neutral
Table["hello"] = "hello";
Table["world"] = "world";
break;
}

cachedResources[culture.Name] = Table;
}
}
}

--
Dave Sexton

hi,

i like to load the resources (specially the string-table)
from a database table. is this possible ?
e.g. with a custom resource-loader
i know there is such a concept in asp.net (resource provider factory)
but i have nothing
related found for winforms.

thanks,
stefan
 
Hi Dave,

I'm aware of this possibility.
But this doesn't make sense to me. If i use the resource manager
directly i',m also
able to use the string-source ( e.g. database table) directly. (ok
there is some
chance to replace it in the future but if this isn't the case it
doesn't matter).

i (simply) like to have the string table (for a component-resource)
stored into the database ( including designer-support).
when the developer changes the localizable property and enter some
strings, these strings should be added to the database instead of the
*.resx file.

It would be easy to get this done if Microsoft had defined an Interface
e.g. IResourceProvider. (see IResouceProviderFactory for Asp.Net).
I think that it would be easy to integrate such things for both
Technologies.
seems that at microsoft one Team ( Asp.Net Architects) doesn't know
what the other Team ( WinForms Architects) is developing.

But i have seen that there are a lot of interfaces like
IResourceService, maybe
i can get this Task done via a work-around ?

stefan
 
Hi Stefan,
I'm aware of this possibility.
But this doesn't make sense to me. If i use the resource manager
directly i',m also
able to use the string-source ( e.g. database table) directly. (ok
there is some
chance to replace it in the future but if this isn't the case it
doesn't matter).

ResourceManager handles the management of data based on the current UI culture. That is the benefit of using the ResourceManager.
Sure, you can add this functionality directly to your data layer without the help of the ResourceManager but the code would end up
doing exactly what the ResourceManager does for you now.
i (simply) like to have the string table (for a component-resource)
stored into the database ( including designer-support).
when the developer changes the localizable property and enter some
strings, these strings should be added to the database instead of the
*.resx file. [...]
But i have seen that there are a lot of interfaces like
IResourceService, maybe
i can get this Task done via a work-around ?

IResourceService on MSDN:
http://msdn.microsoft.com/library/d...nentmodeldesigniresourceserviceclasstopic.asp

I looked up IResourceService on MSDN, which provides an example. As I expected, it requires the use of a custom designer but I
still don't think it can accomplish what you want.

1. I don't think it makes use of a Form's Localizable and Language properties at design-time but instead uses designer verbs
2. I'm not sure if it's even possible to customize the default functionality of the Localizable and Language properties anyway.
3. I'm not sure if this solution will work across an entire Form since you can't apply your custom designer to every control on the
Form, if that's what you'd have to do.
4. I don't think it's worth the effort. As you can see from my previous post it's really easy to just use the ResourceManager from
code.

Another option however, if you aren't willing to just code against the ResourceManager, would be to create a custom
Component-derived class that exposes data-binding functionality and aggregates the custom ResourceManager class. Then, you can drop
the Component on your Form and bind controls' properties to the resource strings of your choice. Again, not sure if it's worth the
effort.
 
Back
Top