Publié mercredi 4 juin 2008 19:58 par Luke77

Configuration en base de données - Partie 2

Dans le post précédent, on avait posé les bases pour pouvoir accéder à notre configuration (à savoir une section dans le web.config qui nous indique quelle ConnectionString utiliser ainsi que la table et sa proc stock pour lire les données).

Désormais il ne nous reste "plus qu'à" écrire quelques classes nous permettant d'y accéder facilement sans trop se poser de questions.

La première est la classe d'accès aux données qui va se contenter d'appeler la procédure stockée et nous renvoyer la valeur de la configuration trouvée.

   1: internal class ConfigurationAccess
   2: {
   3:     #region Private Members
   4:  
   5:     private string _connectionString;
   6:  
   7:     #endregion
   8:  
   9:     #region Constructors
  10:  
  11:     public ConfigurationAccess()
  12:     {
  13:     }
  14:  
  15:     #endregion
  16:  
  17:     #region Public Methods
  18:  
  19:     /// <summary>
  20:     /// Initializes the specified connection string.
  21:     /// </summary>
  22:     /// <param name="connectionString">The connection string.</param>
  23:     public void Initialize(string connectionString)
  24:     {
  25:         _connectionString = connectionString;
  26:     }
  27:  
  28:     /// <summary>
  29:     /// Gets the configuration attribute for a specific application's name and key.
  30:     /// </summary>
  31:     /// <param name="applicationName">Name of the application.</param>
  32:     /// <param name="key">The key.</param>
  33:     /// <returns>The value or <code>null</code> if there's no configuration value</returns>
  34:     public string GetConfigurationAttribute(string applicationName, string key)
  35:     {
  36:         using (SqlConnection connection = ConnectionHelper.CreateOpenedConnection(_connectionString))
  37:         {
  38:             using (SqlCommand command = CommandHelper.CreateCommand("Conf.P_GetConfigurationAttribute", connection))
  39:             {
  40:                 CommandHelper.AddParameter(command, "ApplicationName", SqlDbType.NVarChar, applicationName);
  41:                 CommandHelper.AddParameter(command, "Key", SqlDbType.NVarChar, key);
  42:  
  43:                 return command.ExecuteScalar() as string;
  44:             }
  45:         }
  46:     }
  47:  
  48:     #endregion
  49: }

Rien de bien innovant la dedans ; une méthode d'initialisation afin de pouvoir spécifier sur quelle base de données faire les requêtes, et une seconde qui requête la base de données. Les paramètres fournis lors de l'appel à la procédure stockée sont comme prévus le nom de l'application qui exécute la procédure stockée et la clé de configuration pour laquelle on souhaite obtenir la valeur.

On peut apercevoir l'utilisation de ConnectionHelper.CreateOpenedConnection et CommandHelper.CreateCommand qui sont deux pauvres méthodes de ma création qui ouvrent une connexion et la seconde qui instancie une SqlCommand de type StoredProcedure.

 

Au dessus de cette classe toute bête, nous allons y rajouter une classe ConfigurationController qui va s'occuper principalement de gérer la montée en cache des clés de configuration. En voici une première description :

   1: public class ConfigurationController
   2: {
   3:     #region Private Members
   4:  
   5:     private Cache<string, string> _cache;
   6:     private ConfigurationAccess _confAccess;
   7:  
   8:     private TimeSpan _duration;
   9:  
  10:     private bool _useCache;
  11:  
  12:     #endregion
  13:  
  14:     #region Constructors
  15:  
  16:     public ConfigurationController()
  17:     {
  18:         _confAccess = new JALUM.Utility.Configuration.Database.ConfigurationAccess();
  19:         _confAccess.Initialize(StartupConfiguration.ConnectionString);
  20:         _confAccess = access;
  21:  
  22:         _cache = CacheManager.GetCache<string, string>("Configuration");
  23:         _cache.DefaultSlidingExpiration = false;
  24:     }
  25:  
  26:     #endregion
  27: }

Dans le constructeur, on instancie notre classe d'accès aux données et on l'initialise avec la ConnectionString indiquée dans le web.config (Attention, ici il s'agit de la classe StartupConfiguration qui est une classe statique permettant d'accéder plus simplement aux informations renvoyées par StartupConfigurationSection).

On peut aussi y voir le cache qui contiendra le couple clé/valeur ainsi qu'un TimeSpan qui contient la durée de cache et un bool qui déterminera si on doit utiliser le cache ou non.

Mais comment le contrôleur de configuration sait si il doit monter les données en cache, et si oui pour combien de temps ? En allant les lire en configuration bien sûr. Pour cela on va exposer deux propriétés UseCache et CacheDuration. Sans plus attendre en voici le code :

   1: public bool UseCache
   2: {
   3:     get
   4:     {
   5:         string key = "Configuration.UseCache";
   6:         string returnValue;
   7:         if (_cache.ContainsKey(key))
   8:         {
   9:             return _useCache;
  10:         }
  11:  
  12:         returnValue = _confAccess.GetConfigurationAttribute(StartupConfiguration.ApplicationName, key);
  13:         if (string.IsNullOrEmpty(returnValue))
  14:         {
  15:             returnValue = _confAccess.GetConfigurationAttribute(null, key);
  16:         }
  17:  
  18:         returnValue = returnValue ?? "true";
  19:         _useCache = bool.Parse(returnValue);
  20:         _cache.Add(key, returnValue, CacheDuration);
  21:  
  22:         return _useCache;
  23:     }
  24: }
  25:  
  26: public TimeSpan CacheDuration
  27: {
  28:     get
  29:     {
  30:         string key = "Configuration.CacheDuration";
  31:         string returnValue;
  32:         if (_cache.ContainsKey(key))
  33:         {
  34:             return _duration;
  35:         }
  36:  
  37:         returnValue = _confAccess.GetConfigurationAttribute(StartupConfiguration.ApplicationName, key);
  38:         if (string.IsNullOrEmpty(returnValue))
  39:         {
  40:             returnValue = _confAccess.GetConfigurationAttribute(null, key);
  41:         }
  42:  
  43:         returnValue = returnValue ?? "00:10:00";
  44:  
  45:         _duration = TimeSpan.Parse(returnValue);
  46:         _cache.Add(key, returnValue, _duration);
  47:  
  48:         return _duration;
  49:     }
  50: }

En lisant le code on peut rapidement se poser deux questions :

  • Pourquoi deux appels par propriété à la classe d'accès aux données ?
  • Pourquoi avoir des variables de classe (_useCache et _duration) alors que ces deux données sont forcement dans le cache ?

A la première question, je te répondrai simplement parce que on effectue d'abord une première requête afin de consulter la clé pour l'application en cours, et une seconde pour la configuration commune à toutes les applications si la première requête n'a pas renvoyé de résultats.

Et à la seconde, il s'agit purement et simplement d'augmenter les performances. Si ces deux variables n'existaient pas, à chaque appel à ces propriétés, le code serait obliger de faire un Parse de la string pour obtenir l'objet souhaité (un bool ou un TimeSpan). Alors qu'en vérifiant seulement si la clé et présente et en renvoyant le dernier résultat connu, on évite ce Parse fastidieux et couteux.

Il ne reste plus que l'obtention d'une valeur de configuration générale à définir et cette classe sera terminée. Rien de bien particulier à dire si l'on a déjà lu le code ci-dessus :

   1: public string GetConfigurationValue(string key)
   2: {
   3:     bool _useCache = UseCache;
   4:  
   5:     string returnValue;
   6:     if (_useCache && _cache.TryGetValue(key, out returnValue))
   7:     {
   8:         return returnValue;
   9:     }
  10:  
  11:     returnValue = _confAccess.GetConfigurationAttribute(StartupConfiguration.ApplicationName, key);
  12:     if (string.IsNullOrEmpty(returnValue))
  13:     {
  14:         returnValue = _confAccess.GetConfigurationAttribute(null, key);
  15:     }
  16:  
  17:     if (_useCache)
  18:     {
  19:         _cache.Add(key, returnValue, CacheDuration);
  20:     }
  21:  
  22:     return returnValue;
  23: }

 

Tout cela c'est bien beau, mais ca permet pas d'organiser facilement sa configuration, ni de la hiérarchiser. Cela je l'exposerai dans un prochain post.

Sur ce, je vais célébrer mon anniversaire qui tombe pile poil aujourd'hui, alors soyez indulgent sur les commentaires si il y en a :-) .

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

# re: Configuration en base de données - Partie 2 @ jeudi 5 juin 2008 08:58

Bonne anniversaire !!

Ashi4


Les 10 derniers blogs postés

- Hook sous Vista : il faut montrer patte blanche par Blog d'Olivier Huet le il y a 43 minutes

- EF et WPF : Réponse à Thomas par Matthieu MEZIL le il y a 3 heures et 9 minutes

- EF et WPF par Matthieu MEZIL le il y a 18 heures et 23 minutes

- C# : Vérifications / Performances par Pierrick's Blog le il y a 21 heures et 58 minutes

- Du nouveau sur le clubvsts par Noham Choulant le il y a 23 heures et 38 minutes

- StyleCop SDK disponible par Michel Perfetti [Miiitch] le 08-29-2008, 13:59

- Data Structures and Algorithms : un livre gratuit par Elise's blog le 08-29-2008, 11:39

- [ASP.NET] - Ajax vNext Preview 2 par Aurelien's Blog - When ClientSide meets .Net le 08-29-2008, 10:35

- TPH IS Not Null sur la relation par Matthieu MEZIL le 08-29-2008, 08:15

- Mise à jours du code Source du .NET Framework 3.5 SP1 disponible sur le Reference Source Code Center par RedoBlog - The .NET Gentleman !!! le 08-29-2008, 01:50