Press "Enter" to skip to content

8 – Lavorare con i dati – UsersDb Testiamo la libreria Dati

In questa seconda parte dedicata alla libreria dati, scriviamo del codice per testare il corretto funzionamento dei metodi della classe UsersDp che abbiamo generato nella libreria dati. Quello che faremo probabilmente ci aiuterà ad iniziare a studiare quanto necessario fare per iniziare a sviluppare poi il Tier dei servizi.

La prima operazione che dobbiamo fare per iniziare a sviluppare del codice per testare la classe UsersDp è referenziare la libreria dati che abbiamo generato nel precedente articolo all’interno del progetto contenente l’interfaccia utente.

08_usersdb_01_addreference

Pertanto dal nodo References del progetto UsersDb, menu contestuale, Add Reference, selezioniamo Projects, Solutions e come nell’immagine spuntiamo SqlServerDp.

Una volta fatto questo, abbiamo bisogno di creare un Contesto per la nostra User interface, che cos’è un contesto, il nome l’ho deciso arbitrariamente ma mi sembra sia appropriato, infatti è un luogo ove memorizzeremo i parametri funzionali della nostra applicazione, parametri che dovranno essere accessibili a tutta l’applicazione e gestiti in modo consistente. Pertanto, la prima cosa che facciamo nel nostro progetto è creare una nuova cartella che chiameremo Context.

08_usersdb_02_addContextFolder

08_usersdb_03_addContextFolder

Ecco la cartella, aggiunta al progetto UsersDb. In questa cartella andremo a creare la classe AppContext, che sarà la classe ove memorizzeremo tutti i parametri applicativi. Per questa applicazione, il primo importante parametro che ci serve anche solo per i test è la stringa di connessione al database.

Essendo in fase di test, creeremo qualcosa di temporaneo, in seguito, quando andremo a sviluppare la User Interface reale per manipolare in nostri dati, modificheremo in modo opportuno questa classe e ne creeremo delle altre per poter gestire la possibilità da parte dell’utente di scegliere sia il tipo di database che impostare la stringa di connessione allo stesso.

08_usersdb_04_addAppContextClass

Creiamo la classe AppContext

Andiamo sulla cartella appena creata nel progetto e selezioniamo dal menu contestuale Add > New Item… selezionando un oggetto di tipo Class e lo chiamiamo AppContext.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace Dnw.Users.Context
{

    public class AppContext 
    {
        #region Public Fields
        public const string APP_ID = "it.DotNetwork.UsersDb";
 
        private const string CNS_SqlServer = "Server=localhost;Database=UsersDb;Trusted_Connection=True;Persist Security Info=True";
 
        #endregion Public Fields
 
        #region Private Fields
 
        private static volatile AppContext mInstance;
 
        private static object syncRoot = new Object();
 
        #endregion Private Fields
 
        #region Public Properties
 
        public static AppContext Instance
        {
            get
            {
                if (mInstance == null)
                {
                    lock (syncRoot)
                    {
                        if (mInstance == null)
                            mInstance = new AppContext();
                    }
                }
 
                return mInstance;
            }
        }
 
 
        public string CnString
        {
            get
            {
                return CNS_SqlServer;
            }
 
        }
 
        #endregion Public Properties
 
    }
}

E’ una classe molto semplice, infatti contiene al suo interno ben poche cose, ma è anche una classe speciale, infatti si tratta di una classe Singleton, ovvero di una classe non statica ma instanziata una sola volta all’interno di un applicazione ed accessibile da qualsiasi oggetto all’interno dell’applicazione tramite la property Instance.

E’ molto simile ad una classe static, e può essere rimpiazzata da quest’ultimo tipo di classe se vogliamo, la sola differenza di un singleton è che essendo istanziabile, al contrario dello static, può essere ricreata se necessario aggiornare il contesto a runtime.

Una classe Singleton conterrà al suo interno sempre due variabili a livello di classe che per convenzione hanno i nomi da noi assegnati ed una property statica che è un istanza di se stessa che rappresenta l’oggetto singleton globale.

Possiamo notare come la classe si auto istanzia se possiamo usare un termine inventato, la prima volta che viene richiesto il suo contenuto. Inoltre, essendo accessibile da tutta l’applicazione, per renderla Thread Safe, viene utilizzato un thread lock per regolare le chiamate all’istanza singleton.

La sola property della classe è la nostra stringa di connessione, che essendo al momento stata creata solo ai fini di testare il codice, abbiamo inserito come costante. Quando implementeremo la vera applicazione, creeremo quanto necessario a leggerla e scriverla su un file di configurazione.

Proseguiremo il suo sviluppo nelle prossime puntate di questa breve serie.

Le modifiche a UsersWindow.xaml e UsersWindow.xaml.cs

Per testare la nostra classe, andiamo ad aggiungere un bottone nella nostra finestra principale:

<Button
    Margin="4,2,4,2"
    Padding="10,4,10,4"
    Click="TestDataProvider_Click">
                <TextBlock Text="Test Data provider class"/>
</Button>

Lo inseriamo nello StackPanel prima del bottone di test della classe Entity. Questo perché abbiamo inserito l’ordinamento da destra a sinistra nello stack panel.

private void TestDataProvider_Click(object sender, RoutedEventArgs e)
{
    try
    {
        UsersDp dp = new UsersDp(AppContext.Instance.CnString);
        Result<List<User>> retSelect = dp.SelectAll();
        if (retSelect.HasError)
        {
            MessageBox.Show(retSelect.Error, "ERROR", MessageBoxButton.OK, MessageBoxImage.Error);
            return;
        }
        Users.Clear();
        foreach (User x in retSelect.Data)
        {
            Users.Add(x);
        }

Iniziamo a generare il nostro event handler per il bottone che ci testerà il funzionamento della classe data provider. La prima cosa che facciamo è creare un istanza della classe UsersDp, passando al costruttore la stringa di connessione al database.

Chiamiamo il metodo Select, in caso di errore diamo un messaggio e usciamo dal metodo, altrimenti, aggiungiamo tutti gli utenti letti dal database alla collection che avevamo creato nella Window dopo averla ripulita del suo contenuto.

StringBuilder sb = new StringBuilder();
sb.AppendLine("Selected users prior modification are:");
foreach (User u in Users)
{
    sb.AppendLine(u.ToString());
}

Proseguiamo creando uno string builder che conterrà il risultato di tutte le prove che faremo in sequenza e vi appendiamo una stringa descrittiva e il contenuto della collection letta.

User usr = new User();
usr.ID = -1;
usr.Computer = "PC_999";
usr.LoginType = TypeOfLogin.Windows;
usr.UserName = "Giovanni Dalle Bande Nere";
usr.WinDomain = "Domain_999";
usr.WinUser = "GDBNere";
Users.Add(usr);
 
usr = new User();
usr.ID = -2;
usr.Computer = "PC_888";
usr.LoginType = TypeOfLogin.Manual;
usr.UserName = "Bartolomeo Moranni";
usr.WinDomain = "Domain_999";
usr.WinUser = "BMoranni";
Users.Add(usr);
 
usr = new User();
usr.ID = -3;
usr.Computer = "PC_777";
usr.LoginType = TypeOfLogin.Windows;
usr.UserName = "Luigino Fantoni";
usr.WinDomain = "Domain_999";
usr.WinUser = "LFantoni";
Users.Add(usr);

Aggiungiamo tre nuovi utenti alla collection Users.

Result<List<User>> retInsert = dp.Insert(Users.Where(x => x.ID < 0).ToList());
if (retInsert.HasError)
{
    MessageBox.Show(retInsert.Error, "ERROR", MessageBoxButton.OK, MessageBoxImage.Error);
    return;
}
sb.AppendLine(new string('-', 80));
sb.AppendLine("The Added users are:");
foreach (User u in retInsert.Data)
{
    sb.AppendLine(u.ToString());
}

Eseguiamo l’insert e aggiungiamo il contenuto degli User che abbiamo inserito nel database alla stringa che visualizzerà il log delle operazioni fatte.

usr = Users.FirstOrDefault(x => x.ID >= 1);
usr.Computer = usr.Computer + " MOD";
usr.Password = "W0rkF0ll0wsMe";
usr.UserName = usr.UserName + " MOD";
List<User> changedUsers = new List<User>();
changedUsers.Add(usr);
usr = Users.FirstOrDefault(x => x.ID >= (usr.ID + 1));
usr.Computer = usr.Computer + " MOD";
usr.Password = "But1RunF@ster";
usr.UserName = usr.UserName + " MOD";
changedUsers.Add(usr);

Modifichiamo due utenti nella collection e aggiungiamoli alla lista dei dati modificati.

Result<List<User>> retUpdate = dp.Update(changedUsers);
if (retUpdate.HasError)
{
    MessageBox.Show(retUpdate.Error, "ERROR", MessageBoxButton.OK, MessageBoxImage.Error);
    return;
}
 
sb.AppendLine(new string('-', 80));
sb.AppendLine("The Edited users are:");
foreach (User u in retUpdate.Data)
{
    sb.AppendLine(u.ToString());
}

Aggiorniamo il database con il metodo apposito e aggiorniamo la stringa che visualizzerà il log delle operazioni fatte.

Result<List<int>> retDelete = dp.Delete(Users.Where(x => x.WinDomain == "Domain_999").ToList());
if (retDelete.HasError)
{
    MessageBox.Show(retDelete.Error, "ERROR", MessageBoxButton.OK, MessageBoxImage.Error);
    return;
}

Cancelliamo dal database i tre record che abbiamo inserito utilizzando il codice di WinDomain per selezionarli dalla collection Users e passarli al metodo di cancellazione.

sb.AppendLine(new string('-', 80));
sb.AppendLine("The Deleted users are:");
foreach (int i in retDelete.Data)
{
    usr = Users.FirstOrDefault(x => x.ID == i);
    if (usr != null)
    {
        sb.AppendLine(usr.ToString());
        Users.Remove(usr);
    }
}

Visualizziamo i dati appena cancellati leggendoli dalla collection Users per poi rimuoverli definitivamente dalla collection.

sb.AppendLine(new string('-', 80));
sb.AppendLine("The Remaining users are:");
foreach (User u in Users)
{
    sb.AppendLine(u.ToString());
}

Visualizziamo il contenuto della collection.

retSelect = dp.SelectAll();
if (retSelect.HasError)
{
    MessageBox.Show(retSelect.Error, "ERROR", MessageBoxButton.OK, MessageBoxImage.Error);
    return;
}
Users.Clear();
foreach (User x in retSelect.Data)
{
    Users.Add(x);
}

Per sicurezza, rileggiamo i dati dal database.

sb.AppendLine("Check if true re-reading database:");
foreach (User u in Users)
{
    sb.AppendLine(u.ToString());
}

Aggiungiamo il loro contenuto alla stringa di log.

ResultText = sb.ToString();

Visualizziamo sulla TextBox della window il contenuto del log delle nostre operazioni di test.

Lanciando l’applicazione e cliccando sul bottone Test Data Provider, se avete modificato manualmente il progetto del precedente articolo, riceverete un errore, che dice che il parametro @ID non è definito, questo perché copiando e incollando i metodi, abbiamo lasciato nel metodo SelectAll la query di cancellazione invece di quella di selezione, pertanto l’oggetto da errore. Rimettiamo a posto la stringa con la query di selezione e rieseguiamo tutto.

Il risultato sarà il seguente:

Selected users prior modification are:
Windows Credentials: MRossi DmRossi Computer PC001 MOD

User Data: 1 Mario MOD W0rkF0ll0wsMe None

Windows Credentials: GVerdi DmRossi Computer PC002 MOD

User Data: 2 Gervasio MOD But1RunF@ster Windows

Windows Credentials: LBianchi DmRossi Computer PC003

User Data: 3 Lorenzo Windows

Windows Credentials: SOsso DmRossi Computer PC003

User Data: 4 Stefano Windows

——————————————————————————–

The Added users are:

Windows Credentials: GDBNere Domain_999 Computer PC_999

User Data: 1005 Giovanni Dalle Bande Nere Windows

Windows Credentials: BMoranni Domain_999 Computer PC_888

User Data: 1006 Bartolomeo Moranni Manual

Windows Credentials: LFantoni Domain_999 Computer PC_777

User Data: 1007 Luigino Fantoni Windows

——————————————————————————–

The Edited users are:

Windows Credentials: MRossi DmRossi Computer PC001 MOD MOD

User Data: 1 Mario MOD MOD W0rkF0ll0wsMe None

Windows Credentials: GVerdi DmRossi Computer PC002 MOD MOD

User Data: 2 Gervasio MOD MOD But1RunF@ster Windows

——————————————————————————–

The Deleted users are:

Windows Credentials: GDBNere Domain_999 Computer PC_999

User Data: 1005 Giovanni Dalle Bande Nere Windows

Windows Credentials: BMoranni Domain_999 Computer PC_888

User Data: 1006 Bartolomeo Moranni Manual

Windows Credentials: LFantoni Domain_999 Computer PC_777

User Data: 1007 Luigino Fantoni Windows

——————————————————————————-

The Remaining users are:

Windows Credentials: MRossi DmRossi Computer PC001 MOD MOD

User Data: 1 Mario MOD MOD W0rkF0ll0wsMe None

Windows Credentials: GVerdi DmRossi Computer PC002 MOD MOD

User Data: 2 Gervasio MOD MOD But1RunF@ster Windows

Windows Credentials: LBianchi DmRossi Computer PC003

User Data: 3 Lorenzo Windows

Windows Credentials: SOsso DmRossi Computer PC003

User Data: 4 Stefano Windows

Check if true re-reading database:

Windows Credentials: MRossi DmRossi Computer PC001 MOD MOD

User Data: 1 Mario MOD MOD W0rkF0ll0wsMe None

Windows Credentials: GVerdi DmRossi Computer PC002 MOD MOD

User Data: 2 Gervasio MOD MOD But1RunF@ster Windows

Windows Credentials: LBianchi DmRossi Computer PC003

User Data: 3 Lorenzo Windows

Windows Credentials: SOsso DmRossi Computer PC003

User Data: 4 Stefano Windows


Come potete vedere dal log, le quattro operazioni di CRUD funzionano correttamente. Non preoccupatevi se il valore delle ID dei record aggiunti variano, ovviamente dipendono da quello che avete inserito e da quanti dati avete creato e cancellato sul vostro database.

Direi che con questo il test della classe Data Provider possiamo considerarlo completato, e passeremo alle classi servizio nella prossima puntata.

Riepilogo

In questo articolo abbiamo visto le seguenti cose:

  • Come referenziare la libreria dei Data Provider nell’eseguibile
  • Come creare una classe AppContext per contenere i parametri di contesto applicativi che devono essere noti a livello di tutta l’applicazione
  • Come creare un Button per testare la classe.
  • Come testare i 4 metodi di manipolazione dati della classe UsersDp.

Potete scaricare il progetto di esempio al link qui indicato:

Per qualsiasi domanda, commento, curiosità, approfondimento, o per segnalare un errore potete utilizzare il link alla form di contatto in cima alla casa.