Radical 1.0.1 (Vacuum): Memento Property Metadata
Nel post precedente abbiamo visto che il processo di inizializzazione di una Entity/ViewModel da esporre è più o meno una cosa del genere:
L’implementazione di SetInitialPropertyValue è questa:public void Initialize( Person person, Boolean registerAsTransient )
{
if( registerAsTransient )
{
this.RegisterTransient();
}
this.SetInitialPropertyValue( () => this.FirstName, person.FirstName );
this.SetInitialPropertyValue( () => this.LastName, person.LastName );
}
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.protected PropertyMetadataSetInitialPropertyValue ( Expression<Func > property, T value )
{
var name = property.GetMemberName();
var metadata = this.GetPropertyMetadata( name );
metadata.DefaultValue = value;
return metadata;
}
readonly IDictionary<String, PropertyMetadata> propertiesMetadata = new Dictionary<String, PropertyMetadata>();
protected PropertyMetadataGetPropertyMetadata ( String propertyName )
{
PropertyMetadata md;
if( !this.propertiesMetadata.TryGetValue( propertyName, out md ) )
{
md = this.GetDefaultMetadata( propertyName );
this.propertiesMetadata.Add( propertyName, md );
}
return ( PropertyMetadata)md;
}
protected virtual PropertyMetadataGetDefaultMetadata ( String propertyName )
{
return new PropertyMetadata( propertyName );
}
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;
MementoEntity infatti ridefinisce i Metadati di default aggiungendo il supporto per il tracking:protected override PropertyMetadataGetDefaultMetadata ( String propertyName )
{
return new MementoPropertyMetadata( propertyName );
}
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 class MementoPropertyMetadata: PropertyMetadata
{
public MementoPropertyMetadata( String propertyName )
: base( propertyName )
{
this.TrackChanges = true;
}
public Boolean TrackChanges { get; set; }
}
Se andiamo a spulciare nell’implementazione di MementoEntity troviamo questo:public String FirstName
{
get { return this.GetPropertyValue( () => this.FirstName ); }
set { this.SetPropertyValue( () => this.FirstName, value ); }
}
Un passo indietro… Entity, da cui MementoEntity eredita, espone un metodo SetPropertyValueprotected override void SetPropertyValue( string propertyName, T data, PropertyValueChanged pvc )
{
base.SetPropertyValue( propertyName, data, e =>
{
var md = this.GetPropertyMetadata( propertyName ) as MementoPropertyMetadata ;
if( md != null && md.TrackChanges )
{
var callback = this.GetRejectCallback( propertyName );
this.CacheChange( e.OldValue, callback );
}
if( pvc != null )
{
pvc( e );
}
} );
}
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;
- 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à;