Tutorial de ASP.NET
Vista Previa de ASP.NET 2.0

Enlazando a Bases de Datos

Uno de los tipos de datos más comunes que se suelen representar en aplicaciones Web son datos provenientes de bases de datos SQL, como Microsoft SQL Server, Oracle, o otro almacén de datos OLEDB o ODBC. El control SqlDataSource representa una conexión directa a una base de datos en una aplicación Web, que puede ser usada por los controles de enlazado de datos para obtener los datos de forma automática. Se pretende que SqlDataSource reemplace al código ADO.NET que escribiriamos normalmente para crear una conexión y hacer una petición a una base de datos. Debido a que las peticiones de datos se especificar directamente como propiedades de los controles de fuente de datos, a veces se le llama "modelo de dos capas", ya que las peticiones de datos se mantienen en el código de la página. Por esta razón, el control SqlDataSource está dirigido hacia los sitios pequeños hechos por hobby o personales, que no requieren una encapsulación total de los objetos de datos de nivel medio. En posteriores secciones del tutorial se hablará del control ObjectDataSource, destinado a empresas mayores, que necesitan encapsulación de nivel medio de las peticiones a base de datos.

El Control GridView

Para demostrar como enlazar los datos desde una base de datos, los siguientes ejemplos aprovecharán el nuevo control de enlazado de datos llamado GridView. Dicho control es un nuevo control de enlazado de datos de ASP.NET 2.0 para presentar los datos en un formato de rejilla tabular. Cada fila de la rejilla corresponde a un registro de datos y las columnas representan los campos del registro. Si estáis familiarizados con el control DataGrid de ASP.NET 1.x, el control GridView es el que lo reemplaza y tiene un modelo de objeto muy similar.

El control GridView soporta las siguientes características:
  • Vicular controles de fuente a datos.
  • Capacidades de clasificación.
  • Capacidades de actualización y borrado.
  • Capacidades de paginación.
  • Capacidades de selección de columnas.
  • Acceso mediante código al modelo de objeto GridView para poder establecer las propiedades y manejar los eventos.
  • Nuevos tipos de columnas como CheckBoxField y ImageField.
  • Múltiples campos de datos para las columnas de hiperenlaces.
  • Múltiples campos de datos llaves para selección, actualiación y borrado.
  • Apariencia personalizable a través de temas y estilos.

Creando un Informe de Datos

El tipo más simple que podemos encontrar en una página orientada a objetos es un informe de sólo-lectura, que muestra los datos pero no permiten que el usuario manipule la presentación o modifique los datos. Para crear un informe de sólo-lectura de una base de datos SQL primero hay que configurar un SqlDataSource en la página y después conectar un control enlazado a datos, como por ejemplo el GridView, a la fuente de datos, especificando la propiedad DataSourceID property. El siguiente ejemplo nos muestra un control GridView asociado a un SqlDataSource.

<form runat="server">
<asp:GridView ID="GridView1" DataSourceID="SqlDataSource1" runat="server"/>
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
SelectCommand="SELECT [au_id], [au_lname], [au_fname] FROM [authors]"
ConnectionString="<%$ ConnectionStrings:Pubs %>" />
</form>
La propiedad ConnectionString del SqlDataSource especifica la cadena de conexión a la base de datos y la propiedad SelectCommand especifica la consulta a ejecutar para obtener los datos. La cadena de conexión se puede especificar literalmente en la página, pero en este caso hemos asignado dicha propiedad mediante una nueva expresión que obtiene el valor del fichero Web.config. En el siguiente ejemplo, un control GridView se enlaza a un control SqlDataSource conectado a una base de datos Microsoft™ SQL Server.
C# GridView-SqlDataSource

 
El control SqlDataSource no se limita a conexiones con bases de datos de Microsoft™ SQL Server, sino que en realidad puede conectarse a cualquier proveedor ADO.NET configurado como System.Data.Common.DbProviderFactory. Por defecto, hay cuatro proveedores incluidos en el fichero machine.config del Framewrok .NET.
<configuration>
<system.data>
<DbProviderFactories>
<add name="Odbc Data Provider" invariant="System.Data.Odbc" type="System.Data.Odbc.OdbcFactory, ..." />
<add name="OleDb Data Provider" invariant="System.Data.OleDb" type="System.Data.OleDb.OleDbFactory, ..." />
<add name="OracleClient Data Provider" invariant="System.Data.OracleClient" type="System.Data.OracleClient.OracleClientFactory, ..." />
<add name="SqlClient Data Provider" invariant="System.Data.SqlClient" type="System.Data.SqlClient.SqlClientFactory, ..." />
</DbProviderFactories>
</system.data>
</configuration>
La propiedad ProviderName de SqlDataSource se puede establecer a un nombre invariante de cualquier proveedor (por defecto System.Data.SqlClient). Observar que si cambiamos el nombre de proveedor tendremos que asegurarnos que las propiedades ConnectionString y SelectCommand utilizan la sintaxi correcta para el proveedor seleccionado. 

En el ejemplo anterior, el control GridView se "reflejaba contra" los campos de los registros de datos devueltos por SqlDataSource para generar dinámicamente las columnas de la rejilla. También podemos especificar las columnas explícitas que queremos mostrar añadiendo objetos DataControlField a la colecciónde Columnas del GridView. Esto nos permite especificar exactamente que columnas hay que mostrar y su orden relativo. El siguiente ejemplo muestra una colección de objetos BoundField y CheckBoxField en la colección de Columnas del GridView. Otro tipo de campos que pueden ser asignados a esta colección son ImageField, HyperLinkField, CommandField, ButtonField y TemplateField.
C# GridView-SqlDataSource (BoundFields)


La propiedad SelectCommand de SqlDataSource también se puede configurar con un nombre de procedimiento almacenado en lugar de un comando SQL. Para permitir esto, hay que establecer la propiedad SelectCommandType a "StoredProcedure". El siguiente ejemplo muestra el contro SqlDataSource configurado para seleccionar datos de un procedimiento almacenado en la base de datos Northwind.
C# GridView-SqlDataSource (Procedimiento Almacenado)


Por defecto el control SqlDataSource devuelve un objeto DataView desde un objeto DataSet que contiene los resultados de la consulta. Podemos configurar el control SqlDataSource para devolver los datos como un objeto DataReader en su lugar, estableciendo la propiedad SqlDataSourceMode a "DataReader". Utilizar un DataReader es, generalmente, mejor en cuanto a rendimiento que utilizar un DataSet cuando sólo necesitamos acceso read-only o "fordward-only" a los datos. Sin embargo, es importante observar que la capacidad de clasificación del control SqlDataSource se deshabilitará en este modo. El siguiente ejemplo muestra el modo DataReades del control SqlDataSource.
C# GridView-SqlDataSource (DataReader)

Configuración de Cadenas de Conexión

En los ejemplos anteriores, SqlDataSource hace referencia a la cadena de conexión a la base de datos por su nombre, utilizando la nueva sintaxis declarativa de ASP.NET 2.0 que resuelve el valor de la cadena de conexión en tiempo de ejecución. La cadena de conexión se almacena en el fichero Web.config en la sección de configuración <connectionStrings>, de forma que es sencillo de mantenerlo en un solo lugar para todas las páginas de la aplicación.
<configuration>
<connectionStrings>
<add name="Pubs" connectionString="Server=(local);Integrated Security=True;Database=pubs;"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
El siguiente ejemplo muestra el fichero Web.config utilizado por los anteriores ejemplos de SqlDataSource.
Configuración de Cadenas de Conexión


Almacenar las cadenas de conexión en el fichero Web.config es una práctica recomendada para cualquier aplicación ASP.NET, no sólo para su administración centralizada, sino para hacer más seguras las cadenas de conexión. En ASP.NET 2.0 hay disponible una herramienta de linea de comandos para el encriptado de esta sección para una mayor seguridad en ambientes de producción. Para más de talles sobre el encriptado de cadenas de conexión acudid a la sección "Encriptado de Secciones de Configuración" de la sección de administración de este tutorial. El siguiente ejemplo muestra el fichero Web.config con una sección <connectionStrings/> encriptada.
Configuración de Cadenas de Conexión (Encriptadas)

La propiedad ConnectionString del control SqlDataSource se establece a la expresión <%$ ConnectionStrings:Pubs %>, que es traducida por el analizador de ASP.NET al valor de la cadena de conexión en tiempo de ejecución. También podemos especificar una expresión para la propiedad ProviderName de SqlDataSource, por ejemplo <%$ ConnectionStrings:Pubs.ProviderName %>.

Clasificación y Paginación de Datos

Una de las principales ventajas del control GridView sobre otros controles de enlazado de datos es su habilidad para aprovechar las propiedades de la fuente de datos. En lugar de dejar en manos del código de la página la clasificación o paginación de los datos, el control GridView puede realizar estas operaciones de forma automática, siempre que la fuente de datos esté configurada para ello.

El control SqlDataSource soporta la clasificación cuando la propiedad DataSourceMode se configura como "DataSet". Para permitir clasificación en la UI usando un control GridView hay que configurar la propiedad AllowSorting a "verdadero". Ésto provoca que el control GridView cree botones de enlazado para las cabeceras de sus columnas, en los que podamos hacer clic para clasificar la columna. El control GridView pasa la expresión SortExpression asociada con el campo de la columna al control de la fuente de datos, que devuelve los datos clasificados al GridView.

La sintaxis de SortExpression que espera SqlDataSource es la misma que  la de la propiedad Sort de System.Data.DataView, aunque otras fuentes de datos pueden soportar sintaxis diferentes. Debido a que el comportamiento de clasificación de SqlDataSource depende la propiedad DataViewSort, SqlDataSource sólo soporta la clasificación en modo DataSet; si se configura como DataReader, la clasificación se deshabilita. Normalmente asignaremos el SortExpression a un sólo nombre de campo asociado con una columna del GridView. El GridView alternará de forma automática entre "ASC" o "DESC" en SortExpression en cada clic, para conmutar entre orden de clasificación ascendente o descendente.
C# Clasificación del GridView


También podemos permitir la paginación de la UI en el control GridView, poniendo la propiedad AllowPaging a verdadero. El GridView puede paginar cualquier valor devuelto por una fuente de datos que soporte la interfaz ICollection. El DataView que devuelve SqlDataSource cuando está en en modo DataSet soporta dicha interfaz, de forma que GridView puede (paginar)"page over" el resultado. Cuando se encuentra en mode DataReader, el GridView no puede "page over" los datos devueltos por SqlDataSource. El siguiente ejemplo nos muestra la UI de paginación del GridView con un SqlDataSource en modo DataSet..
C# Paginación de GridView


También podemos personalizar el estilo y la configuración del paginador, configurando las propiedades PagerStyle y PagerSettings, respectivamente. PagerStyle determina el aspecto y la sensación del paginador, mientras que PagerSettings determina el tipo de paginación a usar (numérica o con botones Siguiente/anterior), la posición del paginador y opciones relacionadas. El siguiente ejemplo muestra algunos de estos estilos y ajustes aplicados al paginador de GridView.
C# Ajustes del Paginador de GridView


Observar que la operación de paginación del anterior ejemplo la está realizando íntegramente el control GridView sobre el DataView devuelto por SqlDataSource, que soporta la interfaz ICollection. En este caso, el GridView obtiene todos los datos de la fuente de datos, presenta un subconjunto de las filas y después descarta las restantes. A esto se le llama a veces "paginación UI", porque la lógica de paginación se da en la capa de presentación del control GridView. Aunque sea conveniente para paginar colecciones arbitrarias, ésta no es la forma más eficiente de paginar los datos. también es posible configurar la paginación en el nivel de la interfaz de la fuente de datos, de forma que el GridView solo pide a la fuente de datos las filas que necesita para representar la página. El control SqlDataSource no soporta, por ahora, paginación a nivel de interfaz. El control ObjectDataSource no soporta esta característica, y de esto se habla en el topic "Clasificación y Paginación Avanzadas" de este tutorial.

Actualizando y Borrando Datos

Además de clasificación y paginación, el control GridView también permite preparar la UI para la modificación de los datos mediante operaciones de Actualización y Borrado, siempre que la fuente de datos asociada se configure para soportar dichas funcionalidades. El control SqlDataSource soporta las operaciones de Actualización cuándo se establece la propiedad UpdateCommand y las de Borrado cuando la propiedad DeleteCommand se establece a un comando válido de actualización o borrado o a un procedimiento almacenado. UpdateCommand o DeleteCommand deben contener parámetros de substitución para cada valor que pasará el control GridView (más sobre esto abajo). Tamnbién podemos especificar una colección de UpdateParameters o DeleteParameters para establecer las propiedades de cada parámetro, tales como el tipo de parámetro, la dirección de entrada/salida o el valor por defecto. Se hablará más detalladamente sobre estas colecciones en los siguientes capítulos.

<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:Pubs %>"
SelectCommand="SELECT [au_id], [au_lname], [au_fname], [state] FROM [authors]"
UpdateCommand="UPDATE [authors] SET [au_lname] = @au_lname, [au_fname] = @au_fname, [state] = @state WHERE [au_id] = @au_id"
DeleteCommand="DELETE FROM [authors] WHERE [au_id] = @au_id"/>
Para permitir la Actualización y el Borrado desde la UI en el GridView, podemos establecer las propiedades AutoGenerateEditButton y AutoGenerateDeleteButton a verdadero, o añadir un CommandField al control GridView y habilitar sus propiedades ShowEditButton y ShowDeleteButton. El GridView soporta la edición o borrado de una fila cada vez. Para editar, el usuario pondrña la fila en modo de edición haciendo clic sobre el botón "Edit" y después confirmará la Actualización haciendo clic sobre el botón "Update" mientras la fila esté en modo de edición. El usuario también puede hacer clic sobre el botón "Cancel" para cancelar la operación de edición y volver al modo de sólo-lectura. El siguiente ejemplo muestra el GridView y la SqlDataSource configurados para la actualización de filas de datos.
 

C# Actualización del GridView

Una propiedad importante que juega un papel especial en las operaciones de Actualización y Borrado es la propiedad de DataKeyNames. Esta propiedad se fija al valor de los nombres de los campos de la fuente de datos que forman parte de la llave primaria mediante la que podremos localizar una fila concreta en la fuente de datos. Cuando especificamos esta propiedad de forma declarativa, tendremos que separar por comas los diferentes campos, aunque normalmente se suele tener un sólo campo llave. Los valores de los campos especificados en la propiedad DataKeyNames son de ida y vuelta, para poder mantener los valores originales que tendremos que pasar a las operaciones de Actualización y Borrado, incluso si el campo no se va a presentar como una columna en el control GridView. Cuándo GridView invoca las operaciones de Actualización o Borrado de la fuente de datos, pasa los valores de estos campos en un diccionario de Llaves especiales, diferente al diccionario de Valores que contiene los nuevos valores que ha introducido el usuario mientras la fila estaba en modo de edición (para operaciones de actualización). Los contenidos del diccionario de Valores se obtienen de los controles de entrada presentados en la fila en el modo de edición. Para excluir un valor de este diccionario, tendremos que establecer la propiedad ReadOnly a verdadero en el correspondiente BoundField dentro del grupo de columnas. Si estamos usando el diseñador de GridView de Visual Studio, la propiedad ReadOnly se pone a verdadero por defecto, para los campos de las llaves primarias.

Observar la convención en la nomenclatura de los parámetros en la sentencia de Actualización asignada a UpdateCommand. La capacidad automática de invocación la operación de Actualización que tiene el GridView y otros controles de enlazado de datos depende de la convención de la nomenclatura para su funcionamiento. Los parámetros deben ser nombrados como los valores de los campos asociados devueltos por el SelectCommand. Mediante este acuerdo en la nomenclatura se hace posible alinear los valores pasados por el control de enlazado de datos a la fuente de datos con los parámetros de la sentencia de actualización SQL.

La utilización de esta convención en la nomenclatura asume que el contenido de los diccionarios de Llaves y los de Valores son mutuamente excluyentes (es decir, los valores de los campos que deben ser actualizados por el usuario, mientras que el control de enlazado de datos está en modo de edición, deben nombrarse de forma diferente a los valores de los campos utilizados para encontrar la fila a acualizar (en la cláusula WHERE de SqlDataSource)). Otra forma de ver ésto es que cualquier campo que se encuentre en DataKeyNames deberá ser de sólo-lectura o invisible en el control de enlace de datos (por ejemplo, en el conjunto de Columnas de GridView).

A pesar que es común que los campos llave sean de sólo-lectura, hay casos en los que querremos que se puedan actualizar campos que también se usan para encontrar la fila de datos a actualizar. Por ejemplo, si establecemos la propiedad ReadOnly=false en un campo del conjunto de Columnas de GridView que también forma parte de DataKeyNames, GridView pasará el antiguo valor al campo correspondiente del diccionario de Llaves, mientras que pasará el nuevo valor al campo del diccionario de Valores. Para diferenciar entre estos dos valores, necesitaremos nombrar los parámetros de forma diferente en la sentencia SQL, por ejemplo:
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:Pubs %>"
SelectCommand="SELECT [au_id], [au_lname], [au_fname], [state] FROM [authors]"
UpdateCommand="UPDATE [authors] SET [au_id] = @au_id, [au_lname] = @au_lname, [au_fname] = @au_fname, [state] = @state WHERE [au_id] = @original_au_id"
DeleteCommand="DELETE FROM [authors] WHERE [au_id] = @original_au_id"/>
OldValuesParameterFormatString="original_{0}"
En este ejemplo, el parámetro de nombre @original_au_id se usa para hacer referencia al valor original del campo llave y @au_id para el nuevo valor. La propiedad OldValuesParameterFormatString de SqlDataSource también se establece a un formato de string válido para el Framework .NET para indicar cómo deben ser renombrados los parámetros en el diccionario de Llaves. Este formato también se aplica a los viejos valores de los campos que no son llave que pasa el control de enlace de datos cuando la propiedad ConflictDetection de SqlDataSource se establece a CompareAllValues. En las operaciones de Borrado, SqlDataSource aplica el diccionario de Llaves por defecto (no hay nuevos valores para la operación de borrado), usando el valor de la propiedad OldValuesParameterFormatString para dar formato a los nombres de los parámetros llave.

Filtrado de Datos

Un escenario común en las páginas orientadas a datos es la habilidad de filtrar los datos en un informe. Por ejemplo, supongamos que un usuario pueda seleccionar entre unos valores de una DropDownList para filtrar la cuadrícula del informe de manera que sólo se muestren las filas que coincidan con el valor del campo. En ASP.NET 1.x necesitábamos escribir todo este código:
  1. Cancelar el enlace de datos en el Page_Load si la consulta es un postback
  2. Manejar el evento SelectedIndexChanged
  3. Añadir el SelectedValue de la DropDownList a la colección de Parámetros de los comandos
  4. Ejecutar el comando y llamar a DataBind
En ASP.NET 2.0 se elimina este código mediante el uso de objetos Data Parameter declarativos. Un "data parameter" permite que los valores externos se asocien a operaciones de la fuente de datos de forma declarada. Estos parámetros normalmente se asocian a una variable en un comando o propiedad, por ejemplo, un parámetro de una sentecia SQL o un procedimiento almacenado para SqlDataSource. Los controles de la fuente de datos permiten acceder a las grupo de propiedades de parámetros que contienen los objetos paramétricos para cada operación de datos soportada. Por ejemplo:
<asp:DropDownList ID="DropDownList1" ... runat="server"/>
...
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:Pubs %>"
SelectCommand="SELECT [au_id], [au_lname], [au_fname], [state] FROM [authors] WHERE [state] = @state">
<SelectParameters>
<asp:ControlParameter Name="state" ControlID="DropDownList1" PropertyName="SelectedValue" />
</SelectParameters>
</asp:SqlDataSource>
El siguiente ejemplo nos muestra un QueryStringUtilizado para obtener un valor paramétrico del querystring del URL de consulta:
C# Filtrado por QueryString


El siguiente ejemplo nos muestra como un ControlParameter se usa para obtener el valor de un parámetro de un control DropDownList de la página:
C# Filtrado por DropDownList

Podemos configurar los parámetros de datos para devolver valores de cualquiera de las siguientes fuentes:

Nombre Descripción
  Parameter
La clase Parameter es la base común de la que derivan el resto de tipos de Parámetros. La clase Parameter también siver como implementación de parámetros estáticos, dónde el valor se especifica de forma estática mediante la propiedad DefaultValue.

Parameters comparte la propiedad Name común, que es el nombre del parámetro para el funcionamiento de la fuente de datos (por ejemplo, esto encontrará el nombre del parámetro en el SelectCommand para SqlDataSource). Todos los parámetros comparten, también, la propiedad Type, que especifica cuál es el tipo del valor del parámetro.

Los parámetros también comparten la propiedad Direction, que se usa para especificar dónde usamos el parámetro como entrada, salida (o ReturnValue) o ambos, entrada y salida. Las fuentes de datos suelen mostrar los parámetros de salida y devolver valores de un evento "args" que se pasa al evento de estado de operación de la fuente de datos. Para un ejemplo de lo aquí descrito ir a "Trabajando con Parámetros".

  QueryStringParameter
La clase QueryStringParameter enlaza el valor de un campo querystring al valor del objeto Parameter. La propiedad QueryStringField encuentra el nombre del campo querystring desde el que se recupera el valor. La propiedad DefaultValue se devolverá siempre que el valor querystring no esté disponible.

  ControlParameter
La clase ControlParameter enlaza el valor de una propiedad Control al valor de un objeto Parameter. La propiedad ControlID encuentra la ID del Control cuya propiedad está enlazada al parámetro. La PropertyName específica la propiedad del control desde la que se obtiene el valor. El control del cuál especificamos la ID mediante ControlID puede definir, opcionalmente, un ControlValuePropertyAttribute, que determina el nombre de propiedad por defecto del que obtendremos el valor del control. Esta propiedad se utilizará cuando no se establezca explícitamente el PropertyName. El ControlValuePropertyAttribute se aplica a las siguientes propiedades de control:
  • Label.Text
  • TextBox.Text
  • ListControl.SelectedValue (por ejemplo, DropDownList)
  • CheckBox.Checked
  • Calendar.SelectedDate
  • DetailsView.SelectedValue
  • GridView.SelectedValue
  • TreeView.SelectedValue
  • FileUpload.FileBytes
  SessionParameter
La clase SessionParameter enlaza el valor de un objeto de Session con el valor de un objeto Parameter. La propiedad SessionField encuentra el nombre de la clave se Session desde la que se obtiene el valor. La propiedad DefaultValue se devolvera si no se puede acceder al valor de Session.

  FormParameter
La clase FormParameter vinvula el valor de un campo de un formulario HTML al valor de un objeto Parameter. La propiedad FormField encuentra el nombre del campo del formulario desde el que se obtendrá el valor. La propiedad DefaultValue se devolverá cuando el valor de Form no esté disponible.

  CookieParameter
La clase CookieParameter enlaza el valor de un HttpCookie al valor de un objeto Parameter. La propiedad CookieName encuentra el nombre de la cookie desde la que se obtendrá el valor (sólo se admiten cookies de valor simple "simple-valued"). La propiedad DefaultValue se devolverá cuando la cookie no esté disponible.

  ProfileParameter
La clase ProfileParameter enlaza el valor de un objeto "User Profile" al de un objeto Parameter. La propiedad ParameterName encuentra el nombre del perfil desde el que se obtendrá el valor. La propiedad DefaultValue se devolverá cuando la cookie no esté disponible. Para más información, acudir a la sección "Perfiles de Usuario" del tutorial.

Observar la diferencia entre los parámetros de datos que se evaluan en una fuente interna (Control QueryString, etc.) y los que se pasan para las operaciones Actualizar, Insertar y Borrar de los ejemplos anteriores. En el último escenario, los valores de los parámetros son proporcionados dinámicamente por el control de enlace de datos, en este caso GridView, qué invoca la operación de Actualización. Para las operaciones de Actualización, Inserción y Borrado normalmente no necesitaremos parámetros de datos asciados a valores externos. Sin embargo, podemos incluir un objeto <asp:Parameter> (clase base para todos los parámetros de datos) en los grupos UpdateParameters, InsertParameters o DeleteParameters de la fuente de datos, para especificar propiedades como Type, Direction o DefaultValue (valor a usar si el que pasa GridView es null), para ser aplicadas a los valores de los parámetros pasados desde el control GridView.

Cacheando Datos

Otra característica de la fuente de datos es la capacidad de chachear los datos de forma automática. A pesar que seguimos podiendo utilizar las API's de caché para chachear los datos mediante programación, estableciendo unas pocas características de forma declarativa en la fuente de datos podemos obtener el mismo resultado. Para permitir el cacheo para el control SqlDataSource (y ObjectDataSource, que se explica más adelante), tendremos que fijar la propiedad EnableCaching a verdadero.Podemos especificar el tiempo (en segundos) durante el que almacenaremos una entrada en caché, mediante la propiedad CacheDuration.  También podemos  establecer la propiedad CacheExpirationPolicy tanto a either Sliding como a Absolute, como podemos hacer desde la API de caché. El cachero sólo es soportado por el control SqlDataSource cuándo la propiedad DataSourceMode se fija a "DataSet".

Por ejemplo, si fijamos CacheDuration a 5 y SlidingExpiration a Absolute, el SqlDataSource recuperará los datos de la base de datos en la primera petición a la página, y los almacenará en la caché. Para las peticiones siguientes, la SqlDataSource intentará obtener la entrada de la caché para responder la petición sin tener que mirar en la base de datos. Después de 5 segundos (o quizá antes, si la presión de la memoria caché es alta), la entrada de la caché se eliminará y para la siguiente petición el SqlDataSource tendrá que volver a la base de datos de nuevo (repitiendo el proceso de cacheo para los nuevos datos).

Si en lugar de eso fijamos CacheDuration a 5 y SlidingExpiration a Sliding, se refrescará el time-to-live de los datos cacheados periódicamente mientras la fuente de datos los pida al menos una vez cada 5 segundos. Si una página pide los datos cacheados por lo menos una vez cada 5 segundos, y no hay presión en la memoria cache, los datos cacheados se mantendrán en la cache para siempre. Por otra parte, si no se hacen peticiones de los datos cacheados en un periodo de 5 segundos, se eliminarán los datos de la caché y la próxima vez que se produzca una petición el control SqlDataSource volverá a pedir los datos a la base de datos original.

El siguiente ejemplo muestra el cacheo mediante el control SqlDataSource. La columna TimeStamp se actualiza en cada petición, de forma que podemos ver la asiduidad con la que los datos se cogen de la base de datos frente a los que se piden de la caché. Observar que aproximádamente  cada 5 segundos se actualiza el TimeStamp.
C# "Caheando" SqlDataSource

 
Un observador minucioso puede haberse dado cuenta que el TimeStamp también se actualiza cada vez que se selecciona un nuevo valor para el filtro de la DropDownlist. Ésto se debe a que cada conjunto de parámetros único que se le pasa al SelectCommand tiene como resultado una petición diferente a la base de datos y, poe consiguiente, una entrada diferente en la caché (observar que si seleccionamos el mismo valor en la DropDownList en un período de tiempo de 5 segundos el TimeStamp no varía).

Un enfoque alternativo, que funciona bien con peticiones de datos más pequeños, consiste en seleccionar todos los datos de la base de datos y pasarlos a la caché, para luego filtrar esa  única entrada de la caché para los diferentes valores de los parámetros. Para soportar ésto, el control SqlDataSource soporta la propiedad FilterExpression y el grupo correspondiente de FilterParameters. En lugar de aplicar los valores de los parámetros directamente sobre el comando (cómo hacíamos en los SelectParameters), la expresión de filtrado se aplica sobre la propiedad RowFilter del objeto DataView devuelto por la ejecución del comando. La sintaxis de la expresión debe encajar con la esperada para la propiedad RowFilter del DataView. Row Filter

Podemos usar parámetros de substitución para los valores de los parámetros dentro de la FilterExpression siguiendo el estándard del Framework .NET para la sintaxis de formato de strings, por ejemplo "{0}", "{1}" y sucesivamente. en tiempo de ejecución, el control SqlDataSource aplica los valores de los parámetros especificados en el grupo FilterParameters a FilterExpression, dado formato al string con los valores. Observar que los valores de los parámetros no son "escaped", de manera que será necesario que los pongamos entre comillas simples.
<asp:DropDownList ID="DropDownList1" ... runat="server"/>
...
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:Pubs %>"
SelectCommand="SELECT [au_id], [au_lname], [au_fname], [state] FROM [authors]">
FilterExpression="state = '{0}'"
<FilterParameters>
<asp:ControlParameter Name="state" ControlID="DropDownList1" PropertyName="SelectedValue" />
</FilterParameters>
</asp:SqlDataSource>
El siguiente ejemplo muestra esta técnica de filtrado en acción:
C# Filtrando Entradas de Caché


El cacheo de datos debe mantener un compromiso entre rendimiento (no tenemos que volver a la base de datos en cada petición) y los datos obsoletos (porque la entrada de caché contiene una instantánia de los datos capturados en un momento dado). Normalmente usamos valores relativamente pequeños en CacheDuration, para asegurarnos que los datos de la caché están actualizados. Una situación ideal consistiría en invalidar la entrada de la caché únicamente cuándo los datos subyacentes cambiaran. Mientras los datos no hayan cambiado, no hay ninguna razón para eliminar la entrada de la caché.

Una nueva característica de ASP.NET 2.0 llamada SQL Cache Invalidation, nos permite configurar la fuente de datos para cachear los datos de forma indefinida (o por una duración especificada) hasta que los datos de la base de datos cambien, momento en el que la entrada de la caché se elimina. Esta técnica nos permite utilizar valores mucho mayores para CacheDuration y continuar garantizando que los datos que mostramos coinciden con los valores de la base de datos. SQL Cache Invalidation sólo es soportado por las bases de datos Microsoft™ SQL Server. Hay dos implementaciones de esta función: una basada en notificaciones, soportada por SQL Server 2005, y otra basada en votaciones (polling), soportada por versiones anteriores de SQL Server. La sección de SQL Cache Invalidation nos describe los pasos necesarios para configurar ambas implementaciones.

Una vez hemos configurado la SQL Cache Invalidation, podemos utilizarla desde el control de la fuente de datos, especificando la propiedad SqlCacheDependency de la fuente de datos. Si usamos la implementación basada en votaciones, este valor acepta un formato del tipo connectionName:tableName. Si usamos la implementación basada en notificaciones, fijaremos la propiedad a "CommandNotification".

En este ejemplo, fijamos el CacheDuration a "Infinite" y especificamos SqlCacheDependency. Observar que la columna TimeStamp no se actualiza hasta que los datos se modifican mediante el botón "Edit" del GridView.
C# SqlCacheInvalidation de la Fuente de Datos

Master-Details y el Control DetailsView

En la sección de Filtrado de Datos vimos cómo los controles de la fuente de datos pueden aceptar parámetros de fuentes externas, tales como controles de un formulario, valores de querystring, y otros. Una técnica similar se puede emplear para crear un escenario de "master-details". Master-details se suele referir a un "convenio (Arreglo)" entre controles, en el que un registro seleccionado en un control (el control "master") muestra detalles adicionales para el registro seleccionado en otro control (el control "details"). Los detalles adicionales pueden ser propiedades del mismo elemento de datos, o registros relacionados que están asociados al elemento de datos "master" a través de una relación clave externa en la base de datos.

El control GridView soporta la propiedad SelectedValue, qué indica la fila que está seleccionada en el GridView. La propiedad SelectedValue evalúa el valor del primer campo especificado en la propiedad DataKeyNames. Podemos permitir la UI para la selección el el GridView fijando AutoGenerateSelectButton a verdadero, o añadiendo al grupo de columnas del GridView un CommandField con ShowSelectButton fijado a verdadero. Una vez hecho esto, la propiedad SelectedValue del GridView puede ser asociada a un ControlParameter en una fuente de datos para pedir los registros de detalles, de la misma forma que configurábamos el DropDownList en los ejemplos anteriores.

Para mostrar más detalles de la fila que está seleccionada, podemos usar otro control GridView, pero ASP.NET también incluye un nuevo control DetailsView que sólo vale para eso. El control DetailsView presenta un solo registro cada vez, en lugar de un grupo de registros. De la misma forma que el GridView, DetailsView lo presenta en un formato tabulado, a excepción de las filas correspondientes a cada campo de datos (como las columnas GridView). Los campos se especifican en el grupo Fields de DetailsView. Opcionalmente, el control DetailsView también puede paginar un grupo de registros, cómo lo hace GridView (en DetailsView, el PageSize siempre es 1).
C# Master-Details w/ GridView and DetailsView


DetailsView soporta la edición, al igual que lo hacía GridView, y podemos permitir la UI de la misma forma, utilizando las propiedades AutoGenerateEditButton o CommandField.ShowEditButton. Por supuesto, la fuente de datos asociada al DetailsView tiene que ser configurada para soportar la operación de actualización (en este caso, especificando un UpdateCommand en SqlDataSource). El siguiente ejemplo demuestra un DetailsView configurado para soportar la edición de registros en un escenario de master-details.
C# Edición de DetailsView


Normalmente los controles de enlazado de datos re-enlazan de forma automática la fuente de datos cuando cambia la fuente de datos (por ejemplo, después de una actualización). Sin embargo, en el ejemplo anterior, el DetailsView se enlaza a una fuente de datos diferente a la del GridView, de forma que cuando se invoca la operación de actualización, solo el DetailsView recive el evento de cambio de su fuente de datos. Para forzar que el GridView también re-enlace cuándo el DetailsView realiza una actualización, podemos llamar explícitamente al DataBind() del GridView en el evento ItemUpdated del DetailsView. Este ejemplo también maneja eventos para no permitir la edición cuándo una operación de clasificación o paginación del gridView ocurre al mismo tiempo que se selecciona un valor de filtrado en el control DropDownList.

También es común el dividir la visualización del master-details a través de varias páginas de una aplicación Web. Para hacer ésto podemos añadir un hipervínculo a cada fila del GridView para navegar a diferentes páginas de detalles, pasando argumentos mediante el querystring. En la página de detalles, la fuente de datos enlazada al DetailsView aceptará estos argumentos mediante un objeto QueryStringParameter.

Un hipervínculo deberá ser añadido al GridView añadiendo un objeto HyperLinkField al grupo de Columnas de GridView. La propiedad Text del HyperLinkField fija el texto a mostrar en el hipervínculo (por ejemplo "View Details..."), mientras que la propiedad NavigateUrl especifica la URL dónde navegaremos al hacer clic sobre el enlace. En lugar de especificar una URL estática para todas las filas, es más común especificar NavigateUrlFields para ser usado en la construcción de una URL dinámica. Se puede fijar NavigateUrlFields de forma declarativa a un conjunto de campos separados por comas, de la fuente de datos. La propiedad NavigateUrlFormatString especifica el formato del estándard del Framework .NET para la URL, mediante parámetros de substicutción cómo {0} y {1} para substituis los valores del campo, en tiempo de ejecución.

Este ejemplo muestra un escenario master-details utilizando GridVIew y DetailsVIew en páginas separadas. A diferencia de los ejemplos anteriores, qué mostraban el GridView y el DetailsView enlaados a el mismo tipo de registro (un "author"), este ejemplo muestra diferentes tipos de registros para los controles "master" y "details" ("author" y "books"), saociados por relaciones clave externas en la base de datos. Debido a que un registro de un autor puede tener más de un libro asociado, se ha configurado el DetailsView para soportar paginación en los registros de libros, en la página de detalles.
C# Master-Details (Páginas Separadas)

Insertando Datos

Al igual que el control GridView, el control DetailsView soporta la Actualización y el Borrado de datos en su fuente de datos. Sin embargo, DetailsView también soporta la inserción de datos, cosa que no hacía GridView. Podemos emparejar de forma sencilla un DetailsView con un GridView para permitir que se vean los registros de inserción en el GridView.

Para permitir que la SqlDataSource soporte Inserciones, tenemos que fijar la propiedad InsertCommand a un comando válido de inserción, con parámetros de substitución para el valor de cada campo que es representado por el DetailsView en el modo de Inserción. También podemos, de forma opcional, especificar un grupo de InsertParameters que contengan los objetos de parámetros de datos para esta operación.

Para permitir la inserción en la UI, hay que fijar la propiedad AutoGenerateInsertButton a verdadero o añadir al grupo de campos de DetailsView un CommandField con ShowInsertButton establecido a verdadero. Para pasar el DetailsView a modo de inserción, tenemos que hacer clic en el botón clic. DetailsView representará controles de entrada para cada campo cuándo estemos en el modo de inserción. Observar que los campos marcados como "ReadOnly" se representan como controles de entrada en el modo de Inserción (a pesar que no lo harían en modo Actualización). Para excluir un campo en el modo de Inserción, tendremos que establecer la propiedad InsertVisible del campo a falso. Para realizar la operación de Inserción, hay que hacer clic en el botón "Insert" mientras estamos en el modo de inserción. Para abortar la inserción, hacer clic en el botón "Cancel".

Cuando se ha llevado a cabo una operación de inserción, el DetailsView recoge los valores de sus entradas y llena un diccionario de Valores que se pasará a la fuente de datos. El SqlDataSource aplica estos valores al grupo de parámetros de InsertCommand antes de ejecutar el comando. De la misma forma que con las Actualizaciones, la capacidad de inserción automática recae en parámetros en el InserCommand, que se llamn exactamente de la misma forma que los campos que se devuelven en la operación de selección. Observar que el diccionario de Claves no se requiere para la inserción.
C# Inserción en Master-Details


Podemos situar el DetailsView en una página separada para realizar las operaciones de Inserción o Actualización. El siguiente ejemplo muestra un DetailsView configurado en una página separada para realizar las Inserciones y Actualizaciones. Observar que la propiedad DefaultMode se ha fijado en el ejemplo a Insert o Edit, de forma que DetailsView se representará inicialmente en este modo, en lugar de el modo sólo-lectura. Después de una Inserción o una Actualización, DetailsView siempre vuelve al DefaultMode (por defecto ReadOnly).
C# Inserción en Master-Details (Páginas Separadas)