giovedì 21 ottobre 2010

LG Optimus 7 prime (e anche seconde) impressioni

Oggi ho preso l’LG Optimus 7 e, appena aperta la scatola ho avuto subito la piacevolissima sorpresa di un telefono decisamente compatto, maneggevole e accattivante con la sua parte posteriore grigio antracite “righettata” (molto hi-tech).

Visti i proptotipi non mi sarei aspettato un telefono così compatto. Messo a confronto con il mio Acer E100 hanno pressappoco la stessa altezza (la differenza è minima) e la dimensione è ragionevolmente maggiore.

Insomma, ancora prima di accenderlo già mi piaceva.

Poi l’ho acceso e la situazione è migliorata. La configurazione del telefono è stata praticamente automatica e l’unica cosa che ho dovuto inserire è l’APN del mio gestore telefonico.

Per il resto solo gli username e le password è WP7 ha cominciato a sincronizzare tutto.

Unico problema incontrato (ma sarà dovuto alla mia ignoranza) è che ho inserito un liveid sbagliato (sempre mio ma non quello che utilizzo usualmente) e non c’è stato verso di rimuoverlo.

Per il resto un ottimo device e un meraviglioso sistema operativo. Domani conto di mettere in mano il telefono a mia moglie che sta alla tecnologia come io all’uncinetto. Se capisce come funziona siamo davanti alla rivoluzione del millennio A bocca aperta!!

mercoledì 20 ottobre 2010

Test dell’LG Optimus 7 su Hardware Upgrade

 

Tag di Technorati: ,,,

21/10/2010 : WP7 nei negozi

Domani 21/10/2010 saranno nei negozi i primi WP7 disponibili per l’acquisto.

La community DomusDotNet sarà presso il punto vendita Vodafone di Via del Corso 417, Roma (all’incrocio con Piazza San Lorenzo in Lucina) per farvi vedere le funzionalità del nuovo telefono e per rispondere alle vostre domande.

Immagine mappa

Venite a trovarci e vi divertirete.

Contestualmente al telefono verrà lanciata la nuova offerta per abbonamenti e ricaricabili di Vodafone che può essere sottoscritta con il telefono (http://windowsphone7.vodafone.it/).

domenica 17 ottobre 2010

Applicazione WP7 di DomusDotNet sul marketplace

Ieri sera è stata pubblicata, sul marketplace di WP7, l’hub di DomusDotNet:

image

Per scaricare l’applicazione utilizzate il seguente link:

wp7_152x50_red

sabato 16 ottobre 2010

WP7: Aprire i risultati di una ricerca

I task di WP7 sono decisamente utili.

Poter eseguire una ricerca sul web e mostrare i risultati all’utente è quanto mai semplice:

  1. SearchTask searchTask = new SearchTask();
  2. searchTask.SearchQuery = "query di ricerca";
  3. searchTask.Show();

In questo modo la nostra applicazione viene “abbandonata” (attenti al tombstoning) e viene aperta la pagina dei risultati di ricerca del telefono.

I tasks sono nel namespace Microsoft.Phone.Tasks.

 

venerdì 15 ottobre 2010

WP7: Eseguire il binding in un TextBox mentre l’utente scrive

Quando utilizziamo un controllo TextBox all’interno della nostra App per WP7, siamo abituati al fatto che il binding verso la sorgente dati avviene nel momento in cui il TextBox stesso perde il fuoco.

Per ovviare a questo e fare in modo che il binding venga eseguito ad ogni pressione di un carattere sulla tastiera da parte dell’utente possiamo ricorrere ad un TriggerAction.

Realizziamo una classe derivata da TriggerAction che ci permetterà di reagire al cambiamento del testo all’interno del TextBox e “forzare” il binding.

  1. public class TextBoxTextPropertyChangedAction : TriggerAction<TextBox>
  2. {
  3.     protected override void Invoke(object o)
  4.     {
  5.         var be = this.AssociatedObject.GetBindingExpression(TextBox.TextProperty );
  6.         if (be!=null) be.UpdateSource();
  7.     }
  8. }

In questo caso gestiamo l’override del metodo Invoke(), recuperiamo l’espressione di binding sull’oggetto associato al trigger (in questo caso un textbox perché la nostra classe deriva dalla TriggerAction<TextBox>) e, se l’espressione di binding esiste, forziamo l’update del binding stesso.

Per poter montare il trigger su un TextBox è sufficiente scrivere:

  1. <TextBox Text="{Binding proprieta, Mode=TwoWay,BindsDirectlyToSource=True}">
  2.     <custom:Interaction.Triggers>
  3.         <custom:EventTrigger EventName="TextChanged">
  4.             <action:TextBoxTextPropertyChangedAction />
  5.         </custom:EventTrigger>
  6.     </custom:Interaction.Triggers>
  7. </TextBox>

dove custom e action sono i namespace relativi, rispettivamente, a System.Windows.Interactivity e alla nostra classe TextBoxPropertyChangedAction:

  1. xmlns:action="clr-namespace:MioNameapsce.Actions"
  2. xmlns:custom="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

Nel momento in cui l’utente comincia a digitare, e ogni volta che il testo cambia, verrà richiamato l’invoke e il binding verrà di nuovo eseguito.

 

VB for Dummies: Gli Eventi Custom

Nel precedente post di questa serie abbiamo visto cosa sono gli eventi. In questo post vedremo, invece, una specie particolare di eventi denominati “eventi custom”.

Cosa sono

Gli eventi custom sono a tutti gli effetti degli eventi ma danno la libertà allo sviluppatore di gestire tutte le fasi di vita degli stessi, cioè permettono di gestire:

  1. Aggancio degli handler;
  2. Rimozione degli handler;
  3. Esecuzione dell’evento.

Negli eventi standard (come abbiamo visto anche nel precedente post della serie) la gestione della lista dei delegate agganciati all’evento (aggiunta di un nuovo delegate, rimozione di un delegarte e invocazione dell’evento) sono a carico del compilatore e non dobbiamo preoccuparci di nulla.

Come si definiscono

Definire un evento custom è relativamente semplice, basta utilizzare la parola chiave Custom:

  1. Public Custom Event CustomEvent As MyDelegate
  2.     AddHandler(ByVal value As MyDelegate)
  3.  
  4.     End AddHandler
  5.  
  6.     RemoveHandler(ByVal value As MyDelegate)
  7.  
  8.     End RemoveHandler
  9.  
  10.     RaiseEvent()
  11.  
  12.     End RaiseEvent
  13. End Event

I custom event possono essere definiti solo urilizzando un delegate, quindi, dobbiamo definire la firma del nostro evento custom creando un delegate pubblico.

Come possiamo vedere dal precedente codice, il corpo del nostro custom event è formato da tre distinte sezioni:

  • AddHandler : è la sezione che si occupa di gestire l’accodamento di un nuovo gestore di evento. Questa sezione viene richiamata ogni qual volta che, nel codice, ci sia un’istruzione AddHandler riferita all’evento;
  • RemoveHandler: è la sezione che si occupa di gestire lo scodamento di un gestore di evento. Questa sezione viene richiamata ogni volta che, nel codice, ci sia un’istruzione RemoveHandler;
  • RaiseEvent: è la sezione che si occupa dell’invocazione dell’evento. Questa sezione viene richiamata ogni volta che, nel codice, viene eseguita un’istruzione RaiseEvent relativa all’evento.

Quando utilizzarli

Considerato il fatto che dobbiamo gestire noi la lista dei gestori di evento agganciati al nostro evento (lo possiamo fare utilizzando un MultiCastDelegate) e preoccuparci di invocare ognuno di questi nel momento in cui facciamo RaiseEvent, gli eventi custom sono da utilizzare solo quando veramente non possiamo farne a meno e, in particolare, quando vogliamo avere pieno controllo su tutte le fasi degli stessi.

Un esempio pratico

Per dare un esempio di come implementare un custom event supponiamo di voler relizzare una classe che disponga di un evento la cui modalità di invocazione sia costruita in modo da lanciare i gestori di evento associati in parallelo.

Normalmente, gli eventi standard eseguono i gestori di evento in maniera sequenziale a partire dal primo che è stato aggiunto tramite la parola chiave AddHandler.

Cominciamo definendo il delegate che rappresenta la firma del nostro evento:

  1. Public Delegate Sub MioEventoDelegate(ByVal str As String, ByVal datetime As DateTime)

Il delegate non ha, ovviamente, traccia del fatto che verrà utilizzato in un evento custom o in uno standard (e per far vedere questo la nostra classe conterrà anche un evento standard).

A questo punto definiamo l’evento custom con le strutture dati necessarie per gestire la lista dei gestori di evento:

  1. Private _EventoParalleloStore As MioEventoDelegate
  2.  
  3. Public Custom Event EventoParallelo As MioEventoDelegate
  4.     AddHandler(ByVal value As MioEventoDelegate)
  5.         If value Is Nothing Then Throw New ArgumentNullException()
  6.         Me._EventoParalleloStore = DirectCast([Delegate].Combine(Me._EventoParalleloStore, value), MioEventoDelegate)
  7.     End AddHandler
  8.  
  9.     RemoveHandler(ByVal value As MioEventoDelegate)
  10.         If value Is Nothing Then Throw New ArgumentNullException()
  11.         Me._EventoParalleloStore = DirectCast([Delegate].Remove(Me._EventoParalleloStore, value), MioEventoDelegate)
  12.     End RemoveHandler
  13.  
  14.     RaiseEvent(ByVal str As String, ByVal datetime As DateTime)
  15.         If _EventoParalleloStore IsNot Nothing Then
  16.             For Each e In _EventoParalleloStore.GetInvocationList()
  17.                 Dim thread = New Threading.Thread(AddressOf RaiseEventoParalleloThread)
  18.                 thread.Start(New ThreadParameter() With {.Delegate = e,
  19.                                                          .DateTime = datetime,
  20.                                                          .Str = str})
  21.             Next
  22.         End If
  23.     End RaiseEvent
  24. End Event


L’attributo privato _EventoParalleloStore si occupa di memorizzare la lista di gestori di evento associati all’evento. Di fatto si tratta di un MultiCastDelegate.

L’aggiunta di un gestore di evento (AddHandler) avviene utilizzando la funzione Combine tra _EventoParalleloStore e il delegate passato per argomento (value).

In maniera analoga utilizziamo il Remove della classe Delegate per rimuovere il gestore di evento.

La parte più interessante, è, infine, quella che si occupa di eseguire, in maniera parallela, i gestori di evento (RaiseEvent). In questo caso eseguiamo un ciclo su tutti i gestori presenti nella lista e, per ognuno di questi, lanciamo un thread che esegue la seguente funzione:

  1. Private Sub RaiseEventoParalleloThread(ByVal obj As Object)
  2.     Dim param = CType(obj, ThreadParameter)
  3.     param.Delegate.DynamicInvoke(param.Str, param.DateTime)
  4. End Sub

Per eseguire il gestore di evento (che lo ricordiamo è, alla fine dei conti, un delegate) è sufficiente richiamare il metodo DynamicInvoke.

Il risultato definitivo è che, quando nel codice della classe scriviamo:

  1. RaiseEvent EventoParallelo(str, DateTime.Now)

otteniamo che i gestori dell’avento eventualmente associati vengono richiamati su thread differenti in maniera parallela.

In allegato potete trovare la solution completa per Visual Studio 2010. In questa troverete una modifica relativa alla parte di invocazione dei gestori di evento in cui si utilizzano i Task.



mercoledì 13 ottobre 2010

Silverlight Firestarter: evento on-line il 20 Dicembre 2010

Vi comunico che il 2 dicembre 2010 sarà possibile seguire on-line ( a meno che n on vogliate andare a Redmond) l’evento :

Global Silverlight Firestarter

Si tratta di un evento che è più di un evento e, a mio modo di vedere, vale la pena di seguirlo.

Qualsiasi link del marketplace per developers vi riporta alla home e non riuscite a pubblicare applicazioni?

Mi sono scontrato con il problema di ritrovarmi loggato all’interno del marketplace ma di non riuscire ad entrare all’interno dell’area del dashboard. In realtà, qualsiasi link cliccassi, mi ritrovavo nella home.

A questo punto è venuto in aiuto il forum e ho scoperto che potrebbe essere il caso di andare sul sito www.xbox.com, loggarsi con il live id del marketplace, aprire il profilo e accettare il disclaimer che viene proposto.

Fatto questo, tutto a cominciato a funzionare.

Sarà un caso, ma se qualcuno ha il mio stesso problema può provare. Al massimo ha perso altri 5 minuti.

 

Tag di Technorati: ,,,

Meno di un mese al Community Tour

Manca meno di un mese alla tappa romana del community tour che si terrà il 12 Novembre prossimo e già la tensione comincia a salire. Tante sono le persone che si sono iscritte all’evento e, daltronde, non potrebbe essere diversamente considerato che ci sarà un evangelist di Microsoft (il mitico Lorenzo Barbieri) e due MVP (Alessandro Del Sole e Renato Marzaro). Verranno trattati tutti gli argomenti novità del momento da Windows Phone 7 (annunciato ufficialmente lunedì scorso) a LightSwitch passando per Azure, IE9 e MVC3.

300x250

Non potete perdere l’occasione di aggiornarvi sulle ultime novità a gratise!!!!!

Per maggiori info potete andare all’indirizzo http://www.domusdotnet.org/eventi/microsoft-community-tour-2010.aspx in cui trovate location, agenda e link per l’iscrizione.

Really: New Windows Phone Ad

lunedì 11 ottobre 2010

I Tasks del .NET Framework 4.0

In questo post vorrei porre l’attenzione su alcune classi, introdotte nel Framework 4.0, che semplificano la scrittura di codice multithread.

Si tratta delle classi per la gestione dei task cioè di operazioni asincrone su thread separati.

Queste classi fanno parte della Task Parallel Library (TPL) introdotta con il Framework 4.0 e si trovano all’interno del namespace è System.Threading.Task e sono, in dettaglio, le seguenti:

  • Task : Rappresenta un'operazione asincrona.
  • Task(Of TResult) : Rappresenta un'operazione asincrona in grado di restituire un valore.
  • TaskCanceledException : Rappresenta un'eccezione utilizzata per comunicare l'annullamento di un'attività.
  • TaskCompletionSource(Of TResult) : Rappresenta il lato producer di un oggetto Task(Of TResult) non associato a un delegato e che consente l'accesso al lato consumer tramite la proprietà Task.
  • TaskExtensions : Fornisce un set di metodi statici (Shared in Visual Basic) per l'utilizzo di tipi specifici di istanze di Task.
  • TaskFactory : Fornisce supporto per la creazione e la pianificazione di oggetti Task.
  • TaskFactory(Of TResult) : Fornisce supporto per la creazione e la pianificazione di oggetti Task(Of TResult).
  • TaskScheduler : Rappresenta un oggetto che gestisce le operazioni di basso livello relative all'accodamento delle attività nei thread.
  • TaskSchedulerException : Rappresenta un'eccezione utilizzata per comunicare un'operazione non valida eseguita da un oggetto TaskScheduler.
  • UnobservedTaskExceptionEventArgs : Fornisce i dati dell'evento generato quando l'eccezione di un oggetto Task in cui si è verificato un errore non viene osservata.

Classi Task e Task(Of Result)

Entrambe le classi permettono la creazione e gestione di attività multithread asincrone. La classe Task è utilizzata per attività che non hanno la necessità di restituire un valore mentre la Task(Of Result) può essere utilizzata quando dobbiamo eseguire attività asincrone al cui termine abbiamo la necessità di un valore di ritorno. La Task(Of Result) deriva dalla Task.

Le due classi mettono a disposizione una serie di costruttori che ci permettono poter eseguire task in tantissime modalità differenti.

Cominciamo a vedere qualche esempio. Negli esempi riportati di seguito alterneremo la classe Task e la classe Task(Of Result) in modo da far vedere le funzionalità di tutte e due.

Primo esempio molto semplice prevede l’esecuzione di una Sub denominata LongRunJob e l’attesa da parte del programma principale:

  1. Private Sub SimpleTask()
  2.     Dim task = New Threading.Tasks.Task(New Action(AddressOf LongRunJob))
  3.     Console.WriteLine("Start LongRunJob")
  4.     task.Start()
  5.     task.Wait()
  6.     Console.WriteLine("End LongRunJob")
  7.     Console.ReadLine()
  8. End Sub
  9.  
  10. Private Sub LongRunJob()
  11.     Console.WriteLine("{0:HH.mm.ss} - LongRunJob Iniziato", DateTime.Now)
  12.     Threading.Thread.Sleep(10000)
  13.     Console.WriteLine("{0:HH.mm.ss} - LongRunJob Completato", DateTime.Now)
  14. End Sub

In questo esempio è creato un oggetto di classe Task a cui è passato il delegate del codice da eseguire (LongRunJob), è eseguito il task (cioè il codice all’interno della sub LongRunJob) e atteso il completamento dello stesso. Il metodo Start della classe Task (e della classe Task(Of Result)) prevede due possibili firme: la prima è quella dell’esempio precedente mentre la seconda permette la gestione di task schedulati.

Nell’esempio precedente abbiamo atteso il completamento del task incondizionatamente ma possiamo, se serve, attendere per un determinato timeout. Nel successivo esempio vediamo come utilizzare un task che restituisce un valore e come attendere il completamento per un determinato timeout:

  1. Private Sub ResultTask()
  2.     Dim sleepTime = New Random(DateTime.Now.Millisecond).Next(10000)
  3.     Dim task = New Threading.Tasks.Task(Of DateTime)(Function()
  4.                                                          Return LongRunJob(sleepTime)
  5.                                                      End Function)
  6.     Console.WriteLine("Start LongRunJob")
  7.     task.Start()
  8.     If Not task.Wait(5000) Then
  9.         Console.WriteLine("Timeout scattato")
  10.     Else
  11.         Dim result = task.Result
  12.         Console.WriteLine("End LongRunJob- {0:HH.mm.ss}", result)
  13.     End If
  14.  
  15.     Console.ReadLine()
  16. End Sub
  17.  
  18. Private Function LongRunJob(ByVal sleepTime As Integer) As DateTime
  19.     Console.WriteLine("{0:HH.mm.ss} - LongRunJob Iniziato", DateTime.Now)
  20.     Threading.Thread.Sleep(sleepTime)
  21.     Console.WriteLine("{0:HH.mm.ss} - LongRunJob Completato", DateTime.Now)
  22.     Return DateTime.Now
  23. End Function

In questo esempio è avviato un task composto da una funzione che accetta un intero (numero di millisecondi di attesa) e restituisce una data (data di completamento). Questa volta il costruttore del task utilizza una espressione lambda per definire cosa fa il task stesso. Per attendere il completamento del task, viene utilizzata la firma del metodo Wait() che prevede un numero di millisecondi di attesa. Scaduto il timeout, il metodo Wait() restituisce false. Nell’esempio precedente, se non scade il timeout, viene recuperato il valore di ritorno attraverso la proprietà Result.

Le due classi Task prevedono alcune proprietà utili:

  • AsyncState : Ottiene l'oggetto stato fornito quando è stato creato Task oppure null se non ne è stato fornito alcuno. L’oggetto AsyncState ha la stessa funzionalità del corrispondente per i thread classici;
  • CreationOptions : Ottiene l'oggetto TaskCreationOptions utilizzato per creare questa attività. L’utilizzo di tale proprietà verrà spiegato in un esempio successivo;
  • CurrentId : Restituisce l'ID univoco dell'oggetto Task attualmente in esecuzione;
  • Exception : Ottiene l'oggetto AggregateException che ha causato l'interruzione anomala dell'oggetto Task.Se l'oggetto Task è stato completato correttamente o non ha ancora generato alcuna eccezione, verrà restituito Nothing;
  • Factory : Fornisce l'accesso ai metodi factory per la creazione di istanze di Task e Task(Of TResult);
  • Id : Ottiene un ID univoco per questa istanza di Task;
  • IsCanceled : Ottiene un valore che indica se l'esecuzione di questa istanza di Task è stata completata poiché è stata annullata;
  • IsCompleted : Ottiene un valore che indica se questo oggetto Task è stato completato;
  • IsFaulted : Ottiene un valore che indica se l'oggetto Task è stato completato a causa di un'eccezione non gestita;
  • Status : Ottiene l'oggetto TaskStatus di questa attività.

Come possiamo osservare, ogni Task dispone di un ID univoco assegnato dal sistema che ci permette di caratterizzarlo e riconoscerlo.

Nel precedente esempio abbiamo visto come attendere il completamento di un task per un certo timeout. Osserviamo che, nel momento in cui il Wait() ritorna con risultato False (cioè il timeout è scaduto) il task non viene automaticamente interrotto e continua la sua opera. Se abbiamo la necessità di interromperlo, dobbiamo ricorrere ad un CancellationToken nel seguente modo:

  1. Private Sub ResultTaskAborted()
  2.     Dim sleepTime = New Random(DateTime.Now.Millisecond).Next(10000)
  3.     Dim tokenSource = New CancellationTokenSource()
  4.     Dim cancToken = tokenSource.Token
  5.  
  6.     Dim task = New Threading.Tasks.Task(Of DateTime)(Function()
  7.                                                          Return LongRunJob(sleepTime, cancToken)
  8.                                                      End Function, cancToken)
  9.     Console.WriteLine("Start LongRunJob")
  10.     task.Start()
  11.     If Not task.Wait(5000) Then
  12.         tokenSource.Cancel()
  13.         Console.WriteLine("Timeout scattato")
  14.     Else
  15.         Dim result = task.Result
  16.         Console.WriteLine("End LongRunJob- {0:HH.mm.ss}", result)
  17.     End If
  18.     Console.ReadLine()
  19. End Sub
  20.  
  21. Private Function LongRunJob(ByVal sleepTime As Integer, ByVal token As CancellationToken) As DateTime
  22.     Dim startTime = DateTime.Now
  23.     Console.WriteLine("{0:HH.mm.ss} - LongRunJob Iniziato", startTime)
  24.     While (DateTime.Now.Subtract(startTime).TotalMilliseconds <= sleepTime)
  25.         If token.IsCancellationRequested Then
  26.             Return DateTime.MinValue
  27.         End If
  28.         Threading.Thread.Sleep(25)
  29.     End While
  30.     Console.WriteLine("{0:HH.mm.ss} - LongRunJob Completato", DateTime.Now)
  31.     Return DateTime.Now
  32. End Function

Il token di annullamento è generato utilizzando una “sorgente di token” (classe CancellationSourceToken) la quale ci fornisce anche il metodo per richiedere la cancellazione (metodo Cancel()). Il codice del nostro task controlla l’eventuale richiesta di annullamento (IsCancellationRequest=true) e a seguito di questo chiude il proprio lavoro.

Se il nostro task vuole comunicare al mondo esterno che sta chiudendo in maniera anomala, si può utilizzare il metodo ThrowIfCancellationRequested() che è equivalente a generare un eccezione di tipo OperationCanceledException nei thread che sono in waiting sul task.

Il token di annullamento può essere utilizzato anche in altre tipologie di oggetti quali Thread o BackgroundWorker (per questo motivo si trova all’interno di System.Thread e non di System.Thread.Tasks).

Nel costruttore della classe Task (o Task(Of Result)) possiamo utilizzare anche l’enumerazione TaskCreationOptions che ci consente di definire il comportamento delle nostre attività. I valori possibili per l’enumerazione TaskCreationOptions sono i seguenti:

  • None : Specifica che deve essere utilizzato il comportamento predefinito;
  • PreferFairness : Indicazione fornita a un oggetto TaskScheduler affinché pianifichi un'attività nel modo più giusto possibile, ovvero garantendo che le attività pianificate prima abbiano più possibilità di essere eseguite prima delle attività pianificate in un secondo momento;
  • LongRunning : Specifica che un'attività sarà un'operazione a bassa precisione di lunga durata. Fornisce a TaskScheduler un'indicazione in merito alla possibilità di dover ricorrere all'oversubscription;
  • AttachedToParent : Specifica che un'attività è connessa a un elemento padre nella gerarchia delle attività.

Se vogliamo,quindi, far eseguire il nostro task LongRunJob come un task a lunga durata, ci basta scrivere:

  1. Dim task = New Threading.Tasks.Task(New Action(AddressOf LongRunJob), Threading.Tasks.TaskCreationOptions.LongRunning)

L’enumerazione TaskCreationOptions è un enumerazione decorate con l’attributo Flag() e, quindi, è una enumerazione il cui valore può essere anche una combinazione dei valori indicate nella precedente tabella.

Classi TaskFactory e TaskFactory(Of Result)

Le classi TaskFactory e TaskFactory(Of Result) forniscono dei metodi che permettono la creazione dei task:

  1. Private Sub SimpleFactoryTask()
  2.     Console.WriteLine("Start LongRunJob")
  3.     Dim factory = New Threading.Tasks.TaskFactory
  4.     Dim task = factory.StartNew(New Action(AddressOf LongRunJob))
  5.     task.Wait()
  6.     Console.WriteLine("End LongRunJob")
  7.     Console.ReadLine()
  8. End Sub

Nel precedente esempio, tramite la classe TaskFactory, creiamo e avviamo il nostro task.

Classe TaskScheduler

Una delle funzionalità più interessanti dei tasks di .NET Framework 4.0 è la possibilità di realizzare in maniera molto semplice uno scheduler delle attività gestito quasi completamente dal framework. Per fare questo ci viene in aiuto la classe TaskScheduler che rappresenta la classe base su cui lavorare per creare il nostro gestore di attività.

Derivando la classe TaskScheduler e implementando opportunamente i metodi della stessa possiamo realizzare il nostro gestore delle attività. Un esempio di implementazione è riportato all’indirizzo http://msdn.microsoft.com/it-it/library/ee789351.aspx in cui viene mostrato come realizzare uno scheduler che limita il grado di concorrenza sulle attività.

giovedì 7 ottobre 2010

Mapping dinamico con LINQ to SQL

In questo post vi vorrei mostrare un modo per poter cambiare il mapping di LINQ to SQL in modo da puntare a tabelle differenti ma referenziate allo stesso modo all’interno di un datacontext LINQ to SQL.

Immaginiamo di avere un semplicissimo database (che chiameremo Archivio) con due tabelle assolutamente uguali in struttura:

imageCreiamo la nostra applicazione di prova (una semplice windows form) e aggiungiamo un modello LINQ 2 SQL (file .dbml).

A questo punto trasciniamo (prendendola dalla finestra Server Explorer) una delle due tabelle (ad esempio la Principale) sul nostro design e cambiamo il nome all’entità generata dal designer chiamandola Tabellaimage

Per poter cambiare al volo il mapping del datacontext è necessario ricavare l’xml che descrive il mapping stesso.

Per fare questo possiamo ricorrere all’utility SQLMetal.exe (la stessa che Visual Studio utilizza per generare il dbml) da riga di comando.

Nel nostro caso eseguiamo il seguente comando:

sqlmetal.exe /map:Archivio.map ArchivioDataClasses.dbml /code:Archivio.vb /language:VB

Lo switch /map permette di generare in maniera separata il file di mapping e il file della classe corrispondente:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Database Name="Archivio" xmlns="http://schemas.microsoft.com/linqtosql/mapping/2007">
  3.   <Table Name="dbo.Principale" Member="Tabellas">
  4.     <Type Name="Tabella">
  5.       <Column Name="Id" Member="Id" Storage="_Id" DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" AutoSync="OnInsert" />
  6.       <Column Name="Descrizione" Member="Descrizione" Storage="_Descrizione" DbType="NVarChar(100) NOT NULL" CanBeNull="false" />
  7.     </Type>
  8.   </Table>
  9.   <Function Name="dbo.GetAllTables" Method="GetAllTables">
  10.     <ElementType Name="GetAllTablesResult">
  11.       <Column Name="NomeTabella" Member="NomeTabella" Storage="_NomeTabella" DbType="VarChar(14) NOT NULL" CanBeNull="false" />
  12.       <Column Name="DescrizioneTabella" Member="DescrizioneTabella" Storage="_DescrizioneTabella" DbType="VarChar(18) NOT NULL" CanBeNull="false" />
  13.     </ElementType>
  14.   </Function>
  15. </Database>

Se apriamo il file VB generato vedremo la presenza delle stesse classi generate dal designer di LINQ 2 SQL ma depurate degli attributi di mapping.

Il nodo <Table>, del precedente XML, riporta il mapping relativo alla tabella Principale. Nel file è presente anche una Stored Procedure che ci servirà per recuperare l’elenco delle tabelle su cui eseguire il mapping dinamico.

Il trucco per poter cambiare il mapping dinamicamente passa proprio da questo xml.

E’ possibile, infatti, creare il datacontext per l’accesso alla banca dati utilizzando il costruttore prevede come argomenti la stringa di connessione e un oggetto di tipo MappingSource. In particolare possiamo utilizzare un XmlMappingSource creato con l’XML di cui sopra opportunamente modificato. Normalmente quando istanziamo il datacontext senza modificare il mapping ma utilizzando quello generato dal designer, lo stesso datacontext utilizza come tipologia di mapping la classe AttributeMappingSource.

Per esemplificare il discorso, creiamo nella nostra applicazione una classe composta di metodi  e proprietà statiche (una classe utility) che ci consente di creare il mapping XML al “volo”.

  1. Public NotInheritable Class ConfigurationData
  2.     Private Sub New()
  3.  
  4.     End Sub
  5.  
  6.     Public Shared ReadOnly Property ConnectionString As String
  7.         Get
  8.             Dim connString = Configuration.ConfigurationManager.ConnectionStrings("LINQ2SQLDynamicMapping.My.MySettings.ArchivioConnectionString")
  9.             If connString Is Nothing Then
  10.                 Return Nothing
  11.             Else
  12.                 Return connString.ConnectionString
  13.             End If
  14.         End Get
  15.     End Property
  16.  
  17.     Public Shared Function GetMapData(ByVal nomeTabella As String) As String
  18.         Dim xml = <?xml version="1.0" encoding="utf-8"?>
  19.                   <Database Name="Archivio" xmlns="http://schemas.microsoft.com/linqtosql/mapping/2007">
  20.                       <Table Name=<%= nomeTabella %> Member="Tabellas">
  21.                           <Type Name="Tabella">
  22.                               <Column Name="Id" Member="Id" Storage="_Id" DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" AutoSync="OnInsert"/>
  23.                               <Column Name="Descrizione" Member="Descrizione" Storage="_Descrizione" DbType="NVarChar(100) NOT NULL" CanBeNull="false"/>
  24.                           </Type>
  25.                       </Table>
  26.                   </Database>
  27.         Return Xml.ToString()
  28.     End Function
  29.  
  30. End Class

La proprietà ConnectionString è stata creata per incapsulare la lettura del file di configurazione (ci servirà nel costruttore del datacontext) mentre il metodo GetMapData() genera la stringa XML necessaria al mapping (depurata della stored procedure che non ci serve per l’accesso alla tabelle di archivio).

In questo modo possiamo cambiare il mapping utilizzando il seguente pezzo di codice:

  1. Dim connectionString = ConfigurationData.ConnectionString
  2. Dim mappingSource = XmlMappingSource.FromXml(ConfigurationData.GetMapData(nomeTabella))
  3. Using dc As New ArchivioDataContext(connectionString, mappingSource)
  4.     ' Accesso ai dati con il nuovo mapping
  5.     '
  6. End Using

L’applicazione demo riportata in allegato prevede una combo riempita con i dati recuperati dalla stored procedure di cui sopra (e sta a simulare il fatto che tali tabelle potrebbero cambiare nel tempo e che i loro nomi potrebbero arrivare da una fonte esterna), un tasto per recuperare effettivamente i dati e una griglia per la visualizzazione:

imageQuando si preme il pulsante, semplicemente si esegue il codice:

  1. Private Sub btnCaricaDati_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCaricaDati.Click
  2.     Dim nomeTabella = CStr(Me.cmbTabella.SelectedValue)
  3.     Dim connectionString = ConfigurationData.ConnectionString
  4.     Dim mappingSource = XmlMappingSource.FromXml(ConfigurationData.GetMapData(nomeTabella))
  5.     Using dc As New ArchivioDataContext(connectionString, mappingSource)
  6.         Me.dgvDati.AutoGenerateColumns = True
  7.         Me.dgvDati.DataSource = dc.Tabellas
  8.     End Using
  9. End Sub

La classe XMLMappingSource mette a disposizione anche dei metodi per creare il mapping a partire da un URL (che punta all’XML), da uno stream (per esempio leggendolo da un file) oppure da un lettore XML.

Tutto ciò, unito anche al fatto che, potenzialmente, possiamo modificare l’XML a nostro piacimento (aggiungendo colonne, modificando tipi e così via) fa si che si possano implementare scenari decisamente interessanti di accesso ai dati.

Ultima osservazione importante è che se utilizziamo sempre il mapping dinamico e la classe generata dall’SQLMetal, togliendo dal progetto il dbml, otteniamo che le classi che rappresentano i record della tabella sono di tipo POCO, ovvero non hanno nessun riferimento alla sorgente dati.

martedì 5 ottobre 2010

Windows Phone 7 Lab: un peccato non esserci

Eccomi di ritorno dal lab di Windows Phone 7 tenuto qui al Roma, presso la sede Microsoft, da Lorenzo Barbieri.

Come al solito ci si presenta con delle applicazioni da provare, pensando che tutto vada in maniera perfetta e, come al solito, ci si ritrova a correggere tante cose nel momento in cui le si prova su un device fisico.

A chi non è venuto dico che ha perso una grande opportunità: sviluppare con l’emulatore va benissimo, ma testare le gesture, l’accelerometro, il GPS o, semplicemente, il layout della nostra applicazione su un device fisico è tutta un’altra cosa!!!

 

Tag di Technorati: ,,

lunedì 4 ottobre 2010

VB.NET for Dummies: Gli Eventi

Vorrei iniziare con questo post una serie dedicata ad aspetti di VB.NET di base che possono essere utile a coloro che si avvicinano al mondo .NET e che, in genere, non vengono trattati a livello base.

La serie di post non ha la pretesa di essere assolutamente esaustivi sugli argomenti che tratterò

In questo primo post parleremo degli eventi.

Cosa sono e a cosa servono

Un evento è la “notifica” dell’accadimento di qualcosa.

Quando, ad esempio, premiamo un bottone della finestra di un’applicazione, dietro le quinte, il bottone stesso “notifica” al mondo circostante che qualcuno, in quell’istante, lo ha premuto.

Sta, poi, al mondo circostante preoccuparsi di “intercettare” l’evento stesso per gestirlo (tramite un gestore di evento).

Attenzione a non confondere la pressione del tasto con la “notifica” della pressione del tasto: l’evento è la “notifica” dell’accadimento, non l’accadimento stesso.

Ma a cosa serve tutto ciò?

Utilizzare gli eventi è un modo per disaccoppiare due o più attori del nostro dominio.

Sempre tornando al bottone di cui sopra, infatti, lo stesso bottone non è assolutamente a conoscenza di chi gestirà la sua “notifica”. Semplicemente comunica tale notifica. Questo ci permette di far gestire l’evento del bottone da qualsiasi classe senza minimamente modificare il bottone stesso.

Tanto per fare un’altro esempio, immaginate una newsletter: l’invio della newsletter è un evento, se voi la leggete avete gestito l’evento!

Come si definisce un evento

Un evento si definisce utilizzando la seguente sintassi:

  1. Public Event <NomeEvento>(<parameterList>)

dove <NomeEvento> è il nome che vogliamo assegnare all’evento (ad esempio Click per la pressione di un Button) e <parameterList> è la lista di argomenti che vogliamo trasferire a chi si è sottoscritto per ricevere la “notifica”.

Lo standard utilizzato all’interno del framework è il seguente:

  1. Public Event <NomeEvento>(sender as object, e as MyArgs)

dove

  • sender è l’istanza della classe (oggetto) che ha sollevato l’evento;
  • MyArgs è una classe che deriva da System.EventArgs (o da sue derivate) che contiene l’argomento dell’evento e che ci serve per trasportare oggetti all’indirizzo di chi deve gestire l’evento.

Il sender tra gli argomenti permette, a chi si sottoscrive, di poter gestire gli eventi di più oggetti con lo stesso tipo di evento in un unico punto (gestore).

Per esemplificare ulteriormente cominciamo con il realizzare una classe che si occuperà di simulare un lavoro di lunga durata:

  1. Public Class LongRunJob
  2.  
  3.     Public Event JobCompleted(ByVal sender As Object, ByVal e As EventArgs)
  4.  
  5.     Public Sub DoWork(ByVal nSec As Integer)
  6.         .
  7.         .
  8.     End Sub
  9.     
  10. End Class

Abbiamo definito un evento che segnalerà il completamento del lavoro. Il metodo DoWork, come vedremo più avanti, si occuperà di avviare un thread che, banalmente, dopo nSec secondi segnalerà l’avvenuto completamento.

Come si solleva un evento

Per poter scatenare un evento è sufficiente eseguire l’istruzione:

  1. RaiseEvent <NomeEvento>(<ParameterList>)

In particolare, per sollevare l’evento JobCompleted() della classe LongRunJob, possiamo scrivere:

  1. RaiseEvent JobCompleted(Me, EventArgs.Empty)

Completiamo, quindi, la nostra classe LongRunJob con il thread che esegue il lavoro:

  1. Public Class LongRunJob
  2.  
  3.     Public Event JobCompleted(ByVal sender As Object, ByVal e As EventArgs)
  4.  
  5.     Public Sub DoWork(ByVal nSec As Integer)
  6.         Dim threadStart = New Threading.ParameterizedThreadStart(AddressOf DoWorkCode)
  7.         Dim thread = New Threading.Thread(threadStart)
  8.         thread.Start(nSec)
  9.     End Sub
  10.  
  11.     Private Sub DoWorkCode(ByVal data As Object)
  12.         Dim nSec = CType(data, Int32)
  13.         System.Threading.Thread.Sleep(nSec * 1000)
  14.         RaiseEvent JobCompleted(Me, EventArgs.Empty)
  15.     End Sub
  16.  
  17. End Class

Come si gestisce un evento

La nostra classe comunica, quindi, al mondo circostante di aver completato il proprio lavoro, ma come facciamo ad intercettare l’evento in un’altra classe?

Per poterci “sottoscrivere” alla ricezione di un evento utiliziamo la parola chiave AddHandler:

  1. AddHandler calc.JobCompleted, AddressOf LongRunJobCompletedHandler

In questo modo comunichiamo all’istanza della classe LongRunJob (vedremo più avanti in che modo) quale metodo deve essere richiamato nel momento in cui viene sollevato l’evento. E’ la classe che vuole essere informata della notifica che ha il dovere di sottoscriversi.

Il metodo che gestirà l’evento deve avere la stessa firma dell’evento stesso (Stessi parametri per tipo, ordine e numero).

Nel momento in cui non abbianmo più necessità di ricevere le notifiche dell’evento, possiamo “sganciare” il gestore di evento con la parola chiave RemoveHandler:

  1. RemoveHandler calc.JobCompleted, AddressOf LongRunJobCompletedHandler

Un esempio di gestione dell’evento è il seguente:

  1. Module Module1
  2.  
  3.     Sub Main()
  4.         Dim calc = New LongRunJob()
  5.         AddHandler calc.JobCompleted, AddressOf LongRunJobCompletedHandler
  6.         Console.WriteLine("Inizio lavoro!")
  7.         calc.DoWork(10)
  8.         Console.ReadLine()
  9.         RemoveHandler calc.JobCompleted, AddressOf LongRunJobCompletedHandler
  10.     End Sub
  11.  
  12.     Public Sub LongRunJobCompletedHandler(ByVal sender As Object, ByVal e As EventArgs)
  13.         Console.WriteLine("Lavoro completato!")
  14.     End Sub
  15.  
  16. End Module

Cosa c’è dietro le quinte

Perchè in C#, per implementare un evento, si deve definire un delegate, definire l’evento come membro della classe di tipo delegate e via dicendo mentre VB, essendo comunque un linguaggio del framework, non ha la necessità di scrivere tutto ciò?

La risposta è che il compilatore VB semplifica la vita e crea per noi la struttura del delegate che gestisce l’evento.

In particolare apriamo la nostra classe LongRunJob con Reflector e vediamo esattamente come si presenta:

imageOsserviamo che il compilatore, dietro le quinte, crea un delegate e definisce l’evento con lo stesso delegate:

  1. Public Delegate Sub JobCompletedEventHandler(ByVal sender As Object, ByVal e As EventArgs)
  2.  
  3. Public Custom Event JobCompleted As JobCompletedEventHandler

Inoltre, la parola chiave RaieseEvent (che viene da noi invocata senza controllare se c’è qualcuno in ascolto) viene compilata nel seguente modo:

  1. Private Sub DoWorkCode(ByVal data As Object)
  2.     Thread.Sleep(CInt((Conversions.ToInteger(data) * 1000)))
  3.     Dim VB$t_ref$S0 As JobCompletedDelegate = Me.JobCompletedEvent
  4.     If (Not VB$t_ref$S0 Is Nothing) Then
  5.         VB$t_ref$S0.Invoke(Me, EventArgs.Empty)
  6.     End If
  7. End Sub

IL compilatore, quindi, genera un codice del tutto simile a quello che scriverebbe un programmatore C#: si controlla se c’è qualcuno in ascolto e, solo in quel caso, si solleva l’evento (metodo Invoke).

Osserviamo, inoltre, che l’evento, nella classe compilata, viene generato come evento custom (ci occuperemo in dettaglio degli eventi custom in un prossimo post). Vengono generati i due metodi richiamati per l’aggancio del gestore di evento e per lo sgancio dello stesso:

  1. Public Sub add_JobCompleted(ByVal obj As JobCompletedEventHandler)
  2.     Me.JobCompletedEvent = DirectCast(Delegate.Combine(Me.JobCompletedEvent, obj), _
  3.         JobCompletedEventHandler)
  4. End Sub
  5.  
  6. Public Sub remove_JobCompleted(ByVal obj As JobCompletedEventHandler)
  7.     Me.JobCompletedEvent = DirectCast(Delegate.Remove(Me.JobCompletedEvent, obj), _
  8.         JobCompletedEventHandler)
  9. End Sub

Di fatto, i gestori di evento agganciati al nostro evento sono “memorizzati” nell’attributo privato JobCompletedEvent (generato dal compilatore). JobCompletedEvent è un oggetto di classe MultiCastDelegate, ovvero un oggetto in grado di avere collegata una lista di gestori di eventi (delegate) detta lista di invocazione. I metodi Combine e Remove del MulticastDelegate consentono di aggiungere e rimuovere un gestore di evento dalla lista di invocazione.

Ultima annotazione importante è la presenza dell’attributo JobCompletedEvent di tipo JobCompletedEventHandler. Questo è presente all’interno della nostra classe pur non essendo visibile all’intellisense. Di fatto possiamo utilizzarlo per capire se qualcuno si è agganciato al nostro evento:

  1. Public Function IsJobCompletedListening() As Boolean
  2.     Return (Me.JobCompletedEvent IsNot Nothing)
  3. End Function

Alla fine, sfruttando le osservazioni fatte, possiamo scrivere la nostra classe “alla C#”, definendo, cioè tutto quello che ci serve:

  1. Public Class LongRunJobCSLike
  2.  
  3.     Public Delegate Sub JobCompletedDelegate(ByVal sender As Object, ByVal e As EventArgs)
  4.  
  5.     Public Event JobCompleted As JobCompletedDelegate
  6.  
  7.     Public Sub DoWork(ByVal nSec As Integer)
  8.         Dim threadStart = New Threading.ParameterizedThreadStart(AddressOf DoWorkCode)
  9.         Dim thread = New Threading.Thread(threadStart)
  10.         thread.Start(nSec)
  11.     End Sub
  12.  
  13.     Private Sub DoWorkCode(ByVal data As Object)
  14.         Dim nSec = CType(data, Int32)
  15.         System.Threading.Thread.Sleep(nSec * 1000)
  16.         If Me.JobCompletedEvent IsNot Nothing Then
  17.             Me.JobCompletedEvent.Invoke(Me, EventArgs.Empty)
  18.         End If
  19.     End Sub
  20.  
  21. End Class

In questo caso il compilatore non definisce il delegate in quanto lo abbiamo già fatto noi.