Data Binding
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
LINQ to SQL converte le query LINQ 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 usare il data binding 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. Le origini dati generiche Table<TEntity> (Table<T>
in C# o Table(Of T)
in Visual Basic) e DataQuery
generiche sono state aggiornate per implementare 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:
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;
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
Lo stesso comportamento si verifica con Windows Presentation Foundation:
ListView listView1 = new ListView();
var custQuery2 =
from cust in db.Customers
select cust;
ListViewItem ItemsSource = new ListViewItem();
ItemsSource = (ListViewItem)custQuery2;
Dim listView1 As New ListView()
Dim custQuery2 = _
From cust In db.Customers _
Select cust
Dim ItemsSource As New ListViewItem
ItemsSource = custQuery2
Le generazioni di raccolte 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 riesce a trovare l'oggetto sottostante Table<TEntity>, l'origine non consente l'edizione, ad esempio
groupby
. LINQ to SQL esplora la query per riempire un oggetto genericoSortableBindingList
, un semplice BindingList<T> che implementa la funzionalità di ordinamento per le entità T per una determinata proprietà.
Raccolte specializzate
Per molte funzionalità descritte in questo documento, BindingList<T> è stato specializzato per alcune classi diverse. Tali classi, SortableBindingList
e DataBindingList
, sono di tipo generico e 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
. Usando 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 usati 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
usato 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
usa 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()
, quindiOnListChanged()
.Le proprietà SortDirection e SortProperty espongono la definizione di ordinamento corrente, che viene archiviata nei membri locali.
Quando si usa 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 usa 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 sarà necessario chiamare EntitySet<Tentity>.GetNewBindingList per aggiornare BindingSource.List ma si perderà la 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 usato 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 tutte le raccolte 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
Questa sezione descrive alcuni elementi che potrebbero facilitare la risoluzione dei problemi relativi alle applicazioni LINQ to SQL con data binding.
È necessario usare proprietà; non è sufficiente usare solo campi. Questo tipo di uso è obbligatorio in Windows Form.
Per impostazione predefinita, i tipi di database
image
,varbinary
etimestamp
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 usata 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.