Tutorial ADO.NET & Oracle

Parte 4: Il Binding di ADO.NET




P   a   r   t   e   1          -          P   a   r   t   e       2          -          P   a   r   t   e       3          -          P   a   r   t   e       4



INTRODUZIONE

Le funzionalità fornite da ADO.NET sono molteplici, quali tra queste sono utili per le nostre esigenze? Questo tutorial cerca la risposta a tale domanda, indirizza l'apprendimento e l'utilizzo di ADO.NET secondo le esigenze tipiche di una Software Factory. Il tutorial perciò descrive l'architettura di ADO.NET, gli obiettivi e le finalità della tecnologia. Inoltre suggerisce le parti di maggiore utilità e le linee guida per l'utilizzo di ADO.NET e le specificità legate ad Oracle. Questa quarta parte si concentra sul binding di ADO.NET.


ADO.NET E LE PAGINE WEB

Come noto i controlli Web in binding con una DataTable o una DataView  vengono popolati al momento della prima creazione della  pagina (ossia quando !IsPostback). Da quel momento in poi la relazione tra il controllo e la DataTable cessa.

Binding

Tuttavia in un momento successivo la relazione tra il controllo e la DataTable potrebbe tornale utile, per esempio:
Quindi tra una elaborazione della pagina e la successiva (ossia per dirla in HTTP ad ogni Post o per dirla in HTML ad ogni Submit) come mantenere il DataSet?
Una risposta univoca non esiste, tuttavia bisogna conoscere le variabili in gioco per trovare il punto di equilibrio adatto ad ogni specifica soluzione. A tale scopo si veda: State Management Recommendations . Tra le varie opzioni, specialmente per la paginazione non è da escludere di limitare il numero massimo di righe (imponendo all'utente di inserire condizioni di filtro più stingenti oppure forzando il caricamento di alpiù 5 pagine e  fornendo l'opzione di caricare anche le successive) e di salvare su file-system il DataSet (delle prime 5 pagine) serializzato. Si  veda la soluzione proposta dal progetto open-source Relartist per una soluzione prestazionalmente accettabile.


DALLA DATAGRID AL DATASOURCE

Accedere ai controlli in Edit della DataGrid

Quando una riga della griglia è in Edit è possibile accedere ai valori in edit (e cioè ai controlli in edit) sia per indice che per chiave:
Quale tecnica usare?
Segue domanda e risposta poste ad una mailing-list.
-----------------------------------------------------------------------------
Date:         Mon, 10 Dec 2001 01:30:57 -0800
From:         Luca Minudel
Subject:      DataBinder.Eval and FindControl... should I use them?
I've seen that ASP.NET QuickStart examples use FindControl for gain access 
to user controls
   (sysWebCtl.DropDownList)E.Item.FindControl("dropDownEditCustomer")
I've tryed to do it myself without FindControl
(sysWebCtl.DropDownList)E.Item.Cells[3].Controls[1]
I do it just to learn how thinks work. But now I'd like to know if I should
continue to use FindControl in my code or I shouldn't... and why?
 TIA
-----------------------------------------------------------------------------
Date:         Tue, 11 Dec 2001 13:57:47 -0800
From:         Conrad Chan 
Using FindControl so that you can locate your control by its name instead
of by its ordinal position.  Hence wherever you move your control based on
your page design changes, as long as your control stays with its name your
code will continue to work.
Conrad
-----------------------------------------------------------------------------

Trovare le righe corrispondenti nella DataGrid e nel DataSource 

Il Binding nelle pagine Web è unidirezionale, cioè la DataGrid accede al DataSource solo per popolare le righe e le colonne, quindi il riferimento al DataSource viene perso. Per riportare nel DataSet le modifiche fatte dall'utente durante l'Edit della DataGrid è necessario che il programmatore scriva del codice apposito. Questo codice dovrà individuare quale riga del DataSource corrisponde alla riga modificata della DataGrid ed anche accedere ai dati originali, quelli del DataSource, corrispondenti ad una cella della DataGrid. Come fare?
QUANDO IL BINDING E' ATTIVO
Il Binding è attivo al momento della popolazione della Data Grid quando cioè il DataSource è stato assegnato alla DataGrid ed è stato eseguito il Bind e cioè durante gli eventi di ItemCreated, ItemDataBound e DataBinding . L'assegnazione del DataSource alla DataGrid può essere ripetuta in più occasioni avendo cura di mantenere in memoria ed inalterato il DataSource. L'accesso ai dati avviene con le medesime tecniche usate per indicare esplicitamente una espressione di Binding personalizzato quindi quanto detto in seguito vale anche per le espressioni di Binding. Ecco due tecniche per accedere alla riga del DataSource corrisponde ad una riga nella DataGrid :
  • Con cast esplicito
    ((DataRowView)E.Item.DataItem)["OrderID"])
    Ovviamente bisogna indicare il nome della colonna a cui accedere, ma bisogna indicare anche il tipo della fonte dati (DataRowView nell'esempio) che potrebbe variare a seconda si usi per DataSource un DataTable, una DataView un DataReader o magari una Array o una collezione.
    In questo caso E è l'argomento di un evento della griglia, in alternativa ad E.Item posso indicare Datagrid1.Items[idx] :
    ((DataRowView)Datagrid1.Items[idx].DataItem)["OrderID"])
    dove idx è l'indice della riga.
    Qualora si voglia specificare una espressione personalizzata per popolare un controllo in Binding (solitamente nella griglia per le <BoundColumn>  e le <TemplateColumn> ) l'espressione sarebbe:
    <%# ((DataRowView)Container.DataItem)["OrderID"]%>
    a cui si aggiunge solitamente lo String.Format per controllare la visualizzazione testuale del dato in griglia .
  • Con DataBinder
    DataBinder.Eval(E.Item.DataItem, "OrderID")
    Impiegando il DataBinder è possibile accedere al dato semplicemente indicando il nome della colonna e quindi indipendentemente dal tipo della fonte dati impiegata.
    Qualora si voglia specificare una espressione personalizzata per popolare un controllo in Binding (solitamente nella griglia per le <BoundColumn>  e le <TemplateColumn> ) l'espressione sarebbe:
    <%# DataBinder.Eval(Container.DataItem, "OrderID", "{0:c}") %>
    in cui compare anche la formattazione della visualizzazione testuale del dato in griglia.
Quale tecnica usare?
La documetazione MSDN a proposito del DataBinder dice:
CAUTION   Since this method performs late-bound evaluation, using reflection, at runtime, it can cause performance to noticeably slow compared to standard ASP.NET data-binding syntax.
 
Segue domanda e risposta poste ad una mailing-list.
-----------------------------------------------------------------------------
Date:         Mon, 10 Dec 2001 01:30:57 -0800
From:         Luca Minudel
Subject:      DataBinder.Eval and FindControl... should I use them?
I've seen that ASP.NET QuickStart examples use DataBinder.Eval for
automatic casting and formatting while binding
<%# DataBinder.Eval(Container.DataItem, "OrderID", "{0:c}") %>
I've tryed to do it myself without DataBinder.Eval
<%# String.Format("{0:c}", ((DataRowView)Container.DataItem)["OrderID"])%>
I do it just to learn how thinks work. But now I'd like to know if I should
continue to use DataBinder.Eval or I shouldn't... and why?
-----------------------------------------------------------------------------
Date:         Tue, 11 Dec 2001 13:57:47 -0800
From:         Conrad Chan 
Using DataBinder.Eval hides the detail about the structure of the
underlying data source from the ASPX page.  In your example of not using
DataBinder, you have to know your underlying datasource which is a
dataRowView.  Using DataBinder.Eval approach simply gives you more room to
change in the future.  To me, there is nothing wrong with either approach.
It is simply a design decision for you particular use cases.
Conrad
-----------------------------------------------------------------------------
Date:         Wed, 12 Dec 2001 00:39:03 -0800
From:         Luca Minudel 
>In your example of not using DataBinder, you have to know your underlying
>datasource which is a dataRowView.

In my example the cast to the underlyng datasource is resolved at run-time
(just like with DataBinder.Eval I think).
How can I code the binding to allow this check at compile-time?
-----------------------------------------------------------------------------
Date:         Wed, 12 Dec 2001 09:59:39 -0800
From:         Conrad Chan 
First of all, DataBinder uses reflection instead of casting to load
data.  Both styles bind the data at run-time.  The only difference is the
flexibility.  Say if you need to switch to use other datasource type for
some reason like DataTable or an array of typed objects, your
DataBinder.Eval code will continue to work.
-----------------------------------------------------------------------------
QUANDO IL BINDING NON E' ATTIVO
Come prima cosa bisogna ricordare che le informazioni visualizzate nella pagina corrente dalla DataGrid e raccolte tramite DataSource sono memorizzate in forma testuale nello stato della DataGrid (cioè nel ViewState) e successive visualizzazioni della medesima pagina della DataGrid (a seguito di operazioni dell'utente che provocano il submit della pagina, l'esecuzione dell'evento sul server  e il successivo invio della pagina modificata al client)  si baseranno sulle informazioni mantenute nello stato (il ViewState) e non su quelle del DataSource al quale la DataGrid non ha più riferimenti.

E'  possibile scoprire quale riga del DataSource corrisponde ad una riga visualizzata nella DataGrid anche senza ripetere l'operazione di Binding che è una operazione costosa in termini di tempo CPU (ricordo che la memorizzazione del DataSource è comunque a carico del programmatore come descritto sopra in ADO.NET e pagine Web ).
Ecco come. Sia con una DataGrid che con una DataList  si può impostare il DataKeyField per indicare quale campo del DataSource è il campo chiave. Ciò causerà la popolazione della collezione DataKeys del controllo. Una volta che la collezione è popolata risulta facile, dentro un evento del controllo, ottenere il valore della chiave relativa alla riga corrente con l'espressione


   MiaDataTable.DataKeys[e.Item.ItemIndex]
XXX


Il campo chiave così ottenuto può essere usato per trovare la rispettiva riga del DataSource con cui è stata popolata la griglia (come descritto in seguito in Cercare, filtrare, ordinare ) o compiere altre elaborazioni.
 
Quando la chiave è composta da più colonne non è possibile usare DataKeyField e DataKeys; ci sono due alternative:

CERCARE, FILTRARE E ORDINARE

Dal libro di ADO.NET (MS Press):

Q. How do I determine which method to use to search for data in my DataTable?

A. That depends on what type of search you want to perform and what you want to do with the results of the search.
     Here’s a simple set of guidelines:
Dalla  MSDN Library c'è questa nota importante per il DataView:

Creating a DataView without specifying sort or filter criteria and then setting the Sort, RowFilter, or
RowStateFilter properties later results in the index being built at least twice: once when the DataView is
created, and again when any of the sort or filter  properties are modified.

Quindi se è necessario specificare Sort, RowFilter, or RowStateFilter per un DataView è bene farlo nel costruttore.


DATASOURCE  VALIDI

Il DataSource accetta fonti dati di tipo generico Object.
Nel caso di fonti dati complesse che prevedono colonne e righe il Binding deve navigare le righe (e per questo l'enumeratore basta) e determinare quali siano le colonne.
Ci sono due modi in cui il Binding può determinare automaticamente le colonne:
  1. Quando il DataSource implementa l'interfaccia IDataReader la quale a sua volta implementa IDataRecord, il Binding  interroga IDataRecord per sapere quante colonne ci sono, che nome hanno e quale valore ha ogni colonna;
  2. Altrimenti è possibile passare una collezione o un array di oggetti, ogni Property get verrà considerata una colonna, il nome della Property sarà il nome della colonna mentre il valore restituito dal get sarà il valore.
  3. Ovviamente è possibile costruire in memoria una DataTable e popolarla da codice.
In alternativa è possibile indicare esplicitamente una espressione personalizzata di Binding per specificare come accedere al valore di una colonna della riga corrente.



P   a   r   t   e   1          -          P   a   r   t   e       2          -          P   a   r   t   e       3          -          P   a   r   t   e       4