INotifyPropertyChanged è ormai di casa per chi usa databinding sopratutto per chi lo fa con WPF, con LINQ è in arrivo un altra interfaccia denominata INotifyPropertyChanging.
Mentre la prima ha lo scopo di informare il motore di binding che una determinata proprietà è stata modificata e che quindi l'eventuale controllo ad essa bindato deve essere aggiornato, la seconda serve per ottimizzare il meccanismo di change tracking implementato da LINQ per determinare se un entità è stata modificata e quindi inviare il comando SQL di aggiornamento quando invochiamo DataContext.SubmitChanges().
In assenza di questa interfaccia quando recuperiamo delle entities via DataContext come nell'esempio che segue:
una copia delle entities viene memorizzata nel DataContext e viene poi utilizzata per capire se un entity è stata modificata o meno quando si invoca il metodo SubmitChanges().
Oltre a questa copia, l'istanza dell'entity restituita viene a sua volta memorizzata dal DataContext e utilizzata dal cosidetto Identity Management Service affinchè a parità di riga recuperata dal db contentente una primary key corrisponda la stessa istanza dell'entity associata.
Ovvero, se nel DB abbiamo un solo customer che si chiama Teodoro e ha 15 anni recuperando lo stesso usando due diverse query l'entity ritornata è la stessa come dimostrato di seguito:
In questo caso MyDb è una classe che eredita da DataContext ed espone una proprietà Customers di tipo Table<Customer> ma quello che è importante notare è che quella che viene ritornata è esattamente la stessa istanza (in alcuni casi non viene nemmeno inviato il comando SQL al DB...)
Questo è vero se il servizio di tracking è attivo ovvero se la proprietà DataContext.ObjectTrackingEnabled=true, impostandola a false il servizio di tracking viene disattivato con relativo incremento di performances anche se, in questo caso, il contenuto del db diventa non modificabile o almeno non direttamente da LINQ.
Sta di fatto che, alla fine, il DataContext mantiene due copie delle entities recuperate:
- Quella per l' Identity management (che coincide con quella restituita dalla query)
- Quella per il Change Tracking Service (se ObjectTrackingEnabled=true)
Per la prima dobbiamo ricordarci che l'istanza rimane attiva fino a quando il relativo DataContext rimane caricato, ovvero se scriviamo qualcosa tipo:
L'istanza di C1 non verrà richiamata dal garbage collector fino a quando il relativo DataContext non verrà impostato a null.
Per la seconda, implementando INotifyPropertyChanging possiamo evitare la creazione della seconda copia facendoci carico di informare il change tracking service quando un entità viene modificata come nell'esempio che segue:
Gran parte di questo codice ripetitivo viene generato automaticamente da SQLMetal oppure dal designer integrato di Visual Studio 2008 (peccato non funzioni con SQL Server Compact 3.5)
Sia che facciate uso di un tool oppure che le classi le generate voi "by hand" è comunque importante conoscere i "retroscena" legati al DataContext affinchè tutte queste "copie interne" non finiscano per affossare la vostra applicazione.