Associazione dati (LINQ to SQL)
LINQ to SQL supporta l'associazione ai controlli comuni, ad esempio i controlli griglia.In particolare, in LINQ to SQL vengono definiti i modelli di base per l'associazione a una griglia dei dati e la gestione dell'associazione Master-Details per quanto riguarda le operazioni di visualizzazione e aggiornamento.
Principio sottostante
In LINQ to SQL le query LINQ vengono convertite in SQL per l'esecuzione in un database.I risultati sono costituiti da oggetti IEnumerable fortemente tipizzati.Poiché si tratta di oggetti Common Language Runtime (CLR) comuni, per visualizzare i risultati è possibile utilizzare l'associazione dati di oggetti comune.Al contrario, le operazioni di modifica (inserimenti, aggiornamenti ed eliminazioni) richiedono passaggi aggiuntivi.
Operazione
L'associazione implicita ai controlli Windows Form viene eseguita implementando IListSource.Gli oggetti generici Table<TEntity> (Table<T> in C# o Table(Of T) in Visual Basic) e DataQuery delle origini dati sono stati aggiornati ai fini dell'implementazione di IListSource. I motori di associazione dati dell'interfaccia utente (Windows Form e Windows Presentation Foundation) verificano se le relative origini dati implementano IListSource.Pertanto, con la scrittura di una simulazione diretta di una query in un'origine dati di un controllo viene eseguita una chiamata implicita alla generazione di una raccolta LINQ to SQL, come nell'esempio seguente:
Dim dataGrid1 As New DataGrid()
Dim dataGrid2 As New DataGrid()
Dim dataGrid3 As New DataGrid()
Dim custQuery = _
From cust In db.Customers _
Select cust
dataGrid1.DataSource = custQuery
dataGrid2.DataSource = custQuery
dataGrid2.DataMember = "Orders"
Dim bs = _
New BindingSource()
bs.DataSource = custQuery
dataGrid3.DataSource = bs
DataGrid dataGrid1 = new DataGrid();
DataGrid dataGrid2 = new DataGrid();
DataGrid dataGrid3 = new DataGrid();
var custQuery =
from cust in db.Customers
select cust;
dataGrid1.DataSource = custQuery;
dataGrid2.DataSource = custQuery;
dataGrid2.DataMember = "Orders";
BindingSource bs = new BindingSource();
bs.DataSource = custQuery;
dataGrid3.DataSource = bs;
Lo stesso comportamento si verifica con Windows Presentation Foundation:
Dim listView1 As New ListView()
Dim custQuery2 = _
From cust In db.Customers _
Select cust
Dim ItemsSource As New ListViewItem
ItemsSource = custQuery2
ListView listView1 = new ListView();
var custQuery2 =
from cust in db.Customers
select cust;
ListViewItem ItemsSource = new ListViewItem();
ItemsSource = (ListViewItem)custQuery2;
Le generazioni di insiemi vengono implementate dall'oggetto generico Table<TEntity> e dall'oggetto generico DataQuery in GetList.
Implementazione di IListSource
LINQ to SQL implementa IListSource in due posizioni:
L'origine dati è un oggetto Table<TEntity>: LINQ to SQL esamina la tabella per il riempimento di una raccolta DataBindingList che mantiene un riferimento nella tabella.
L'origine dati è un oggetto IQueryable<T>.Esistono due possibili scenari:
Se LINQ to SQL trova l'oggetto Table<TEntity> sottostante da IQueryable<T>, l'origine consente la modifica e la situazione rispecchia quella descritta nel primo punto dell'elenco.
Se LINQ to SQL non trova l'oggetto Table<TEntity> sottostante, l'origine non consente la modifica (ad esempio, groupby). LINQ to SQL esamina la query per il riempimento di un oggetto SortableBindingList generico, che corrisponde a un semplice oggetto BindingList<T> che implementa la funzionalità di ordinamento per le entità T di una determinata proprietà.
Insiemi specializzati
Per molte funzionalità descritte in questo documento, BindingList<T> è stato specializzato per alcune classi diverse.Tali classi, SortableBindingList e DataBindingList, sono di tipo genericoe vengono entrambe dichiarate come interne.
SortableBindingList generica
Questa classe eredita da BindingList<T> ed è una versione ordinabile di BindingList<T>.L'ordinamento è una soluzione in memoria e non effettua mai la connessione al database.BindingList<T> implementa IBindingList ma non supporta l'ordinamento per impostazione predefinita.Tuttavia BindingList<T> implementa IBindingList con metodi virtuali di base.È possibile eseguire facilmente l'override di questi metodi.La classe generica SortableBindingList esegue l'override di SupportsSortingCore, SortPropertyCore, SortDirectionCore e ApplySortCore.ApplySortCore viene chiamato da ApplySort e ordina l'elenco di elementi T per una determinata proprietà.
Se la proprietà non appartiene a T, viene generata un'eccezione.
Per ottenere l'ordinamento, LINQ to SQL crea una classe SortableBindingList.PropertyComparer generica che eredita dall'oggetto generico IComparer.Compare e implementa un operatore di confronto predefinito per un tipo T specificato, un oggetto PropertyDescriptor e una direzione.Questa classe crea dinamicamente un oggetto Comparer di T, dove T è PropertyType di PropertyDescriptor.L'operatore di confronto predefinito viene quindi recuperato dall'oggetto statico generico Comparer.Utilizzando la reflection si ottiene un'istanza predefinita.
La classe generica SortableBindingList è anche la classe base per DataBindingList.La classe generica SortableBindingList offre due metodi virtuali per sospendere o riprendere il rilevamento dell'aggiunta o rimozione di elementi.Questi due metodi possono essere utilizzati per funzionalità di base come l'ordinamento, ma saranno effettivamente implementati dalle classi superiori come DataBindingList generico.
DataBindingList generica
Questa classe eredita dalla classe generica SortableBindingLIst.La classe generica DataBindingList mantiene un riferimento all'oggetto generico Table sottostante dell'oggetto generico IQueryable utilizzato per il riempimento iniziale della raccolta.La classe generica DatabindingList aggiunge il rilevamento dell'aggiunta o rimozione degli elementi alla raccolta eseguendo l'override di InsertItem() e RemoveItem().Implementa inoltre la funzionalità di rilevamento della sospensione o ripresa di elementi astratti per rendere condizionale il rilevamento.Questa funzionalità consente alla classe generica DataBindingList di sfruttare completamente l'uso polimorfico della funzionalità di rilevamento delle classi padre.
Associazione a EntitySet
L'associazione a EntitySet è un caso speciale perché EntitySet è già una raccolta che implementa IBindingList.LINQ to SQL aggiunge il supporto per l'ordinamento e l'annullamento (ICancelAddNew).Una classe EntitySet utilizza un elenco interno per archiviare entità.Tale elenco è una raccolta di basso livello basato su una matrice generica, la classe ItemList generica.
Aggiunta di una funzionalità di ordinamento
Le matrici offrono un metodo di ordinamento (Array.Sort()) che può essere utilizzato con un oggetto Comparer di T.LINQ to SQL utilizza la classe generica SortableBindingList.PropertyComparer descritta in questo argomento per ottenere questo oggetto Comparer per la proprietà e la direzione di ordinamento.Per chiamare questa funzionalità, viene aggiunto un metodo ApplySort all'oggetto generico ItemList.
Sul lato EntitySet è necessario dichiarare il supporto dell'ordinamento:
SupportsSorting restituisce true.
ApplySort chiama entities.ApplySort(), quindi OnListChanged().
Le proprietà SortDirection e SortProperty espongono la definizione di ordinamento corrente, che viene archiviata nei membri locali.
Quando si utilizza un oggetto System.Windows.Forms.BindingSource e si associa un oggetto EntitySet<TEntity> all'oggetto System.Windows.Forms.BindingSource.DataSource, è necessario chiamare EntitySet<Tentity>.GetNewBindingList per aggiornare BindingSource.List.
Se si utilizza un oggetto System.Windows.Forms.BindingSource e si imposta la proprietà BindingSource.DataMember nonché si imposta BindingSource.DataSource su una classe che contiene una proprietà denominata in BindingSource.DataMember che espone EntitySet<TEntity>, non è necessario chiamare EntitySet<Tentity>.GetNewBindingList per aggiornare BindingSource.List ma si perde funzionalità di ordinamento.
Memorizzazione nella cache
Le query LINQ to SQL implementano GetList.Quando la classe BindingSource di Windows Form individua questa interfaccia, chiama GetList() tre volte per una sola connessione.Per evitare questa situazione, LINQ to SQL implementa una cache per ogni istanza per archiviare e restituire sempre la stessa raccolta generata.
Annullamento
IBindingList definisce un metodo AddNew utilizzato dai controlli per creare un nuovo elemento da una raccolta associata.Il controllo DataGridView mostra molto chiaramente questa funzionalità quando nell'intestazione dell'ultima riga visibile è presente un asterisco.L'asterisco indica che è possibile aggiungere un nuovo elemento.
Oltre a questa funzionalità, una raccolta consente di implementare ICancelAddNew.Tale funzionalità consente ai controlli di annullare o di convalidare che il nuovo elemento modificato è stato o meno convalidato.
ICancelAddNew viene implementato in tutti gli insiemi LINQ to SQL con associazione a dati (SortableBindingList e EntitySet generici). In entrambe le implementazioni il codice viene eseguito come segue:
Consente l'inserimento e la successiva rimozione di elementi dalla raccolta.
Non tiene traccia delle modifiche finché non ne viene eseguito il commit dall'interfaccia utente.
Non tiene traccia delle modifiche se queste vengono annullate (CancelNew).
Consente di registrare quando viene eseguito il commit della modifica (EndNew).
Consente alla raccolta di comportarsi normalmente se il nuovo elemento non proviene da AddNew.
Risoluzione dei problemi
In questa sezione vengono indicati alcuni elementi che potrebbero facilitare la risoluzione dei problemi relativi alle applicazioni LINQ to SQL con associazione dati.
È necessario utilizzare proprietà; non è sufficiente utilizzare solo campi.Questo tipo di utilizzo è obbligatorio in Windows Form.
Per impostazione predefinita, i tipi di database image, varbinary e timestamp eseguono il mapping alla matrice di byte.Poiché ToString() non è supportato in questo scenario, questi oggetti non possono essere visualizzati.
Un membro della classe per il quale è stato eseguito il mapping a una chiave primaria dispone di un metodo di impostazione, ma LINQ to SQL non supporta la modifica di identità dell'oggetto.Non è quindi possibile aggiornare la chiave primaria/univoca utilizzata per il mapping nel database.Una modifica nella griglia genera un'eccezione quando si chiama SubmitChanges.
Se un'entità è associata in due griglie separate, ad esempio una di dettaglio e una master, un'operazione Delete nella griglia master non viene propagata alla griglia di dettaglio.