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 generico SortableBindingList, 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:

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 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 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.

Vedi anche