Guarda! Senza mani! - Quinta parte
a cura di Sabrina Cosolo e Diego Cattaruzza (requisiti: conoscenza intermedia di Sql Server e di .Net)

Ancora un po' di pazienza
Per sviluppare la classe di gestione delle impostazioni nell'applicazione, UsingSqlServerConfig, aggiungiamo alla classe IoHelper altri quattro metodi. Infatti, per gestire i file di configurazione, ci serve un metodo che ci restituisca la cartella di installazione dell'applicazione, un metodo che ci restituisca il nome del file di setting dell'applicazione ed un metodo che ci restituisca il nome del file di setting dell'utente. Per cucinare tutto questo, bisogna utilizzare un po' di Environment, un po' di Reflection, condire con qualche metodo delle classi di IO e l'helper è pronto.

using System.IO;
Imports System.IO

Prima aggiungiamo una direttiva utile.

    public static string GetAppDir()
    {
      return AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
    }

    public static string GetAppDir(string pPath)
    {
      if (pPath == null) pPath = String.Empty;
      return Path.Combine(GetAppDir(), pPath);
    }
  Public Shared Function GetAppDir() As String
    Return My.Application.Info.DirectoryPath
  End Function

  Public Shared Function GetAppDir(ByVal nomeFile As String) As String
    If nomeFile Is Nothing Then nomeFile = String.Empty
    Return Path.Combine(GetAppDir(), nomeFile)
  End Function

Ecco i primi due metodi aggiunti a IoHelper, che ci restituiscono il nome della cartella di installazione dell'applicazione e il nome di un file specifico mappato sulla cartella dell'applicazione. Vi chiederete perché scomodare una cosa come l'AppDomain per generare la cartella corrente. Vi rispondiamo in anticipo: perché, se l'applicazione, in qualsivoglia modo, contiene degli OpenFileDialog o dei SaveFileDialog, o comunque qualsiasi funzione che possa cambiare la cartella corrente dell'applicazione, il metodo che ci fornisce il path corrente non ci dà quel che ci serve.

Per i prossimi due metodi, abbiamo bisogno di crearci una eccezione personalizzata, ed anche una risorsa (un messaggio 'tipico'). Quindi, tasto desto su TAndT.Base, aggiungiamo una nuova classe di nome InvalidParameterException (simile alle altre: ci serve un nome esplicativo):

namespace TAndT.Base
{
  public class InvalidParameterException : System.ApplicationException
  {
    public InvalidParameterException(string pMessage)
      : base(pMessage)
    {

    }
  }
}
Public Class InvalidParameterException
  Inherits System.ApplicationException

  Public Sub New(ByVal message As String)
    MyBase.New(message)
  End Sub

End Clas

Se in Solution Explorer non vediamo tutti i file, clicchiamo sull'icona apposita e poi apriamo il Resources.resx - se non c'è aggiungiamolo. clic destro su TAndT.Base.Properties per C# - della TAndT.Base, aggiungendovi una risorsa:

  warIOHNoProductName Il nome prodotto indicato è vuoto
    public static string CreateAppConfigFileName(string pApplication)
    {
      if (pApplication == null || pApplication.Trim().Length == 0)
      {
        throw new InvalidParameterException(Properties.Resources.warIOHNoProductName);
      }
      string filename = string.Format("{0}App.ttcfg", pApplication);
      return (GetAppDir(filename));
    }
  Public Shared Function CreateAppConfigFileName(ByVal application As String) As String
    If String.IsNullOrEmpty(application) Then
      Throw New InvalidParameterException(My.Resources.warIOHNoProductName)
    End If
    Dim fileName As String = String.Format("{0}App.ttcfg", application)
    Return GetAppDir(fileName)
  End Function

Il terzo metodo ci crea il nome del file di configurazione, passato il nome dell'applicazione, lanciando l'eccezione col messaggio appena creati nel caso il parametro passato sia vuoto.
Per il quarto metodo abbiamo bisogno di essere sicuri dell'esistenza di una cartella, quindi ne creiamo un quinto:

    public static void CheckCreateDir(string pDir)
    {
      try
      {
        if (!System.IO.Directory.Exists(pDir))
        {
          System.IO.Directory.CreateDirectory(pDir);
        }
      }
      catch (Exception ex)
      {
        throw new ApplicationException(" " + mClassName + "."
          + System.Reflection.MethodBase.GetCurrentMethod().Name, ex);
      }
    }
  Public Shared Sub CheckCreateDir(ByVal directoryPath As String)
    Try
      If Not Directory.Exists(directoryPath) Then
        Directory.CreateDirectory(directoryPath)
      End If

    Catch ex As Exception
      Throw New ApplicationException(" " + mClassName + "." _
                                         + System.Reflection.MethodBase.GetCurrentMethod().Name, ex)
    End Try

  End Sub

Esso controlla se una cartella esiste e la crea se non esiste.

    public static string CreateUsrConfigFileName(string pApplication)
    {
      if (pApplication == null || pApplication.Trim().Length == 0)
      {
        throw new InvalidParameterException(Properties.Resources.warIOHNoProductName);
      }
      string baseDir = Path.Combine(Environment.GetEnvironmentVariable("APPDATA"), "TAndT");
      CheckCreateDir(baseDir);
      baseDir = Path.Combine(baseDir, pApplication);
      CheckCreateDir(baseDir);
      string filename = string.Format("{0}Usr.ttcfg", pApplication);
      return (Path.Combine(baseDir, filename));
    }
  Public Shared Function CreateUsrConfigFileName(ByVal application As String) As String
    If String.IsNullOrEmpty(application) Then
      Throw New InvalidParameterException(My.Resources.warIOHNoProductName)
    End If
    Dim baseDir As String = Path.Combine(Environment.GetEnvironmentVariable("APPDATA"), "TAndT")
    CheckCreateDir(baseDir)
    baseDir = Path.Combine(baseDir, application)
    CheckCreateDir(baseDir)
    Dim fileName As String = String.Format("{0}Usr.ttcfg", application)
    Return Path.Combine(baseDir, fileName)
  End Functio

Il quarto metodo, forse un po' più interessante, genera il nome per il file delle impostazioni per l'utente. Facciamo notare l'uso di GetEnvironmentVariable, che ci permette di richiedere al sistema qual è la cartella Application Data (Dati Applicazioni) dell'utente corrente. Questa cartella è indicata nelle best practices come la cartella sotto cui tutte le applicazioni dovrebbero salvare i dati di configurazione utente. Per essere più pignoli, ci deve essere una sottocartella con il nome dell'azienda, con all'interno una con il nome dell'applicazione, nella quale ultima memorizzare il file. Ecco perché ricorriamo più volte al metodo CheckCreateDir.

Se siete curiosi e volete vedere quante e quali sono le variabili di ambiente della vostra macchina, aprite una finestra console (Start, run, cmd, su Windows XP), scrivete SET e premete <invio>: la lista che compare può essere interrogata con il metodo GetEnvironmentVariable, anche se alcune variabili quali Username e DomainName sono già mappate nella classe Environment.

Nel nostro caso, il risultato della chiamata al metodo che richiede il nome del file dei setting di applicazione è qualcosa come:
C:\Documents and Settings\Username\Dati applicazioni\TAndT\UsingSqlServer2005\UsingSqlServer2005Usr.ttcfg.

Andiamo a fare un test sulla nostra FrmMain e aggiungiamo al menu Setting Manager Tests un menu dal titolo Test Set App User Config Path, per il quale Visual Studio ci genererà un nuovo ToolStripMenuItem al cui evento click associeremo il seguente codice:

    private void testSetAppUserConfigPathToolStripMenuItem_Click(object sender, EventArgs e)
    {
      try
      {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine("Test Valori files di configurazione ");
        sb.AppendLine(IoHelper.CreateUsrConfigFileName("UsingSqlServer2005"));
        sb.AppendLine(IoHelper.CreateAppConfigFileName("UsingSqlServer2005"));
        Warnings.Info(sb.ToString());
      }
      catch (Exception ex)
      {
        Warnings.Errore(mClassName,
          System.Reflection.MethodBase.GetCurrentMethod(), ex);
      }
    }
  Private Sub TestSetAppUserConfigPathToolStripMenuItem_Click(ByVal sender As System.Object, _
                                             ByVal e As System.EventArgs) _
                                             Handles TestSetAppUserConfigPathToolStripMenuItem.Click
    Try
      Dim sb As New StringBuilder
      sb.AppendLine("Test Valori files di configurazione")
      sb.AppendLine(IoHelper.CreateUsrConfigFileName("UsingSqlServer2005"))
      sb.AppendLine(IoHelper.CreateAppConfigFileName("UsingSqlServer2005"))
      Warnings.Info(sb.ToString)

    Catch ex As Exception
      Warnings.Errore(mClassName, System.Reflection.MethodBase.GetCurrentMethod(), ex)
    End Try
  End Sub

Il risultato di questo fantastico test sulla macchina di Sabrina è il seguente:

Test generatore nomi file config

Come vedete, abbiamo usato una estensione non standard (ttcfg), per fare in modo che questi file siano proprio nostri, anche se sono meri file XML.
Potete anche provare a togliere UsingSqlServer2005 per vedere l'errore.

La classe UsingSqlServerConfig
Questa classe sarà una classe statica. "Ancora? - direte voi - ma com'è possibile che facciamo solo classi statiche?"
Effettivamente, nell'ossatura di una applicazione ne servono varie, ma non dubitate che avrete molti e numerosi oggetti con cui lavorare molto presto.

Questa classe conterrà innanzitutto un TAndTSettingsManager per la gestione dei file, poi due variabili readonly per i nomi dei suddetti file, una costante con il nome di ciascuno dei setting che per ora utilizzeremo, un metodo per il caricamento dei setting con la generazione automatica di quelli che non fossero presenti nella lista su disco, una serie di proprietà che mapperanno i setting all'esterno della classe, in maniera che siano reperibili in modo per così dire "tipizzato", un metodo per il salvataggio dei setting... e per ora ci fermiamo qui (se del caso, aggiungeremo altro).

Posizioniamoci su UsingSqlServer, facciamo Add>Class> e generiamo una classe che chiamiamo UsingSqlServerConfig.

using System;
using System.Collections.Generic;
using System.Text;
using TAndT.Base;
using TAndT.Base.Collections;

namespace TAndT.UsingSqlServer
{
  public static class UsingSqlServerConfig
  {
    private static readonly string mClassName = System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name;
  }
}
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports TAndT.Base
Imports TAndT.Base.Collections

Public Class UsingSqlServerConfig

  Private Shared ReadOnly mClassName As String = _
                          System.Reflection.MethodBase.GetCurrentMethod.ReflectedType.Name

End Class

Aggiungiamo le direttive giuste e la solita mClassName. Curiamoci di indicare (o verificare) il namespace: chi scrive in VB controlli la correttezza del proprio Namespace e tenga presente il fatto che la classe è statica, che cioè tutti i suoi membri dovranno essere shared.
Aggiungiamo una regione:

    #region Constants

    public const string APP_NAME = "UsingSQLServer";
    public const string STT_APPSqlDatabase = "SqlDatabase";
    public const string STT_APPSqlServer = "SqlServer";
    private const string DEFAULT_MyDatabase = "myDatabase";
    private const string DEFAULT_MyPassword = "myPassword";
    private const string DEFAULT_MyServer = "myServer";
    private const string DEFAULT_MyUser = "myUser";
    private const string DEFAULT_True = "true";
    private const string SYSDB_Master = "Master";
    private const string SYSDB_Msdb = "Msdb";
    public const string STT_USRPassword = "Password";
    public const string STT_USRTrusted = "Trusted";
    public const string STT_USRUserId = "UserID";

    #endregion
#Region "Costanti"

  Public Const APP_NAME As String = "UsingSQLServer"
  Public Const STT_APPSqlDatabase As String = "SqlDatabase"
  Public Const STT_APPSqlServer As String = "SqlServer"
  Private Const DEFAULT_MyDatabase As String = "myDatabase"
  Private Const DEFAULT_MyPassword As String = "myPassword"
  Private Const DEFAULT_MyServer As String = "myServer"
  Private Const DEFAULT_MyUser As String = "myUser"
  Private Const DEFAULT_True As String = "true"
  Private Const SYSDB_Master As String = "Master"
  Private Const SYSDB_Msdb As String = "Msdb"
  Public Const STT_USRPassword As String = "Password"
  Public Const STT_USRTrusted As String = "Trusted"
  Public Const STT_USRUserId As String = "UserID"

#End Regio

Queste costanti rappresentano:

    private static readonly string mAppFileName = IoHelper.CreateAppConfigFileName(APP_NAME);
    private static readonly string mUserFileName = IoHelper.CreateUsrConfigFileName(APP_NAME);

    private static TAndTSettings mAppSettings;
    private static TAndTSettings mUserSettings;
  Private Shared ReadOnly mAppFileName As String = IoHelper.CreateAppConfigFileName(APP_NAME)
  Private Shared ReadOnly mUserFileName As String = IoHelper.CreateUsrConfigFileName(APP_NAME)

  Private Shared mAppSettings As TAndTSettings
  Private Shared mUserSettings As TAndTSettings

Le variabili a livello di classe sono quattro, i nomi dei due file dati e le collezioni dei setting, i nomi dei file li componiamo immediatamente in quanto non modificabili dall'utente, le collezioni sono campi relativi a proprietà (che seguono in ordine sparso, con qualche preparazione laddove occorre).

    public static TAndTSettings AppSettings
    {
      get
      {
        return mAppSettings;
      }
      set
      {
        mAppSettings = value;
      }
    }

    public static TAndTSettings UserSettings
    {
      get
      {
        return mUserSettings;
      }
      set
      {
        mUserSettings = value;
      }
    }

    public static bool Trusted
    {
      get
      {
        bool ret = false;
        if (!bool.TryParse(UserSettings[STT_USRTrusted].Value, out ret))
        {
          ret = false;
        }
        return ret;
      }
      set
      {
        bool ret = false;
        if (bool.TryParse(value.ToString(), out ret))
        {
          UserSettings[STT_USRTrusted].Value = ret.ToString();
        }
        else
        {
          UserSettings[STT_USRTrusted].Value = false.ToString();
        }
      }
    }

    public static string SqlServer
    {
      get
      {
        return AppSettings[STT_APPSqlServer].Value;
      }
      set
      {
        AppSettings[STT_APPSqlServer].Value = value;
      }
    }

    public static string SqlDatabase
    {
      get
      {
        return AppSettings[STT_APPSqlDatabase].Value;
      }
      set
      {
        AppSettings[STT_APPSqlDatabase].Value = value;
      }
    }

    public static string UserID
    {
      get
      {
        return UserSettings[STT_USRUserId].Value;
      }
      set
      {
        UserSettings[STT_USRUserId].Value = value;
      }
    }

    public static string Password
    {
      get
      {
        return UserSettings[STT_USRPassword].Value;
      }
      set
      {
        UserSettings[STT_USRPassword].Value = value;
      }
    }
  Public Shared Property AppSettings() As TAndTSettings
    Get
      Return mAppSettings
    End Get
    Set(ByVal value As TAndTSettings)
      mAppSettings = value
    End Set
  End Property

  Public Shared Property UserSettings() As TAndTSettings
    Get
      Return mUserSettings
    End Get
    Set(ByVal value As TAndTSettings)
      mUserSettings = value
    End Set
  End Property

  Public Shared Property Trusted() As Boolean
    Get
      Dim ret As Boolean = False
      If Not Boolean.TryParse(UserSettings(STT_USRTrusted).Value, ret) Then
        ret = False
      End If
      Return ret
    End Get
    Set(ByVal value As Boolean)
      Dim ret As Boolean = False
      If Boolean.TryParse(value.ToString, ret) Then
        UserSettings(STT_USRTrusted).Value = ret.ToString
      Else
        UserSettings(STT_USRTrusted).Value = False.ToString
      End If
    End Set
  End Property

  Public Shared Property SqlServer() As String
    Get
      Return AppSettings(STT_APPSqlServer).Value
    End Get
    Set(ByVal value As String)
      AppSettings(STT_APPSqlServer).Value = value
    End Set
  End Property

  Public Shared Property SqlDatabase() As String
    Get
      Return AppSettings(STT_APPSqlDatabase).Value
    End Get
    Set(ByVal value As String)
      AppSettings(STT_APPSqlDatabase).Value = value
    End Set
  End Property

  Public Shared Property UserID() As String
    Get
      Return UserSettings(STT_USRUserId).Value
    End Get
    Set(ByVal value As String)
      UserSettings(STT_USRUserId).Value = value
    End Set
  End Property

  Public Shared Property Password() As String
    Get
      Return UserSettings(STT_USRPassword).Value
    End Get
    Set(ByVal value As String)
      UserSettings(STT_USRPassword).Value = value
    End Set
  End Property

Oltre alle proprietà che mappano le due collezioni dei setting, per renderle accessibili per qualsiasi uso (sicuramente ne troveremo qualcuno), ci sono delle proprietà che mappano gli elementi delle collezioni, il che ci permette di poter tipizzare i setting anche se sul file dati vengono memorizzati come stringa. Un esempio è il setting Trusted, che stabilisce se utilizzare una connessione trusted oppure una connessione SQL, che viene restituito come booleano dal get e impostato come stringa dal set.

Per alcune proprietà bisogna implementare un metodo per validare la classe:

    public static bool IsValid()
    {
      bool ret = false;
      try
      {
        if (Trusted)
        {
          ret = (SqlServer != null && SqlServer.Trim().Length > 0 &&
              SqlDatabase != null && SqlDatabase.Trim().Length > 0);
        }
        else
        {
          ret = (SqlServer != null && SqlServer.Trim().Length > 0 &&
              SqlDatabase != null && SqlDatabase.Trim().Length > 0 &&
              UserID != null && UserID.Trim().Length > 0);
        }
      }
      catch (Exception)
      {
        ret = false;
      }
      return (ret);
    }
  Public Shared Function IsValid() As Boolean
    Dim ret As Boolean = False
    Try
      If Trusted Then
        ret = Not (String.IsNullOrEmpty(SqlServer) OrElse String.IsNullOrEmpty(SqlDatabase))
      Else
        ret = Not (String.IsNullOrEmpty(SqlServer) OrElse String.IsNullOrEmpty(SqlDatabase) _
              OrElse String.IsNullOrEmpty(UserID))
      End If

    Catch ex As Exception
      ret = False
    End Try
    Return ret
  End Function

Il metodo di validazione della classe ci permette di stabilire se i setting della connection string sono corretti o meno, anche se in modo superficiale. Infatti, da qui non viene controllato se il server o il database esistono e sono validi, ma viene fatto un mero controllo di correttezza formale.
Ovviamente, il controllo di validazione della connessione dovrà essere affidato ad una funzione specifica che scriveremo in seguito.

E' necessario anche aggiungere alcune risorse, quindi apriamo il file Resources.resx di UsingSqlServer - per C#, bisogna aggiungerlo alle Properties:

  SqlConnectionSql Data Source={0}; Initial Catalog={1}; Persist Security Info=True; User ID={2};Password={3}
  SqlConnectionTrusted Data Source={0}; Initial Catalog={1}; Integrated Security=True

Queste sono le stringhe di formato per le nostre connectionstring. Adesso siamo pronti per implementare altre proprietà:

    public static string CnString
    {
      get
      {
        string retCnStr = string.Empty;
        if (IsValid())
        {
          if (Trusted)
          {
            retCnStr = string.Format(Properties.Resources.SqlConnectionTrusted, 
                                     SqlServer, SqlDatabase);
          }
          else
          {
            retCnStr = string.Format(Properties.Resources.SqlConnectionSql, 
                                     SqlServer, SqlDatabase, UserID, Password);
          }
        }
        return (retCnStr);
      }
    }

    public static string CnStringMaster
    {
      get
      {
        string retCnStr = string.Empty;
        if (Trusted)
        {
          if (SqlServer != null && SqlServer.Trim().Length > 0)
          {
            retCnStr = string.Format(Properties.Resources.SqlConnectionTrusted, 
                                     SqlServer, SYSDB_Master);
          }
        }
        else
        {
          if (SqlServer != null && SqlServer.Trim().Length > 0 &&
            UserID != null && UserID.Trim().Length > 0 &&
            Password != null)
          {
            retCnStr = string.Format(Properties.Resources.SqlConnectionSql, 
                                     SqlServer, SYSDB_Master, UserID, Password);
          }
        }
        return (retCnStr);
      }
    }

    public static string CnStringMsdb
    {
      get
      {
        string retCnStr = string.Empty;
        if (Trusted)
        {
          if (SqlServer != null && SqlServer.Trim().Length > 0)
          {
            retCnStr = string.Format(Properties.Resources.SqlConnectionTrusted, 
                                     SqlServer, SYSDB_Msdb);
          }
        }
        else
        {
          if (SqlServer != null && SqlServer.Trim().Length > 0 &&
            UserID != null && UserID.Trim().Length > 0 &&
            Password != null)
          {
            retCnStr = string.Format(Properties.Resources.SqlConnectionSql, 
                                     SqlServer, SYSDB_Msdb, UserID, Password);
          }
        }
        return (retCnStr);
      }
    }
  Public Shared ReadOnly Property CnString() As String
    Get
      Dim retCnStr As String = String.Empty
      If IsValid() Then
        If Trusted Then
          retCnStr = String.Format(My.Resources.SqlConnectionTrusted, SqlServer, SqlDatabase)
        Else
          retCnStr = String.Format(My.Resources.SqlConnectionSql, _
                                   SqlServer, SqlDatabase, UserID, Password)
        End If
      End If
      Return retCnStr
    End Get
  End Property

  Public Shared ReadOnly Property CnStringMaster() As String
    Get
      Dim retCnStr As String = String.Empty
      If Trusted() Then
        If Not String.IsNullOrEmpty(SqlServer) Then
          retCnStr = String.Format(My.Resources.SqlConnectionTrusted, SqlServer, SYSDB_Master)
        End If
      Else
        If Not String.IsNullOrEmpty(SqlServer) AndAlso Not String.IsNullOrEmpty(UserID) _
              AndAlso Not Password Is Nothing Then
          retCnStr = String.Format(My.Resources.SqlConnectionSql, _
                                   SqlServer, SYSDB_Master, UserID, Password)
        End If
      End If
      Return retCnStr
    End Get
  End Property

  Public Shared ReadOnly Property CnStringMsdb() As String
    Get
      Dim retCnStr As String = String.Empty
      If Trusted Then
        If Not String.IsNullOrEmpty(SqlServer) Then
          retCnStr = String.Format(My.Resources.SqlConnectionTrusted, SqlServer, SYSDB_Msdb)
        End If
      Else
        If Not String.IsNullOrEmpty(SqlServer) AndAlso Not String.IsNullOrEmpty(UserID) _
              AndAlso Not Password Is Nothing Then
          retCnStr = String.Format(My.Resources.SqlConnectionSql, _
                                   SqlServer, SYSDB_Msdb, UserID, Password)
        End If
      End If
      Return retCnStr
    End Get
  End Property

Da notare inoltre le property readonly per le connection string: la connection string primaria, che si connette al database applicativo, fa una validazione utilizzando il metodo IsValid e restituisce una connection string valida oppure una stringa vuota. Lo stesso fanno le due connection string ai database di sistema di SQL Server che introduciamo, le cui funzioni saranno spiegate al momento opportuno.
Le connessioni di sistema fanno una validazione locale, in quanto non possono utilizzare IsValid, dato che il database è diverso da quello standard.
In questo modo, ci sarà facile verificare immediatamente da programma la correttezza della connessione al database e richiedere di aggiornarne i parametri, eventualmente.

    public static void SaveSettings()
    {
      try
      {
        TAndTSettings.SaveSettings(mAppFileName, true, AppSettings);
        TAndTSettings.SaveSettings(mUserFileName, true, UserSettings);
      }
      catch (Exception ex)
      {
        throw new ApplicationException(" " + mClassName + "."
          + System.Reflection.MethodBase.GetCurrentMethod().Name, ex);
      }
    }
  Public Shared Sub SaveSettings()
    Try
      TAndTSettings.SaveSettings(mAppFileName, True, AppSettings)
      TAndTSettings.SaveSettings(mUserFileName, True, UserSettings)

    Catch ex As Exception
      Throw New ApplicationException(" " + mClassName + "." _
                                         + System.Reflection.MethodBase.GetCurrentMethod().Name, ex)
    End Try
  End Sub

Il metodo di salvataggio dei setting è semplice, visto che aggiorna solo i due file su disco.

    public static void LoadSettings()
    {
      try
      {
        bool hasToBeSaved = false;
        TAndTSettings.LoadSettings(mAppFileName, ref mAppSettings);

        if (!AppSettings.ExistSetting(STT_APPSqlServer))
        {
          AppSettings.Add(STT_APPSqlServer, DEFAULT_MyServer);
          hasToBeSaved = true;
        }
        if (!AppSettings.ExistSetting(STT_APPSqlDatabase))
        {
          AppSettings.Add(STT_APPSqlDatabase, DEFAULT_MyDatabase);
          hasToBeSaved = true;
        }

        TAndTSettings.LoadSettings(mUserFileName, ref mUserSettings);

        if (!UserSettings.ExistSetting(STT_USRTrusted))
        {
          UserSettings.Add(STT_USRTrusted, DEFAULT_True);
          hasToBeSaved = true;
        }
        if (!UserSettings.ExistSetting(STT_USRUserId))
        {
          UserSettings.Add(STT_USRUserId, DEFAULT_MyUser);
          hasToBeSaved = true;
        }
        if (!UserSettings.ExistSetting(STT_USRPassword))
        {
          UserSettings.Add(STT_USRPassword, DEFAULT_MyPassword);
          hasToBeSaved = true;
        }
        if (hasToBeSaved)
        {
          SaveSettings();
        }
      }
      catch (Exception ex)
      {
        throw new ApplicationException(" " + mClassName + "."
          + System.Reflection.MethodBase.GetCurrentMethod().Name, ex);
      }
    }
  Public Shared Sub LoadSetting()
    Try
      Dim hasToBeSaved As Boolean = False
      TAndTSettings.LoadSettings(mAppFileName, mAppSettings)

      If Not AppSettings.ExistSetting(STT_APPSqlServer) Then
        AppSettings.Add(STT_APPSqlServer, DEFAULT_MyServer)
        hasToBeSaved = True
      End If
      If Not AppSettings.ExistSetting(STT_APPSqlDatabase) Then
        AppSettings.Add(STT_APPSqlDatabase, DEFAULT_MyDatabase)
        hasToBeSaved = True
      End If

      TAndTSettings.LoadSettings(mUserFileName, mUserSettings)

      If Not UserSettings.ExistSetting(STT_USRTrusted) Then
        UserSettings.Add(STT_USRTrusted, DEFAULT_True)
        hasToBeSaved = True
      End If
      If Not UserSettings.ExistSetting(STT_USRUserId) Then
        UserSettings.Add(STT_USRUserId, DEFAULT_MyUser)
        hasToBeSaved = True
      End If
      If Not UserSettings.ExistSetting(STT_USRPassword) Then
        UserSettings.Add(STT_USRPassword, DEFAULT_MyPassword)
        hasToBeSaved = True
      End If

      If hasToBeSaved Then
        SaveSettings()
      End If


    Catch ex As Exception
      Throw New ApplicationException(" " + mClassName + "." _
                                         + System.Reflection.MethodBase.GetCurrentMethod().Name, ex)
    End Try
  End Sub

Il metodo di caricamento dei setting richiama le funzioni della collection, poi verifica l'esistenza di tutti i setting generandoli se non esistono. In questo modo, abbiamo la certezza che non vi saranno eccezioni per l'errata manipolazione di un file fatta magari da Notepad.

    static UsingSqlServerConfig()
    {
      LoadSettings();
    }
  Shared Sub New()
    LoadSetting()
  End Sub

Il costruttore (sì, un costruttore statico) viene eseguito automaticamente quando la classe statica è referenziata la prima volta e si occupa di creare le collezioni e caricarle con i dati dei files XML oppure generando i valori di default (chiamando il metodo appena creato).

Arrivederci alla prossima puntata
Per questa puntata basta così, non c'è il test perché questa classe verrà testata per benino nella form che svilupperemo per gestire l'aggiornamento del contenuto dei file di setting, la prima vera form del progetto che servirà a qualche cosa.
Come al solito, il codice fin qui prodotto è scaricabile dall'Area Download.
Potete scrivere Feedback (commenti, critiche, suggerimenti, correzioni) sul blog di Sabrina.