Enterprise Library 5… ma peccccchèèèè?
Posted by mauro in Software Mason on Tuesday 07 September 2010 at 12:50 PM
La domanda a molti potrebbe suonare strana ma secondo me ci sono regole che non vanno mai violate, in una solution di test ho rimpiazzato il Validation Application Block di Enterpirse Library 4 con quello della versione 5.
Sbomba alla grande…
L’inghippo è che adesso il “V.A.B.” ha un po’ di dipendenze che prima non c’erano:
ServiceLocation e Unity (e compagnia) prima non erano mandatory, spulciando la documentazione non vi è traccia di come poter ovviare a questa dipendenza, la cosa che mi da fastidio è che Enterprise Library è un framework e imporre una dipendenza da un motore di IoC se si sviluppano framework ritengo sia semplicemente sbagliato.
Tempo permettendo cercherò di approfondire la cosa al fine di capire se è evitabile.
.m
Radical 1.0.1 (Vacuum): Memento Property Metadata
Posted by mauro in Memento Software Mason on Monday 06 September 2010 at 1:34 PM
Abbiamo introdotto il concetto di Memento disponibile in Radical e ad un certo punto abbiamo detto che esistono dei metadati che è possibile associare ad una proprietà.
Nel post precedente abbiamo visto che il processo di inizializzazione di una Entity/ViewModel da esporre è più o meno una cosa del genere:
public void Initialize( Person person, Boolean registerAsTransient )
{
if( registerAsTransient )
{
this.RegisterTransient();
}
this.SetInitialPropertyValue( () => this.FirstName, person.FirstName );
this.SetInitialPropertyValue( () => this.LastName, person.LastName );
}
L’implementazione di SetInitialPropertyValue è questa:
protected PropertyMetadata<T> SetInitialPropertyValue<T>( Expression<Func<T>> property, T value )
{
var name = property.GetMemberName();
var metadata = this.GetPropertyMetadata<T>( name );
metadata.DefaultValue = value;
return metadata;
}
readonly IDictionary<String, PropertyMetadata> propertiesMetadata = new Dictionary<String, PropertyMetadata>();
protected PropertyMetadata<T> GetPropertyMetadata<T>( String propertyName )
{
PropertyMetadata md;
if( !this.propertiesMetadata.TryGetValue( propertyName, out md ) )
{
md = this.GetDefaultMetadata<T>( propertyName );
this.propertiesMetadata.Add( propertyName, md );
}
return ( PropertyMetadata<T> )md;
}
protected virtual PropertyMetadata<T> GetDefaultMetadata<T>( String propertyName )
{
return new PropertyMetadata<T>( propertyName );
}
quello che si può notare è che viene implicitamente fornito un set di Metadati di default, metadati che possono essere customizzati nel caso in cui sia necessario aggiungere funzionalità e/o attributi.
Che cosa fanno i Metadati di default?
- tengono traccia di quale sia il valore di default/iniziale assegnato alla proprietà;
- determinano se la proprietà debba o meno notificare, via INotifyPropertyChanged, le variazioni di stato;
- consentono di implementare il concetto di Cascade Notification:
- faccio il set della proprietà BornDate;
- l’entità notifica che la propprietà BornDate è cambiata;
- l’entità notifica che anche la proprietà Age è cambiata;
Se la nostra entity deriva da MementoEntity abbiamo anche il supporto per il change tracking e questo supporto è attivabile/disattivabile per singola proprietà proprio grazie ai Metadati:
protected override PropertyMetadata<T> GetDefaultMetadata<T>( String propertyName )
{
return new MementoPropertyMetadata<T>( propertyName );
}
MementoEntity infatti ridefinisce i Metadati di default aggiungendo il supporto per il tracking:
public class MementoPropertyMetadata<T> : PropertyMetadata<T>
{
public MementoPropertyMetadata( String propertyName )
: base( propertyName )
{
this.TrackChanges = true;
}
public Boolean TrackChanges { get; set; }
}
La domanda a questo punto è: come fa Memento entity a intercettare tutte i get/set per fare tracking? la risposta è abbastanza semplice (e apre a molti scenari interessanti anche per gli utilizzatori). Una proprietà perchè funzioni in questo modello deve essere definita così:
public String FirstName
{
get { return this.GetPropertyValue( () => this.FirstName ); }
set { this.SetPropertyValue( () => this.FirstName, value ); }
}
Se andiamo a spulciare nell’implementazione di MementoEntity troviamo questo:
protected override void SetPropertyValue<T>( string propertyName, T data, PropertyValueChanged<T> pvc )
{
base.SetPropertyValue<T>( propertyName, data, e =>
{
var md = this.GetPropertyMetadata<T>( propertyName ) as MementoPropertyMetadata<T>;
if( md != null && md.TrackChanges )
{
var callback = this.GetRejectCallback<T>( propertyName );
this.CacheChange( e.OldValue, callback );
}
if( pvc != null )
{
pvc( e );
}
} );
}
Un passo indietro… Entity, da cui MementoEntity eredita, espone un metodo SetPropertyValue<T>( Expression<Func<T>> property, T value ) che permette di eseguire il set di un valore, ma espone anche un overload SetPropertyValue<T>( Expression<Func<T>> property, T value, PropertyValueChanged<T> pvc ) che permette all’utilizzatore di agganciare un delegato che viene invocato nel caso in cui il set modifichi effettivamente la proprietà.
MementoEntity fa esattamente quello, esegue l’override di SetPropertyValue e:
- recupera un’istanza dei metadati;
- se è attivo il tracking fa tracking della variazione;
- se l’inheritor ha settato una callback invoca la callback per garantire consistenza;
e il giochetto è fatto
, che vantaggi porta all’eventuale inheritor?
- Una classe derivata potrebbe definire un suo subset di metadati che contengono ad esempio le regole di validazione della proprietà in oggetto;
- alla variazione della proprietà utilizzare i metadati per recuperare il set di regole e validare la proprietà;
.m
M-V-VM: Commanding
Posted by mauro in Software Mason on Thursday 02 September 2010 at 11:40 AM
Finalmente ci siamo, dopo anni luce dall’ultima volta, siamo all’ultimo step della nostra parte introduttiva su Model View ViewModel. Il bello inzia adesso :-)
L’ultimo requisito che abbiamo, per questa parte, è la possibilità di cancellare un indirizzo dalla lista degli indirizzi della persona corrente.
Commanding
Prima di addentrarci nei tecnicismi dell’implementazione introduciamo una delle “novità”, alla faccia della novità… 6 anni sono passati, di Wpf, l’accoppiata ICommand/ICommandSource.
ICommand
Un command è una classe che rappresenta un’azione, concetto semplicissimo per chi arriva da Delphi in cui esistono proprio le Action. Per Wpf un command è una qualsiasi classe che implementa l’interfaccia ICommand che è caratterizzata da:
- Boolean CanExecute( Object ): l’infrastruttura di Wpf chiama CanExecute per sapere se in quel determinato momento il comando debba essere considerato “disponibile”;
- void Execute( Object ): Execute viene chiamato per effettivamente invocare il comando e permettergli di eseguire l’azione per cui è pensato, Execute verrà invocato se e solo se CanExecute ritorna true;
- EventHanlder CanExecuteChanged: Un comando può sollevare questo evento per notificare al motore di Wpf che il suo stato è cambiato ed è quindi il caso che Wpf venga a controllare nuovamente con una chiamata a CanExecute;
Un esempio tipico potrebbe essere un ipotetico SaveCommand che a seguito di un chiamata a CanExecute ritorna true se il grafo è effettivamente modificato, lo stesso command può notificare a Wpf che il suo stato è cambiato quando lo stato del grafo cambia ed infine il command si preoccuperà di innescare la logica per il salvataggio all’atto dell’execute.
n.d.r.: CanExecute ed Execute prendono in ingresso un parametro di tipo object che possiamo usare sulla falsa riga di un command argument che potrebbe essere utile per pilotare dall’esterno parte della logica interna de command.
ICommandSource
La controparte di un ICommand è ICommandSource. Una classe che implementa ICommandSource è qualcosa, tipicamente un controllo della UI, che è in grado di invocare un comando. ICommandSource è così definita:
- ICommand Command{ get; set; }: è la dependency property, quindi con pieno supporto per data binding, che ci consente di impostare quale sia il comando che vogliamo venga eseguito;
- Object CommandParameter{ get; set; }: è la dependency property che ci consente di impostare l’eventuale parametro da passare al comando;
- IInputElement CommandTarget{ get; set; }: Ha senso, e viene utilizzata, solo ed esclusivamente se il comando è un RoutedCommand, tipologia di comando che non ci interessa trattare;
Anche in questo caso un esempio tipico è un bottone, quando impostiamo la proprietà command la prima cosa che succede è che il bottone fa una chiamata a CanExecute per capire lo stato del comando e nel caso in cui l’esito sia negativo automaticamente si disabilita. Allo stesso modo quando il comando scatena l’evento CanExecuteChanged il bottone fa nuovamente una chiamata a CanExecute per rivalutare il suo stato e in caso affermativo si riabilita.
Ogni controllo è libero di implementare a suo piacimento la logica e il comportamento da seguire a seguito della variazione di stato di un comando, come abbiamo detto, ad esempio, il bottone si disabilita.
DelegateCommand
Wpf mette a disposizione un largo set di command già pronti all’uso, questi sono tutti di tipo RoutedCommand che, in un mondo basato su Model View ViewModel, ha una serie di difetti: in primis quello di non avere nessuna possibilità di controllo sul processo di notifica della variazione di stato del comando. I limiti di RoutedCommand e di RoutedUICommand hanno portato alla nascita di svariate implementazioni dell’interfaccia ICommand, implementazioni che tipicamente cadono sotto il nome di DelegateCommand o RelayCommand.
Il tutto si “limita” ad un’implementazione più o meno stilisticamente bella che consente di generare un comando agganciando al volo dei delegati, tipicamente facendo largo uso di una sintassi basata sulle lambda, che verranno eseguiti sia per determnare lo stato del comando (CanExecute) sia per eseguire il comando (Execute); inoltre tutti espongono un meccamismo per poter triggherare la notifica della varizione di stato del comando stesso.
Anche Radical non è da meno e offre un’interessante implementazione.
AddressDeleteCommand
Adesso che sappiamo un po’ meglio la teoria non ci resta che metterla in pratica. La prima cosa che ci interessa fare è esporre la funzionalità dal nostro MainViewModel, semplicemente esponendo una proprietà di tipo ICommand:
public ICommand DeleteAddressCommand { get; private set; }
contestualmente agganciando, via DataBinding, la nuova proprietà ad un nuovo elemento della UI:
<Button Content="Delete" Command="{Binding Path=DeleteAddressCommand}" Margin="0,0,317,0" />
l’implementazione del nostro comando è decisamente semplice anche se dietro le quinte succedono parecchie cose:
this.DeleteAddressCommand = DelegateCommand.Create() .OnCanExecute( o => this.SelectedAddress != null ) .OnExecute( o => { var add = this.SelectedAddress; this.SelectedAddress = null; this.Person.Addresses.Remove( add ); } ) .AddMonitor ( PropertyObserver.For( this ) .Observe( vm => vm.SelectedAddress ) );
Radical offre un entry point statico, e anche un costruttore volendo, per la creazione di un nuovo comando. La cosa comoda è il supporto per le fluent interface che vi permette di definire le caratteristiche del comando in maniera decisamente intuitiva:
- OnCanExecute si aspetta un Func
- OnExecute accetta invece come parametro una Action
La parte veramente interessante, di cui ho già parlato (sintassi a parte che è leggermente cambiata), è AddMonitor che vi permette di agganciare dei “trigger” al command, trigger che altro non fanno che automatizzare il processo di notifica, alla UI, della variazione di stato del comando stesso.
Fatto tutto ciò se mandiamo in esecuzione la nostra bellissima applicazione (bellissima è auto-consolatorio
Come tradizione allego i sorgenti: Mvvm.Application 1.3
Il prossimo requisito? essere in grado di creare una nuova “Person” con la sua simpatica lista di “Address”.
.m
) quello che otteniamo è questo:
NHibernate e Castle 2.5
Posted by mauro in Software Mason on Thursday 26 August 2010 at 5:40 AM
Alberto ha scritto tutto quello che c’era da scrivere: Nuova versione di Castle Windsor (e NHibernate?), ma c’è anche una terza strada, ancora più semplice:
sostituire il ByteCode provider di NHibernate, usato per la generazione dei dynamic proxy, con quello di LinFu rimuovendo di fatto ogni dipendenza tra NH e Castle; in questo modo potete tranquillamente usare Castle 2.5 per i vostri scopi senza toccare NH.
Vantaggi? cambiate solo una riga di configurazione di NH e nulla di più… robetta da 3 nanosecondi ![]()
.m
Amen…
Tutto il post merita, ma questa frase mi fa schiattare:
Ahhhh se solo la maggior parte dei manager sapesse… il mondo girerebbe diversamente, ma la resa dei conti arriverà presto caro cravattato dall’ignoranza bovina…
Fonte: I Kanban non sono una metodologia
.m
Notebook battery life with Sql Server…
Avete un portatile come macchina di sviluppo? usate Sql Server? vi lamentate che la dutata della batteria è decisamente povera?
Ovviamente non è colpa di Sql Server che non è assolutamente pensato, giustamente, per quello scenario, ma nonostatnte questo potete fare un paio di modifiche alla configurazione di Sql Server e probabilmente osservare, nel mio caso è stato così, un boost della durata della batteria:
Sulla mia nuova macchina Dell E4310, è si non ho più un Mac
, ho un Core i5 con 8Gb di RAM, ho deciso di assegnare a Sql un solo core e un solo Gb di RAM: il Dell Battery Meter è felicissimo
.
Naturalmente fate anche un bel giro di “powercfg –energy” da un promp con elevation… si scoprono sempre cose interessanti ![]()
.m
Tip: Windows 7 Explorer folder pinning
Un piccolo tip, che mi ha fatto impazzire per un po’…
Vi siete mai chiesti quale sia la logica per cui una “folder” compare nella lista dei “frequent”? io spesso…
perchè ci sono folder che avrei sempre voluto “pinnare” che non sono mai riuscito a far comparire li in mezzo…
Poi un giorno, qualche settimana fa, ho notato un comportamento curioso che mi ha portato ad un caso riproducibile con certezza matematica:
perchè una folder compaia nei frequent, e sia quindi pinnabile, è condizione necessaria e sufficiente che ci abbiate salvato qualche cosa; attenzione copiarci un file non basta è necessario che quel file venga modificato e salvato in quella folder.
è quindi sufficiente creare un file di testo in una folder, aprirlo, scriverci qualcosa e salvarlo e “magicamente” quella folder apparirà nei “frequent”.
Ora io capisco tutto… ma spero che il tizio che ha pensato questa feature (per la serie feature? quale feature?) sia stato deportato a vendere gelati in Alaska ![]()
.m
Il supporto perfetto :-)
volevo ringraziare tutto il team di Dexter (di cui al momento faccio parte anche io anche se per sport) che ha dato prova di essere veramente in gamba nel supportarmi durante il debug di uno stranissimo e randomicissimo problema che si verifica sulla mia installazione.
Sono intervenuti in massa, in ordine sparso Gian Maria Ricci, Ughetto, Andrea Balducci e Alessandro Giorgetti mettendo a disposizione armi raffinatissime come ad esempio una VM su cui spostare il tutto per tornare in produzione e nel frattempo continuare la fase di debug/test sull’installazione presso il mio provider.
Complimenti! se tutti i supporti fossero come questo gruppo di amici pazzi saremmo tutti più felici ![]()
.m
Recent Comments