Technology Experience
.NET World

Salvare in modo asincrono gli stati di un processo transazionale

Uao, che titolo altisonante che ho tirato fuori dal cilindro questo pomeriggio.
Vi illustro lo scenario.

Immaginatevi un piccola applicazione mobile (Pocket PC o Windows Mobile, non importa) composta essenzialmente da 5 form diverse: Form1, Form2, Form3, Form4 e, sorpresa, Form5. L’applicazione parte mostrando Form1. Su ciascuna form ci sono due pulsanti – Next e Previous – per poter navigare avanti ed indietro da una forma all’altra. Gli stati possibili all’interno dell’applicazione sono i seguenti, con le varie possibilità di navigazione…

Partenza=Form1, Indietro=Form1, Avanti=Form2
Partenza=Form2, Indietro=Form1, Avanti=Form3
Partenza=Form3, Indietro=Form2, Avanti=Form4
Partenza=Form4, Indietro=Form3, Avanti=Form5
Partenza=Form5, Indietro=Form4, Avanti=Form5

Vediamo un po’ di codice per capire di cosa sto parlando. Questa è la classe Form1.

1 public partial class Form1 : Form 2 { 3 TransactionManager manager = new TransactionManager(); 4 5 public Form1() 6 { 7 InitializeComponent(); 8 } 9 10 private void previousButton_Click(object sender, EventArgs e) 11 { 12 manager.CurrentForm = this; 13 manager.NextIndex = 1; 14 manager.MoveToTransaction(); 15 } 16 17 private void nextButton_Click(object sender, EventArgs e) 18 { 19 manager.CurrentForm = this; 20 manager.NextIndex = 2; 21 manager.MoveToTransaction(); 22 } 23 }

Faccio uso di quello che ho chiamato TransactionManager, ovvero una classe che ha la responsabilità di farmi navigare avanti o indietro in base alla “posizione” in cui mi trovo. Se clicco sul previousButton e mi trovo su Form1, rimango su Form1. Se clicco su nextButton e mi trovo su Form1, vado su Form2, e così via, secondo lo schema (chiamiamolo così) che ho indicato sopra. Vediamo il codice di TransactionManager per capire un po’ di cose.

1 class TransactionManager : ITransaction 2 { 3 Form _currentForm; 4 int _nextIndex; 5 6 public Form CurrentForm 7 { 8 get { return _currentForm; } 9 set { _currentForm = value; } 10 } 11 12 public int NextIndex 13 { 14 get { return _nextIndex; } 15 set { _nextIndex = value; } 16 } 17 18 public void MoveToTransaction() 19 { 20 PDAManager.ShowWaitCursor(true); 21 22 string formName = "FormsNavigator.Form" + (_nextIndex).ToString(); 23 Form newForm = (Form)Assembly.GetExecutingAssembly().CreateInstance(formName); 24 25 if (newForm != null) 26 { 27 newForm.Show(); 28 newForm.BringToFront(); 29 30 if (_currentForm.GetType() != typeof(Form1)) 31 { 32 _currentForm.Close(); 33 } 34 } 35 36 PDAManager.ShowWaitCursor(false); 37 } 38 }

Qua ci divertiamo.

La classe TransactionManager implementa l’interfaccia ITransactionManager, che prevede due proprietà pubbliche (CurrentForm e NextIndex) e l’implementazione di un metodo MoveToTransaction(). Quest’ultimo è fondamentale: visualizza la clessidra sullo schermo del palmare. Poi viene creata tramite Reflection l’istanza del nuovo form da visualizzare: mi spiego meglio. Supponiamo di essere su Form2 e supponiamo di cliccare sul pulsante Next: il flusso di esecuzione entra nel metodo MoveToTransaction() e viene calcolato il nome del nuovo form: nel nostro caso FormsNavigator.Form3 (linea 22 del codice qui sopra). Il nuovo form viene creato (linea 23) e mostrato sullo schermo. Se il vecchio form non è Form1, lo chiudo, altrimenti rimane lì dov’è: Form1 è l’entry-point di questa applicazione, se lo chiudessi chiuderei tutto, perciò lo evito. La clessidra viene tolta e l’utente si ritrova con il nuovo form davanti.

Aggiungiamo un po’ di asincronia

Il codice qui sopra è semplice: permette di navigare avanti ed indietro sulle 5 form dell’applicazione. Supponiamo adesso che il passaggio da una form all’altra implichi il salvataggio di una qualche informazione su database locale, magari un SQL Server Compact Edition. Nulla ci vieta di mettere TextBox, ComboBox o altri controlli su ciascuna delle 5 form. Va da sè che il passaggio da uno stato all’altro deve prima occuparsi del salvataggio di queste informazioni da qualche parte, magari – come dicevo prima – un database locale. Questo caso può anche essere relativamente veloce, ma se la mole di dati cresce, oppure se utilizzo un database remoto, il tempo di attesa potrebbe anche crescere. Se c’è una cosa che non mi piace, è far aspettare l’utente per una cosa di cui non gliene può fregare di meno. Mi piacerebbe che la navigazione fosse veloce, che l’utente possa viaggiare da Form1 a Form2 senza aspettare troppo, mentre qualcun’altro sta facendo il lavoro sporco senza bloccare la UI, e senza far aspettare nessuno. Ho risolto così.

Alla classe TransactionManager di prima ho aggiunto un metodo privato:

1 void saveTransactionDataAsync() 2 { 3 System.Threading.Thread.Sleep(5000); 4 string msg = string.Format("Ho finito di salvare i dati della tx {0}", _nextIndex.ToString()); 5 MessageBox.Show(msg); 6 }

L’implementazione per questo post non fa nulla di interessante: il thread corrente viene messo in pausa per 5 secondi, poi visualizzo una MessageBox all’utente che lo informa che l’operazione di salvataggio dati è stata terminata con successo. Ho poi modificato l’implementazione del metodo MoveToTransaction che abbiamo visto prima, inserendo alla linea 21 le seguenti due linee di codice:

1 Thread thread = new Thread(saveTransactionDataAsync); 2 thread.Start();

In pratica, le operazioni su database vengono effettuate in un thread separato, mentre quello principale chiude ed apre le form molto velocemente, senza che l’utente si accorga di quello che sta succedendo in modo più “nascosto”.

Ci sono molte altre cose da dire, ma penso che per dare l’idea e per fornire qualche spunto può essere più che sufficiente. I commenti sono ben accetti.

Technorati Tags:    

Send to Kindle

Igor Damiani

La sua passione per l'informatica nasce nella prima metà degli anni '80, quando suo padre acquistò un Texas Instruments TI-99. Da allora ha continuato a seguire l'evoluzione sia hardware che software avvenuta nel corso degli anni. E' un utente, un videogiocatore ed uno sviluppatore software a tempo pieno. Igor ha lavorato e lavora anche oggi con le più moderne tecnologie Microsoft per lo sviluppo di applicazioni: .NET Framework, XAML, Universal Windows Platform, su diverse piattaforme, tra cui spiccano Windows 10 piattaforme mobile. Numerose sono le app che Igor ha creato e pubblicato sul marketplace sotto il nome VivendoByte, suo personale marchio di fabbrica. Adora mantenere i contatti attraverso Twitter e soprattutto attraverso gli eventi delle community .NET.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.