Press "Enter" to skip to content

6 – Lavorare con i dati – UserDb La libreria Entities

In questo secondo articolo dedicato all’applicazione multi tier, creiamo il Tier Jolly ovvero la libreria contenente la classe (in una applicazione vera le classi) dati, l’unica che sarà nota a tutti gli strati in quanto saranno le sue istanze a contenere i dati che andranno dal database alla user interface e dalla user interface al database.

Come è fatta una classe dati, usualmente nel modo più semplice possibile, contiene solo property e potrebbe contenere dei metodi di servizio (il ToString è solitamente utile, il CompareTo se necessario) ma usualmente in queste classi si cerca di mettere meno cose possibili per renderle snelle, visto che viaggeranno da uno strato all’altro.

La sola cosa che solitamente implemento sempre in queste classi è l’interfaccia INotifyPropertyChanged, perché se vengono utilizzate dalla User Interface WPF non devo necessariamente creare una ulteriore classe che implementi le notifiche per poterle usare come sorgente dati in una qualsiasi window.

Ma vediamo come è fatto il progetto UsersEntities che aggiungiamo alla Solution UsersDb.

usersentities_properties_01[5]

Come potete notare dallo screenshot ho creato una class library ovvero una libreria di classi e, anche in questo caso, prima di iniziare a inserire la prima classe nella libreria ho modificato il progetto nel seguente modo:

  1. L’assembly name è stato modificato in DNW.UsersEntities, per quale motivo? una preferenza personale, tutte le mie dll hanno il prefisso aziendale in modo da essere immediatamente riconoscibili, non è un obbligo, solo una preferenza.
  2. Il Default Namespace, come indicato nel precedente post, deve essere modificato subito, in questo caso, ho usato lo stesso che nel progetto eseguibile, perché le classi saranno ospitate nella cartella Entities, e Visual Studio costruirà automaticamente il namespace corretto.
  3. In Assembly information ho messo le descrizioni come nell’eseguibile.
  4. Ho aggiunto l’icona Dotnetwork alla DLL, essendo una dll di sole classi non verrà usata, ma mi piace che tutte le mie librerie abbiano anche l’icona aziendale.

usersentities_properties_02[5]

Dopo aver modificato i dati dell’assembly in Application, vado a firmare la dll.

usersentities_folder_03[4]

L’operazione successiva è quella di creare la cartella in cui inserirò la prima classe, e dove potrei aggiungerne altre se la mia applicazione gestisse più di una tabella.

La classe User

using System.ComponentModel;
using System.Text;
 
namespace Dnw.Users.Entities
{
    public class User : INotifyPropertyChanged
    {
... here goes the class code
    }
}

La classe User, che rappresenta un record della nostra tabella sul database, come potrete notare per ora conterrà ben poco:

public string Computer
{
    get
    {
        return mComputer;
    }
    set
    {
        mComputer = value;
        OnPropertyChanged(FLD_Computer);
    }
}

La property che rappresenta il nome del computer, (ometto le dichiarazioni della variabile di classe e della costante con il nome campo usata da property changed)

public int ID
{
    get
    {
        return mID;
    }
    set
    {
        mID = value;
        OnPropertyChanged(FLD_ID);
    }
}

L’ID univoco che sarà generato automaticamente sul database quando verrà generato il record.

public TypeOfLogin LoginType
{
    get
    {
        return mLoginType;
    }
    set
    {
        mLoginType = value;
        OnPropertyChanged(FLD_LoginType);
    }
}

Il tipo di Login, ho creato un tipo enumerato, che potrà assumere i seguenti valori:


public enum TypeOfLogin
{

    None = 0,

    Windows,

    Manual
}

Questa enumerazione ci permetterà due cose, decidere se l’accesso all’applicazione sarà garantito semplicemente controllando Windows User e Dominio o se chiederemo Username e Password al nostro utente per accedere alla applicazione, oppure, il valore None, ci permetterà di disattivare l’accesso ad un utente.

public string Password
{
    get
    {
        return mPassword;
    }
    set
    {
        mPassword = value;
        OnPropertyChanged(FLD_Password);
    }
}

Il campo che rappresenta la password che l’utente dovrà digitare per accedere all’applicazione nel caso di login manuale. In questo esempio la memorizzeremo in chiaro, perché non è nei nostri scopi andare a gestire la crittografia, questo lo faremo in caso in un articolo futuro. Ma annotate che questo specifico campo dovrebbe essere crittografato per ovvie ragioni.

public string UserName
{
    get
    {
        return mUserName;
    }
    set
    {
        mUserName = value;
        OnPropertyChanged(FLD_UserName);
    }
}

Il campo che rappresenta il nome utente per l’applicazione, anche in questo caso ha due usi, il primo è mostrare un nome comprensibile per l’utente da usare per determinate azioni dell’applicazione (la più semplice un benvenuto), il secondo uso invece è quello di obbligare l’utente a digitarlo assieme alla password se attiviamo il login manuale.

public string WinDomain
{
    get
    {
        return mWinDomain;
    }
    set
    {
        mWinDomain = value;
        OnPropertyChanged(FLD_WinDomain);
    }
}

Il nome di dominio a cui appartiene il PC (sia esso un dominio active directory o un workgroup non importa).

public string WinUser
{
    get
    {
        return mWinUser;
    }
    set
    {
        mWinUser = value;
        OnPropertyChanged(FLD_WinUser);
    }
}

Il nome dell’utente windows dell’utente applicativo, che potremo usare assieme a dominio e a nome del computer per mappare gli utenti che hanno accesso all’applicazione e volendo, per fare accedere automaticamente gli utenti all’applicazione se usiamo il LoginType Windows, senza obbligarli a digitare un ulteriore user name e password.

public override string ToString()
{
    StringBuilder sb = new StringBuilder();
    sb.AppendFormat("Windows Credentials: {0} {1} Computer {2}", WinUser, WinDomain, Computer);
    sb.AppendLine();
    sb.AppendFormat("User Data: {0} {1} {2} {3}", ID, UserName, Password, LoginType);
    sb.AppendLine();
    return sb.ToString();
}
 
protected virtual void OnPropertyChanged(string propertyName)
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

Completano la classe il metodo ToString, utile in fase di debug, e ovviamente l’implementazione dell’evento Property Changed dell’interfaccia INotifyPropertyChanged, ho omesso la dichiarazione dell’evento che ovviamente è nella classe.

A questo punto la nostra classe dati è stata completata, pertanto andiamo a fare alcune modifiche all’applicazione WPF UsersDb per verificare che la classe funzioni correttamente.

Il Test della classe User

Per testare la nostra classe, andiamo a creare nella UsersWindow.xaml uno spazio dove visualizzare dei dati, e un pulsante per generare delle istanze della classe e verificare che si comporti come ci aspettiamo.

<Window x:Class="Dnw.Users.Winows.UsersWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:wr="clr-namespace:Dnw.Users.Winows"
        mc:Ignorable="d"
        Title="Users Management" Height="640" Width="960">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TextBox
            Grid.Row="0"
            Margin="4,2,4,2"
            HorizontalAlignment="Stretch"
            VerticalAlignment="Stretch"
            HorizontalContentAlignment="Left"
            VerticalContentAlignment="Top"
            VerticalScrollBarVisibility="Auto"
            Text="{Binding ResultText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <StackPanel
            Grid.Row="1"
            Orientation="Horizontal" 
            FlowDirection="RightToLeft">
            <Button
                Margin="4,2,4,2"
                Padding="10,4,10,4"
                Click="TestEntity_Click">
                <TextBlock Text="Test Entity Class"/>
            </Button>
        </StackPanel>
    </Grid>
</Window>

Modifichiamo lo xaml della UsersWindow, inserendo una TextBox, ed uno StackPanel con all’interno un Button.

Tale button ha un evento Click su cui andremo a fare il nostro test.

Nel codice di UsersWindow.xaml.cs, vediamo cosa abbiamo inserito per fare il test.

public UsersWindow()
{
    InitializeComponent();
    this.Icon = BitmapFrame.Create(new Uri("pack://application:,,,/icon_2016.ico", UriKind.RelativeOrAbsolute));
    DataContext = this;
    Users = new ObservableCollection<User>();
}

Per prima cosa, visto che ci piace fare le cose come se fosse una vera applicazione, andiamo a modificare l’icona standard della Window, che appare in alto a sinistra mettendovi l’icona dell’applicazione, che abbiamo inserito nelle property del progetto ed è stata aggiunta per noi sulla root folder del progetto.

Indichiamo poi alla Window che è il ViewModel di se stessa, inserendo il this nel DataContext.

Infine, istanziamo la collection di classi User che definiremo fra poco.

public ObservableCollection<User> Users
{
    get
    {
        return mUsers;
    }
    private set
    {
        mUsers = value;
        OnPropertyChanged(FLD_Users);
    }
}

Users è una observable collection di User, perché abbiamo usato una Observable Collection invece di una List, in questo caso non cambierebbe proprio nulla, visto che la collection non interagisce direttamente con la User Interface, ma in seguito, quando creeremo la user interface per gestire la nostra tabella utenti, l’Observable tornerà utile perché è una collection creata da chi ha disegnato WPF in modo tale da scatenare automaticamente gli eventi che indicano alla User Interface che il suo contenuto è stato modificato aggiungendo o togliendo elementi o modificandoli. Questo permetterà ai controlli della User Interface che gestiranno la collezione di oggetti di aggiornarsi automaticamente.

Per utilizzare l’oggetto User nel codice della Window, abbiamo usato il menu contestuale sul nodo References del solution explorer ed abbiamo aggiunto un riferimento al progetto UserEntities in modo che le classi (in questo caso la classe) contenuta nella libreria sia resa disponibile all’applicazione.

userentities_add_reference_01[4]

Da references, tasto destro Add Reference…

userentities_add_reference_02[5]

Selezioniamo Projects, solutions e verrà mostrato solo UserEntities, quando aggiungeremo gli altri progetti della nostra soluzione, si aggiungeranno a questo e potranno essere referenziati.

Proseguiamo con le property della nostra Window.

public string ResultText
{
    get
    {
        return mResultText;
    }
    set
    {
        mResultText = value;
        OnPropertyChanged(FLD_ResultText);
    }
}

Aggiungiamo anche una property per visualizzare dati sulla textbox, in modo da fare il nostro primo semplice test funzionale sulle entity della nostra classe. Faccio notare che anche in questo caso, la classe implementa INotifyPropertyChanged per fornire alla user interface gli eventi per aggiornare il suo contenuto.

private void TestEntity_Click(object sender, RoutedEventArgs e)
{
    try
    {
        User usr = new User()
        {
            WinUser = "MRossi",
            WinDomain = "DMCompany",
            UserName = "Mario",
            Password = "LaPassw0rd",
            LoginType = TypeOfLogin.Windows,
            Computer = "PCMARIO",
            ID = 1
        };
        Users.Add(usr);
        usr = new User()
        {
            WinUser = "GVerdi",
            WinDomain = "DMCompany",
            UserName = "Giuseppe",
            Password = "Passw0rdLa",
            LoginType = TypeOfLogin.Manual,
            Computer = "PCGIUSEPPE",
            ID = 2
        };
        Users.Add(usr);
 
        usr = new User()
        {
            WinUser = "LBianchi",
            WinDomain = "DMCompany",
            UserName = "Luca",
            Password = "Amin0Acid0",
            LoginType = TypeOfLogin.Windows,
            Computer = "PCLUCA",
            ID = 3
        };
        Users.Add(usr);
 
        StringBuilder sb = new StringBuilder();
        foreach (User u in Users)
        {
            sb.AppendLine(u.ToString());
        }
        ResultText = sb.ToString();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "ERROR", MessageBoxButton.OK, MessageBoxImage.Error);
    }
}

E veniamo al metodo di test, implementato sul click del button, per testare la nostra classe e vedere se funziona, creiamo 3 User, li inseriamo nella collection predisposta e poi leggiamo il loro contenuto usando il ToString per pubblicarlo sulla nostra finestra nella variabile ResultText.

Se tornate a osservare lo xaml, la riga seguente:

Text="{Binding ResultText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

E’ quella che fa in modo che il testo della TextBox sia in Binding con la variabile ResultText e quando si verifica una modifica alla property, la textbox mostrerà il suo contenuto.

usersentities_window_04[4]

Il risultato di quanto implementato è mostrato nell’immagine qui sopra. Abbiamo realizzato il primo mattone della nostra applicazione multi tier.

Riepilogo

In questo articolo abbiamo parlato di:

  • Come generare una Class Library e configurarne il nome della dll prodotta ed il namespace
  • Come creare una classe nella libreria di classi
  • Come implementare le property e i metodi base per un’entity che rappresenta il record di una tabella.
  • Come implementare una semplice interfaccia (temporanea) per testare il funzionamento della nuova classe.
  • Come referenziare la libreria di classi per utilizzarla nell’applicazione.
  • Come utilizzare le basi del pattern MVVM per implementare l’interfaccia.

 

Potete scaricare il progetto di esempio dal link qui indicato:

Per qualsiasi domanda, approfondimento, curiosità o per segnalare un errore, utilizzate pure il link al form di contatto in cima alla pagina.