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 SetInitialPropertyValue( 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 PropertyMetadata GetPropertyMetadata( 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 PropertyMetadata GetDefaultMetadata( String propertyName )
{
return new PropertyMetadata( 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 GetDefaultMetadata( String propertyName )
{
return new MementoPropertyMetadata( propertyName );
}
MementoEntity infatti ridefinisce i Metadati di default aggiungendo il supporto per il tracking:
public class MementoPropertyMetadata : PropertyMetadata
{
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( 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 );
}
} );
}
Un passo indietro… Entity, da cui MementoEntity eredita, espone un metodo SetPropertyValue( Expression> property, T value ) che permette di eseguire il set di un valore, ma espone anche un overload SetPropertyValue( Expression> property, T value, PropertyValueChanged 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 Smile, 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