Press "Enter" to skip to content

5 – Lavorare con i dati – Progettiamo una mini applicazione

In questo post, parleremo un poco di teoria ed inizieremo poi a metterla in pratica, progettando una mini applicazione che si occuperà di gestire un database con una singola tabella. Non sarà la stessa tabella degli articoli precedenti, perché così evitiamo la monotonia.

Lo scopo della nostra mini applicazione sarà quello di gestire una tabella su un database che contiene i dati di configurazione degli utenti di una applicazione, ma la nostra mini applicazione ci servirà anche a mostrare come si dovrebbe (metto il condizionale perché nel mondo reale non è sempre possibile fare le cose come dovrebbero essere fatte) creare una applicazione Client Server che ci permetta di fare in modo di non far dipendere la nostra applicazione client da uno specifico database, separando in modo netto tutto quello che è l’applicazione client ovvero la parte di User Interface, dalla parte di gestione dati.

Anche in questo caso, si tratterà di una applicazione creata nel modo più semplice possibile, così che anche un Programmatore Solitario, come solitamente sono coloro che fanno programmi per le piccole aziende in Italia, possa implementarla senza dover utilizzare nulla di più che una serie di progetti Libreria di classi, un progetto eseguibile in WPF, costruendo una struttura articolata, che può essere la base di una applicazione vera e propria.

Come si crea una semplice applicazione Multi Tier

Multi Tier in italiano si potrebbe tradurre come Multi Strato, perché in questo tipo di applicazioni si separa in strati (tiers) le parti che compongono l’applicazione allo scopo di ottenere vari benefici. Quello da noi individuato è uno di questi, la possibilità di usare database diversi a supporto dell’applicazione in modo da renderla più flessibile. Ma anche la possibilità di rendere l’applicazione scalabile, separando su macchine diverse i vari strati eseguiti dalla stessa (Ad esempio le applicazioni in cui SQL Server gira su una macchina server, i servizi dati su un altra macchina, ed i client sulle macchine degli utenti).

Nel nostro caso, per dimostrare che non è difficile anche se certamente è molto più complicato di quanto sia stato fare l’applicazione che dimostrava come accedere ai 3 diversi database che trovate nei tre post che precedono questo, realizzeremo i vari Tiers in questo modo:

  1. Tier Data Provider: Una libreria di classi per ognuno dei database che decideremo di supportare, ogni libreria fornirà gli stessi metodi per effettuare il CRUD e ogni altro tipo di operazione sul singolo database utilizzando gli specifici provider dati di ADO.Net.
  2. Tier Business: Una libreria di classi che farà da collegamento fra la User Interface e il Tier Dati, sarà quella che conoscerà e saprà usare tutti i database, ma fornirà alla User Interface dei metodi Agnostici, che sapranno a quale tipo di database rivolgersi grazie alle informazioni di configurazione passate loro a runtime.
  3. Applicazione Client: Un eseguibile WPF che conterrà la user interface che saprà utilizzare le altre due librerie per fare quel che gli serve.
  4. Jolly: Una libreria di classi che sarà la sola utilizzata in tutti e tre i Tier, questa libreria conterrà le Data Entities, ovvero le classi dati, le uniche che transiteranno da uno strato all’altro, da un Tier all’altro neutrali e note a tutti.

WorkingSchema

Come potete vedere non è esattamente qualcosa di semplice, ma la cosa bella è che se poi volessimo implementare altre 10 tabelle oltre alla prima, dovremmo semplicemente aggiungere alle nostre librerie ed al nostro eseguibile le stesse classi per ogni tabella.

E la cosa può rivelarsi interessante per i futuri articoli sulla materia perché da buoni sviluppatori sappiamo che il codice si può riciclare, che si possono creare delle classi base condivise in modo da scrivere il codice una volta e usarlo molte volte.

Iniziamo dal database

Database First, al contrario della moda attuale quella del Code First, chiamandomi Bastian Contrario, o se preferite Sabrinasaurus Rex, parto dal database, perché come ogni DBA, e DB Designer che si rispetti IO progetto il database relazionale come mi pare e piace e non lascio che i demoniaci wizard di visual studio osino farlo al posto mio.

Va bene, sto esagerando, nel momdo reale siete invitati ad usare quello che preferite, a fare le cose nel modo più adatto a rispondere ai vostri problemi, noi che scriviamo articoli siamo qui solamente per farvi vedere che le cose si possono fare in 3247 modi diversi quando si scrive Software, e questo è il bello e il brutto allo stesso modo del mestiere che abbiamo scelto di fare. Non c’è mai un solo modo di fare una cosa, non c’è un solo modo per scrivere un algoritmo, non c’è un solo modo per costruire una applicazione.

Quello che abbiamo appena scritto e che da qui in avanti realizzeremo è uno dei modi in cui si può fare, da cui spero possiate prendere delle idee, qualche buona dritta, uno o due Tips e quattro o cinque Tricks, per poi creare le vostre applicazioni nel modo che a voi più si confà.

Ed ora, bando alle chiacchiere e vediamo come è fatto il nostro database.

La Tabella TbUsers

Field Name Type Allows Null
ID int NO
WinUser nvarchar(255) YES
WinDomain nvarchar(255) YES
UserName nvarchar(255) YES
Computer nvarchar(255) YES
LoginType int YES
Password nvarchar(255) YES

Come potete vedere, si tratta di un database semplice, abbiamo due soli tipi di dati, gli interi e le stringhe, non ho neppure limitato la lunghezza dei valori, ma per mia fortuna, nvarchar occupa solo la lunghezza della stringa inserita nel campo + 10 byte o giù di li, quindi potevo anche mettere i campi stringa lunghi 3000 caratteri.

Possiamo creare il nostro database, che chiameremo UserDb in modo molto banale con un file Access oppure da SQL Management Studio in SQL Server e al suo interno creiamo la tabella. Anche SQLite mi pare abbia la possibilità di scaricare dei tools per generare i database.

Se volete cimentarvi nella creazione del database utilizzando degli script SQL potete andare a leggere:

Creare un Database SQL Server pronto per la Produzione

Ed i successivi articoli che vi spiegano come creare un intero database in SQL Server, eseguendo degli Script SQL, se poi volete usarli in Access o SQLite, gli script di creazione tabella non cambiano molto, e consultando gli articoli precedenti potreste indovinare che per eseguirli dovrete usare un ExecuteNonQuery da un Command.

Vi risparmierò la fatica includendo i 3 database già pronti nel progetto, se poi vi interessasse vedere come si crea un database SQLite, potrei scrivere un post per mostrarvelo, anche se, in realtà gli strumenti li avete tutti nei 3 post precedenti, infatti si tratta di creare una connection ed eseguire uno script con la CREATE TABLE usando ExecuteNonQuery.

Iniziamo a creare l’applicazione

Iniziamo a creare la soluzione che ospiterà i progetti della nostra applicazione. Abbiamo descritto più volte soluzioni e progetti, in questo caso però farò una lista di quello che va fatto perché usualmente ogni volta che iniziamo un progetto sarà necessario farlo, pertanto ve lo ricordo subito in modo che vi rassegnate a farlo.

usersdb_01_create_solution

Prima operazione, creiamo la Solution, da Visual Studio:

  • New Project
  • Selezioniamo il Template in Visual C#, Windows, Classic Desktop
  • Scegliamo WPF Application
  • Chiamiamo l’applicazione UsersDb
  • Il Solution Name proposto sarà identico e lo teniamo

Diamo OK e otterremo la nuova soluzione con al suo interno il progetto dell’applicazione già funzionante.

Sfortunatamente, volendo fare un applicazione come si fanno nel mondo reale, e non una semplice applicazione Demo, per prima cosa andiamo a Demolire quello che il progetto standard ci ha regalato.

Se non lo sapeste, vi dico subito che, una volta predisposto il progetto base come ci piace, se vogliamo possiamo salvare il Project Template in modo che il nostro prossimo progetto per una applicazione WPF ci venga generato già modificato. Se volete provarci, sul menu File di Visual Studio, trovate l’opzione Export Template che vi permette di creare un modello di Progetto o un modello per una classe se lo volete.

usersdb_01_export_template

Eccolo qui, è piuttosto comodo per creare strutture di progetto se lavoriamo spesso per clienti diversi facendo molte applicazioni con strutture simili.

Ma torniamo a noi: Per prima cosa per configurare un applicazione adatta al mondo reale, dobbiamo demolire:

usersdb_01_demolition

Ci sono alcune cose che vengono automaticamente aggiunte ad ogni applicazione WPF dal progetto standard che non sempre (o nel mio caso non mai) utilizzeremo, pertanto le eliminiamo ora e le torneremo ad aggiungere solo se ci servissero davvero, se posso dare un suggerimento, quando lavorate ad un progetto capiterà spesso di fare delle prove, non sempre possiamo avere le idee chiare prima di iniziare, ricordate di non lasciare in giro classi e oggetti non usati, perché quando riprenderete in mano un applicazione per fare degli aggiornamenti dopo anni (ma anche dopo settimane) non dovrete perdere tempo a ricordarvi a cosa serve una classe che poi non è usata.

Una volta effettuata la cancellazione degli oggetti marcati di rosso, facciamo doppio click sulla cartella Properties per fare aprire l’interfaccia relativa alla configurazione del progetto.

usersdb_01_application

La pagina Application è quella che ci dice cosa stiamo guardando e cosa produrrà il compilatore da questo progetto.

Assembly Name è il nome dell’oggetto prodotto, in questo caso, essendo una Windows Application, si tratta di un eseguibile, che si chiamerà UsersDb.exe

Nei progetti delle librerie, sarà prodotto un NomeAssembly.dll.

Lasciamo il nome dell’assembly come è perché ci va bene, l’applicazione si chiamerà UsersDb.exe

Modifichiamo invece il Default Namespace, che se ricordo bene ho spiegato almeno 40 milioni di volte ma che non mi stancherò di invitarvi a modificare prima di fare qualsiasi cosa per non dover rinominarlo in seguito in 300 diverse classi.

Io l’ho chiamato Dnw.Users, perché come da linee guida Microsoft è opportuno che le vostre classi siano tutte contenute in namespace che iniziano con un prefisso che identifica la vostra azienda (o voi come persona) può essere un nome di fantasia, ma eviterà che se decidete di creare una classe che esiste già nel Framework .Net (molto facile visto il loro numero) ci siano conflitti d’uso della stessa se userete anche quella standard.

Aggiungete un icona, è sempre bello che la vostra applicazione mostri un icona che la rappresenta o che vi rappresenta invece che l’icona standard di Visual Studio che è anche brutta.

A questo punto premiamo il tasto Assembly Information:

usersdb_01_assembly_info

In questa finestra sono visualizzate le informazioni che potete guardare in modo diretto dentro al file AssemblyInfo.cs nella cartella Properties.

using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
 
// General Information about an assembly is controlled through the following 
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Dnw.UsersDb")]
[assembly: AssemblyDescription("The Users db manager")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Dotnetwork.it")]
[assembly: AssemblyProduct("Dnw.UsersDb")]
[assembly: AssemblyCopyright("Copyright ©  2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
 

[assembly: ComVisible(false)]
 
[assembly: ThemeInfo(
	ResourceDictionaryLocation.None,
								
	ResourceDictionaryLocation.SourceAssembly 
)]
 
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Ve l’ho copiato senza i commenti in modo che possiate vederlo, contiene esclusivamente una serie di Attributes, che forniscono informazioni che saranno riflesse nelle property del file eseguibile dopo la compilazione.

Anche in questo caso, consiglio sempre di modificarlo subito, in modo tale da dare una parvenza di professionalità ai nostri progetti e far sapere al mondo chi li ha creati.

usersdb_01_signing

Seconda modifica che non è obbligatoria ma consiglio sempre di fare, firmate i vostri assembly, davvero non costa nulla e vi eviterà problemi se a qualcuno venisse voglia di metterli nella GAC.


Attenzione che questa firma non c’entra nulla con la firma digitale dei vostri eseguibili e delle vostre DLL, quella si fa dopo la compilazione utilizzando un certificato che deve essere acquistato da una Certification Authority qualificata (e non costa poco).

Adesso che abbiamo modificato la nostra soluzione, prima di tutto dobbiamo correggere App.xaml e App.xaml.cs e dare loro il Namespace Corretto.

usersdb_01_update_appxaml

Questo è l’App.xaml creato dalla mia soluzione. Per prima cosa cancelliamo il richiamo a MainWindow.xaml perché l’abbiamo cancellata, non è una cancellazione definitiva, la creeremo nuova fra poco, ma piloteremo l’apertura dell’applicazione da codice perciò quella riga di Xaml non ci serve, così come non ci serve il riferimento al namespace UsersDb che non esiste più e che inseriremo con il namespace corretto solo se in futuro ci dovesse servire. Conserviamo solo il segno di Maggiore che chiude il Tag Application.

Inoltre dobbiamo modificare il Namespace Standard mettendo quello che abbiamo impostato come Default Namespace.

Ecco perché è opportuno cambiarlo subito, perché il Default Namespace (più eventuale cartella) viene automaticamente applicato a tutte le nuove classi generate in un progetto, perciò se lo faceste dopo aver generato 10 classi dovreste poi andare a cambiare il namespace sia allo XAML che al codice in tutte le classi.

<Application x:Class="Dnw.Users.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Application.Resources>
         
    </Application.Resources>
</Application>

Questo è il nuovo App.xaml.

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
 
namespace Dnw.Users
{
	/// <summary>
	/// Interaction logic for App.xaml
	/// </summary>
	public partial class App : Application
	{
	}
}

Allo stesso modo modifichiamo il Namespace del codice collegato alla classe in App.xaml.cs altrimenti alla compilazione ci verrà indicato che abbiamo dimenticato un pezzo della classe.

Terminata la parte di demolizione, passiamo alla costruzione, pertanto per prima cosa andiamo a creare una cartella nella soluzione, dove porremo tutte le Window della nostra applicazione.

usersdb_01_windows_folder

Tasto destro sul Progetto UsersDb, selezioniamo Add New Folder dal menu contestuale e creiamo la nostra cartella per le finestre.

usersdb_01_windows_addmainwindow

Adesso che abbiamo la cartella, selezioniamola e con il tasto destro andiamo su Add > New Item aprendo la finestra qui sopra per posizionarci sulla cartella WPF nella tree a sinistra e selezionare Window (WPF) per poi rinominare la nuova classe UsersWindow.xaml.

Se vi chiedete perché non ho semplicemente spostato e rinominato MainWindow, il motivo è semplice, avrei dovuto andare a fare tutte le modifiche al namespace e ai riferimenti sia nello XAML che nel codice C# della vecchia classe, mentre così mi viene generata già perfettamente a posto:

<Window x:Class="Dnw.Users.Windows.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:local="clr-namespace:Dnw.Users.Windows"
        mc:Ignorable="d"
        Title="UsersWindow" Height="300" Width="300">
    <Grid>
        
    </Grid>
</Window>

Come vedete nella prima riga dello XAML, la mia UsersWindow è stata definita automaticamente nel Namespace Dnw.Users.Windows ed è stato aggiunto anche un elemento xmlns che lo richiama, al momento potrebbe non esserci, ma è qualcosa che ci tornerà utile quando la finestra diverrà un po’ più complessa di così, pertanto lo conserviamo. io faccio solo una modifica, è qualcosa di personale non è un pattern specifico, mi piace farlo perché amo la brevità e local non mi piace come definizione del namespace quindi cambierò la sesta riga in questo modo:

xmlns:wr="clr-namespace:Dnw.Users.Windows"

Se vi chiedete perché wr, semplicemente perché suona come Window Resources, ed in effetti è solitamente lo scopo per cui quel namespace sarà usato nell’applicazione. Ma non corriamo troppo. Stiamo cercando di fare una cosa per volta.

Se compilate l’applicazione vedrete che non ci saranno errori, ma se la eseguite sfortunatamente il debugger partirà, se guarderete sul Task Manager troverete UsersDb.exe, ma nessuna finestra apparirà a video.

Questo perché dobbiamo dire ad Application di lanciarla. Vediamo come.

Apriamo App.xaml.cs e all’interno della classe andiamo a scrivere il codice seguente:

using Dnw.Users.Winows;
using System.Security.Permissions;
using System.Windows;
using System.Windows.Threading;
 
namespace Dnw.Users
{
	public partial class App : Application
	{
 
		protected override void OnStartup(StartupEventArgs e)
		{
			this.ShutdownMode = System.Windows.ShutdownMode.OnMainWindowClose;
			base.OnStartup(e);
			OpenMainWindow();
		}
 
		private void OpenMainWindow()
		{
			UsersWindow win = new UsersWindow();
			MainWindow = win;
			MainWindow.Show();
		}
	}
}

OnStartup è il codice che viene richiamato dall’evento corrispondente eseguito dal sistema quando l’oggetto Application viene istanziato dal Main dell’applicazione.

La classe Application ha una property che si chiama MainWindow, a cui va assegnata la Classe che istanziamo affinché Application sappia qual’è quella che abbiamo definito come MainWindow, questo perché in WPF è possibile cambiarla se ci serve ed in WPF non c’è il concetto di MDI a cui siamo abituati nelle Windows Forms.

Dopo queste modifiche, abbiamo una applicazione funzionante.

usersdb_01_windows_works

E ci fermiamo qui, nella prossima puntata vedremo come creare la libreria Jolly e testare la classe dati che sarà usata da tutti i Tier della nostra mini applicazione.

Riepilogo

Ecco di cosa abbiamo parlato in questo articolo:

  • Cos’è una applicazione Multi Tier
  • Come creare una mini applicazione Multi Tier per gestire un database
  • Come è fatto il nostro database mono tabella
  • Come modificare il template standard di una applicazione WPF per costruire una applicazione non dimostrativa.
  • Come istanziare la Window dell’applicazione dal codice di App.xaml.cs. E ottenere una applicazione funzionante.

 

 

 

Potete scaricare il progetto di esempio dal link qui indicato:

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