A Custom Resource Reader


With the resource readers that are part of .NET Framework 2.0, you can read resources from resource files and satellite assemblies. If you want to put the resources into a different store (such as a database), you can use a custom resource reader to read these resources.

To use a custom resource reader, you also need to create a custom resource set and a custom resource manager. However, doing this is not a hard task, because you can derive the custom classes from existing classes.

For the sample application, you have to create a simple database with just one table for storing messages that has one column for every supported language. The following table lists the columns and their corresponding values.

Open table as spreadsheet

Key

Default

de

es

fr

it

Welcome

Welcome

Willkommen

Recepción

Bienvenue

Benvenuto

GoodMorning

Good Morning

Guten Morgen

Buonas díaz

Bonjour

Buona Mattina

GoodEvening

Good Evening

Guten Abend

Buonas noches

Bonsoir

Buona sera

ThankYou

Thank you

Danke

Gracias

Merci

Grazie

Goodbye

Goodbye

Auf Wiedersehen

Adiós

Au revoir

Arrivederci

For the custom resource reader, you create a component library with three classes. The classes are DatabaseResourceReader, DatabaseResourceSet, and DatabaseResourceManager.

Creating a DatabaseResourceReader

With the class DatabaseResourceReader, you define two fields: the connection string that is needed to access the database and the language that should be returned by the reader. These fields are filled inside the constructor of this class. The field language is set to the name of the culture that is passed with the CultureInfo object to the constructor:

  public class DatabaseResourceReader : IResourceReader {    private string connectionString;    private string language;    public DatabaseResourceReader(string connectionString, CultureInfo culture)    {       this.connectionString = connectionString;       this.language = culture.Name;    } 

A resource reader has to implement the interface IResourceReader. This interface defines the methods Close() and GetEnumerator() to return an IDictionaryEnumerator that returns keys and values for the resources. In the implementation of GetEnumerator(), create a Hashtable where all keys and values for a specific language are stored. Next, you can use the SqlConnection class in the namespace System.Data.SqlClient to access the database in SQL Server. Connection.CreateCommand() creates a SqlCommand() object that you use to specify the SQL SELECT statement to access the data in the database. If the language is set to de, the SELECT statement is SELECT [key], [de] FROM Messages. Then you use a SqlDataReader object to read all values from the database, and put it into a Hashtable. Finally, the enumerator of the Hashtable is returned.

Tip 

For more information about accessing data with ADO.NET, see Chapter 25, “Data Access.”

  public System.Collections.IDictionaryEnumerator GetEnumerator() {    Hashtable dict = new Hashtable();    SqlConnection connection = new SqlConnection(connectionString);    SqlCommand command = connection.CreateCommand();    if (language == "")       language = "Default";           command.CommandText = "SELECT [key], [" + language + "] " +                          "FROM Messages";    try    {       connection.Open();       SqlDataReader reader = command.ExecuteReader();       while (reader.Read())       {          if (reader.GetValue(1) != System.DBNull.Value)          {             dict.Add(reader.GetString(0).Trim(), reader.GetString(1));          }       }       reader.Close();    }    catch (SqlException ex)    {       if (ex.Number != 207)  // ignore missing columns in the database          throw;              // rethrow all other exceptions    }    finally    {       connection.Close();    }    return dict.GetEnumerator(); } public void Close() { } 

Because the interface IResourceReader is derived from IEnumerable and IDisposable, the methods GetEnumerator(), which returns an IEnumerator interface, and Dispose() must be implemented, too:

     IEnumerator IEnumerable.GetEnumerator()    {       return this.GetEnumerator();    }    void IDisposable.Dispose()    {    } } 

Creating a DatabaseResourceSet

The class DatabaseResourceSet can use nearly all implementations of the base class ResourceSet. You just need a different constructor that initializes the base class with our own resource reader, DatabaseResourceReader. The constructor of ResourceSet allows passing an object by implementing IResourceReader; this requirement is fulfilled by DatabaseResourceReader:

  public class DatabaseResourceSet : ResourceSet {    internal DatabaseResourceSet(string connectionString, CultureInfo culture)       : base(new DatabaseResourceReader(connectionString, culture))    {    }    public override Type GetDefaultReader()    {       return typeof(DatabaseResourceReader);    } } 

Creating a DatabaseResourceManager

The third class you have to create is the custom resource manager. DatabaseResourceManager is derived from the class ResourceManager, and you only have to implement a new constructor and override the method InternalGetResourceSet().

In the constructor, create a new Hashtable to store all queried resource sets and set it into the field ResourceSets defined by the base class:

  public class DatabaseResourceManager : ResourceManager {    private string connectionString;    public DatabaseResourceManager(string connectionString)    {       this.connectionString = connectionString;       ResourceSets = new Hashtable();    } 

The methods of the ResourceManager class that you can use to access resources (such as GetString() and GetObject()) invoke the method InternalGetResourceSet() to access a resource set where the appropriate values can be returned.

In the implementation of InternalGetResourceSet(), check first if the resource set for the culture queried for a resource is already in the hash table; if it already exists, return it to the caller. If the resource set is not available, create a new DatabaseResourceSet object with the queried culture, add it to the hash table, and return it to the caller:

     protected override ResourceSet InternalGetResourceSet(        CultureInfo culture, bool createIfNotExists, bool tryParents)    {       DatabaseResourceSet rs = null;       if (ResourceSets.Contains(culture.Name))       {          rs = ResourceSets[culture.Name] as DatabaseResourceSet;       }       else       {          rs = new DatabaseResourceSet(connectionString, culture);          ResourceSets.Add(culture.Name, rs);       }       return rs;    } } 

Client Application for DatabaseResourceReader

How the class ResourceManager is used from the client application here does not differ a lot from the previous use of the ResourceManager class. The only difference is that the custom class DatabaseResourceManager is used instead of the class ResourceManager. The following code snippet demonstrates how you can use your own resource manager.

A new DatabaseResourceManager object is created by passing the database connection string to the constructor. Then you can invoke the GetString() method that is implemented in the base class as you did earlier, passing the key and an optional object of type CultureInfo to specify a culture. In turn, you get a resource value from the database, because this resource manager is using the classes DatabaseResourceSet and DatabaseResourceReader.

  DatabaseResourceManager rm = new DatabaseResourceManager(       "server=(local);database=LocalizationDemo;trusted_connection=true"); string spanishWelcome = rm.GetString("Welcome",                                       new CultureInfo("es-ES")); string italianThankyou = rm.GetString("ThankYou",                                       new CultureInfo("it")); string threadDefaultGoodMorning = rm.GetString("GoodMorning"); 




Professional C# 2005 with .NET 3.0
Professional C# 2005 with .NET 3.0
ISBN: 470124725
EAN: N/A
Year: 2007
Pages: 427

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net