Efectuando Acceso a Datos

65
Efectuando Acceso a Datos Lo nuevo en 2.0 Controles de Fuente de Datos - ASP.NET 2.0 introduce controles declarativos de fuente de datos que hacen accesibles los datos de almacenes internos (bases de datos SQL, objetos de negocios de capa intermedia o ficheros XML) a los controles de la interfaz de usuario (UI), para el enlazado a datos en una página. Los controles de fuente de datos también tienen otras capacidades como la clasificación, la paginación, el "catching", la actualización, la inserción y la eliminación de datos, que los controles de la UI puede aprovechar sin requerir ningún tipo de código. Nuevos Controles de Enlazado a Datos - Además de los controles de enlazado a datos de ASP.NET 1.x, ASP.NET incluye nuevos controles de UI de enlazado a datos como el GridView, DetailsView,FormView, TreeView y Menu, que se pueden personalizar para mostrar los datos en diferentes formatos. Los controles GridView, DetailsView y FormView pueden, además, aprovechar las capacidades de la fuente de datos, haciendo así más sencillo las operaciones de clasificación, paginación y actualización en páginas orientadas a datos (data-driven). Parámetros de Control de Datos - Las fuentes de datos pueden aceptar parámetros de entrada de diferentes fuentes utilizando los nuevos "objetos de parámetros" de controles de datos de ASP.NET 2.0. Estos "objetos de parámetros" nos permiten proporcionar fácilmente los valores de las propiedades de los controles de servidor (campos sesión, aplicación, cookie y querystring) y las propiedades del perfil de usuario para operaciones de datos parametrizadas. La utilización de estos parámetros permite el filtrado y escenarios de "master-details" con poco código o código personalizado. Sintaxis de Enlazado a Datos Mejorada - La sintaxis de enlazado a datos de DataBinder.Eval en ASP.NET se ha simplificado para el escenario común de enlazar un control en una plantilla enlazada a datos. Es posible, además, asociar dos formas de enlazar datos con las propiedades de un control en una plantilla para permitir, de esta forma, que los valores se pasen automáticamente para la actualización, inserción o borrado en la fuente de datos. Para datos XML jerárquicos ASP.NET 2.0 también incluye una sintaxis de enlazado a datos basado en XPath. Bases de Datos de Ficheros Locales Utilizando SQL Express - Para un desarrollo más sencillo, ASP.NET 2.0 soporta la habilidad de conectar con una base de datos SQL Express como un fichero local de la aplicación, eliminando la necesidad de enviar la base de datos a un servidor sólo para realizar el trabajo de desarrollo. Por supuesto, también podemos seguir conectándonos a las bases de datos de un servidor SQL. Esta sección describe éstas y otras características del acceso a datos en ASP.NET 2.0. Prácticamente todas las aplicaciones Web dinámicas realizan algun tipo de acceso a datos y, afortunadamente, ASP.NET 2.0 facilita mucho esta acción. A diferencia de ASP.NET 1.0, que requería que los desarrolladores escribieran código personalizado para recuperar y enlazar

Transcript of Efectuando Acceso a Datos

Page 1: Efectuando Acceso a Datos

Efectuando Acceso a DatosLo nuevo en 2.0

Controles de Fuente de Datos - ASP.NET 2.0 introduce controles declarativos de fuente de datos que

hacen accesibles los datos de almacenes internos (bases de datos SQL, objetos de negocios de capa

intermedia o ficheros XML) a los controles de la interfaz de usuario (UI), para el enlazado a datos en una

página. Los controles de fuente de datos también tienen otras capacidades como la clasificación, la

paginación, el "catching", la actualización, la inserción y la eliminación de datos, que los controles de la UI

puede aprovechar sin requerir ningún tipo de código.

Nuevos Controles de Enlazado a Datos - Además de los controles de enlazado a datos de ASP.NET

1.x, ASP.NET incluye nuevos controles de UI de enlazado a datos como el GridView, DetailsView,FormView,

TreeView y Menu, que se pueden personalizar para mostrar los datos en diferentes formatos. Los controles

GridView, DetailsView y FormView pueden, además, aprovechar las capacidades de la fuente de datos,

haciendo así más sencillo las operaciones de clasificación, paginación y actualización en páginas orientadas

a datos (data-driven).

Parámetros de Control de Datos - Las fuentes de datos pueden aceptar parámetros de entrada de

diferentes fuentes utilizando los nuevos "objetos de parámetros" de controles de datos de ASP.NET 2.0.

Estos "objetos de parámetros" nos permiten proporcionar fácilmente los valores de las propiedades de los

controles de servidor (campos sesión, aplicación, cookie y querystring) y las propiedades del perfil de

usuario para operaciones de datos parametrizadas. La utilización de estos parámetros permite el filtrado y

escenarios de "master-details" con poco código o código personalizado.

Sintaxis de Enlazado a Datos Mejorada - La sintaxis de enlazado a datos de DataBinder.Eval en

ASP.NET se ha simplificado para el escenario común de enlazar un control en una plantilla enlazada a

datos. Es posible, además, asociar dos formas de enlazar datos con las propiedades de un control en una

plantilla para permitir, de esta forma, que los valores se pasen automáticamente para la actualización,

inserción o borrado en la fuente de datos. Para datos XML jerárquicos ASP.NET 2.0 también incluye una

sintaxis de enlazado a datos basado en XPath.

Bases de Datos de Ficheros Locales Utilizando SQL Express - Para un desarrollo más sencillo,

ASP.NET 2.0 soporta la habilidad de conectar con una base de datos SQL Express como un fichero local de

la aplicación, eliminando la necesidad de enviar la base de datos a un servidor sólo para realizar el trabajo

de desarrollo. Por supuesto, también podemos seguir conectándonos a las bases de datos de un servidor

SQL.

Esta sección describe éstas y otras características del acceso a datos en ASP.NET 2.0.

Prácticamente todas las aplicaciones Web dinámicas realizan algun tipo de acceso a datos y,

afortunadamente, ASP.NET 2.0 facilita mucho esta acción. A diferencia de ASP.NET 1.0, que requería que los

desarrolladores escribieran código personalizado para recuperar y enlazar los datos a controles de servidor,

ASP.NET 2.0 permite una solución declarativa para el enlazado a datos que requiere poco código para los

escenarios de datos más comunes, como por ejemplo:

Seleccionar y Mostrar Datos

Ordenar, Paginar y Cachear Datos

Page 2: Efectuando Acceso a Datos

Actualizar, Insertar y Borrar Datos

Filtrar o "Master-Details" Utilizando Parámetros

ASP.NET 2.0 introduce dos tipos de controles de servidor que participan en este modelo declarativo de

enlazado a datos. Estos dos tipos de controles de datos manejan toda la complejidad del modelo Web para

escenarios de datos, de forma que los desarrolladores no tienen que entender los eventos del ciclo de vida de

las peticiones para realizar el enlazado de los datos. Otro beneficio de este modelo basado en controles es que

se puede extender de forma sencilla para soportar el acceso a datos de otros proveedores.

Controles de Fuente de Datos

Los controles de fuente de datos no se representan, sino que, en lugar de eso, representan un almacén de datos, como por ejemplo una base de datos, un objeto de negocio, un fichero XML o un Web Service XML. Los controles de fuente de datos también hacen posibles funconalidades más "ricas" sobre los datos (clasificación, paginación, filtrado, actualización, borrado e inserción) que pueden utilizar los controles de enlazado a datos de la UI de forma automática. ASP.NET incluye los siguientes controles de servidor, por defecto:

Nombre DescripciónSqlDataSource Permite enlazar a una base de datos SQL representada por un proveedor ADO.NET, como por ejemplo

Microsoft™ SQL Server, OLEDB, ODBC, o Oracle.ObjectDataSource Permite enlazar a un objeto de capa intermedia como los de capa de acceso a datos o un componente

de negocios.AccessDataSource Permite enlazar a una base de datos Microsoft™ Access (Jet).SiteMapDataSource Permite enlazar a la jerarquia mostrada por un proveedor de navegación de sites de ASP.NET 2.0.XmlDataSource Permite enlazar a un fichero o documento XML.

Controles de Enlazado de Datos

Los controles de enlazado de datos son controles de UI que renderizan los datos como marcas para los

dispositivos o navegadores clientes. Un control de enlazado de datos puede auto-enlazar una fuente de datos

a un dato mostrado y traerá los datos en el momento apropiado dentro del ciclo de vida de la página. Estos

controles puedes aprovecharse, opcionalmente, de las capacidades de la fuente de datos, como por ejemplo

clasificación, paginación, filtrado, actualizado, borrado y inserción. Un control de enlazado de datos se conecta con una fuente de datos a través de su propiedad DataSourceID. Podemos estar familiarizados con algunos de los controles de enlazado de datos de ASP.NET 1.x, como por ejemplo DataGrid, DataList, Repeater, y controles de lista como DropDownList. ASP.NET 2.0 contiene varios controles de enlazado de datos nuevos, como:

Nombre DescripciónGridView Presenta los datos en formato de "rejilla" (grid). Es una evolución del control DataGrid, y puede aprovechar

automáticamente las características de la fuente de datos.DetailsView Presenta un sólo elemento en una tabla de parejas etiqueta/valor, similar a la vista de formulario de Microsoft™

Access. Este control también puede aprovechar automáticamente las características de la fuente de datos.FormView Presenta de la forma definida en una platilla personalizada un sólo elemento de datos. Presenta un elemento en

una tabla de parejas etiqueta/valor, similar a la vista de formulario de Microsoft™ Access. Este control también puede aprovechar automáticamente las características de la fuente de datos.

TreeView Presenta los datos en una vista de árbol jerárquico de nodos expandibles.Menu Presenta los datos en un menú dinámico expandible (incluyendo flyouts).

Esta sección demuestra éstas y otras características nuevas de ASP.NET 2.0.

Page 3: Efectuando Acceso a Datos

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

Page 4: Efectuando Acceso a Datos

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.

GridViewSimple_vb.aspx GridView-SqlDataSource

<%@ Page Language="VB" %><html><head runat="server"> <title>GridView Bound to SqlDataSource</title></head><body> <form id="form1" runat="server"> <asp:GridView ID="GridView1" DataSourceID="SqlDataSource1" runat="server" /> <asp:SqlDataSource ID="SqlDataSource1" runat="server" SelectCommand="SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors]" ConnectionString="<%$ ConnectionStrings:Pubs %>" /> </form></body></html>

Web.config

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" />

Page 5: Efectuando Acceso a Datos

--> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

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

Page 6: Efectuando Acceso a Datos

Columnas del GridView. Otro tipo de campos que pueden ser asignados a esta colección son ImageField,

HyperLinkField, CommandField, ButtonField y TemplateField.

GridViewBoundFields_vb.aspx

<%@ Page Language="VB" %><html><head runat="server"> <title>GridView Bound Fields</title></head><body> <form id="form1" runat="server"> <asp:GridView ID="GridView1" DataSourceID="SqlDataSource1" AutoGenerateColumns="False" runat="server"> <Columns> <asp:BoundField HeaderText="ID" DataField="au_id" ReadOnly="true" /> <asp:BoundField HeaderText="Last Name" DataField="au_lname" /> <asp:BoundField HeaderText="First Name" DataField="au_fname" /> <asp:BoundField HeaderText="Phone" DataField="phone" /> <asp:BoundField HeaderText="Address" DataField="address" /> <asp:BoundField HeaderText="City" DataField="city" /> <asp:BoundField HeaderText="State" DataField="state" /> <asp:BoundField HeaderText="Zip Code" DataField="zip" /> <asp:CheckBoxField HeaderText="Contract" DataField="contract" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" runat="server" SelectCommand="SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors]" ConnectionString="<%$ ConnectionStrings:Pubs %>" /> </form></body></html>

Web.config

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> -->

Page 7: Efectuando Acceso a Datos

<add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

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.

GridViewStoredProc_vb.aspx (BoundFields)

<%@ Page Language="VB" %><html><head runat="server"> <title>GridView Bound to Stored Procedure</title></head><body> <form id="form1" runat="server"> <asp:GridView ID="GridView1" DataSourceID="SqlDataSource1" AutoGenerateColumns="False" runat="server"> <Columns> <asp:BoundField DataField="TenMostExpensiveProducts" HeaderText="Product" /> <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" HeaderText="Price" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" runat="server" SelectCommand="Ten Most Expensive Products" ConnectionString="<%$ ConnectionStrings:Northwind %>" SelectCommandType="StoredProcedure" /> </form></body></html>

Page 8: Efectuando Acceso a Datos

Web.config

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

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.

GridView-SqlDataSource (Procedimiento Almacenado)

GridViewStoredProc_vb.aspx<%@ Page Language="VB" %><html><head runat="server"> <title>GridView Bound to Stored Procedure</title></head><body> <form id="form1" runat="server">

Page 9: Efectuando Acceso a Datos

<asp:GridView ID="GridView1" DataSourceID="SqlDataSource1" AutoGenerateColumns="False" runat="server"> <Columns> <asp:BoundField DataField="TenMostExpensiveProducts" HeaderText="Product" /> <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" HeaderText="Price" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" runat="server" SelectCommand="Ten Most Expensive Products" ConnectionString="<%$ ConnectionStrings:Northwind %>" SelectCommandType="StoredProcedure" /> </form></body></html>

Web.config

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching>

Page 10: Efectuando Acceso a Datos

</system.web></configuration>

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.

GridView-SqlDataSource (DataReader)

GridViewDataReader_vb.aspx

<%@ Page Language="VB" %><html><head runat="server"> <title>GridView Bound to SqlDataReader</title></head><body> <form id="form1" runat="server"> <asp:GridView ID="GridView1" DataSourceID="SqlDataSource1" AutoGenerateColumns="False" runat="server"> <Columns> <asp:BoundField DataField="TenMostExpensiveProducts" HeaderText="Product" /> <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" HeaderText="Price" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:Northwind %>" SelectCommand="Ten Most Expensive Products" SelectCommandType="StoredProcedure" DataSourceMode="DataReader" /> </form></body></html>

Web.config

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" />

Page 11: Efectuando Acceso a Datos

<add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

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

Web.config.strings

<?xml version="1.0"?><configuration> <connectionStrings> <add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" />

Page 12: Efectuando Acceso a Datos

<add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> </system.web></configuration>

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)

Web.config.encrypted

<?xml version="1.0"?><configuration> <protectedData> <protectedDataSections> <add name="connectionStrings" provider="RsaProtectedConfigurationProvider" inheritedByChildren="false" /> </protectedDataSections> </protectedData> <connectionStrings> <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns="http://www.w3.org/2001/04/xmlenc#"> <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" /> <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#"> <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" /> <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> <KeyName>Rsa Key</KeyName> </KeyInfo> <CipherData> <CipherValue>408EYuABn0MbVxxN9CAHMFpkX36odWkolxVw0neiuBQ888tZbpf8S7TWfpWDC4uz000uhH0FuR3iYVQkbEZ0gEU+y5zDcVp3uVzzryr7P4h/fm1oH0RnDPeKcegAkkq1HaX0tu5c5wcFmIi2Qs4pTR8kjn30kYBfBIUg5AsdZCo=</CipherValue> </CipherData> </EncryptedKey> </KeyInfo> <CipherData> <CipherValue>5lkgSgwPTYDx6OYlRxaSLZhJwhGgssjmtljt+5Fo3hYt+Mu35oN4L4ODoiFKXpL3Kkd5+E+hABZRSJyRQPvwa26+8YGK5tV03xTjMs4rQkTh55514jv85i+bmZjNLmbw41u8QyEYEo30uu2yChdNAjh3TEHuths9nDoqhkTzfn0sx4Hyd2xukFd037c7ZZYrTIsB+EWX9nhFXhC3tTdQrateyiOHiRgyA5tWKXea86u/IUyZhGyC2w3A/PbfJ+oAJHSRAh0YqR+mGlJDcGypwPv7laAXV0VN1H5dFzCgmF5KsUOLyBAMAk/TGPaoXJGsdTjM2zrEzx1/8PITLU/x6IDSN2V53DRn3uHDMoHXBa6n2V3giyxpAitC/

Page 13: Efectuando Acceso a Datos

A02qDYPxuuDdPSCS7DALh2FF4FhrcoXQVtKWq+yy3bsQG7wNbQ5b5wCopuoLnjGcPK3BAvyC43krxbyUDBzk8ZIJvo6/FDJn3Uz2mbDquFu80yfM3YyqLV/VFuk6PXQXgF/YcbszKUiBlybSMw5KE2ieWWBiVrWExWayAGw</CipherValue> </CipherData> </EncryptedData> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> </system.web></configuration>

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.

Clasificación del GridView

GridViewSorting_vb.aspx

<%@ Page Language="VB" %><html><head runat="server"> <title>Sorting Data Using GridView</title></head><body> <form id="form1" runat="server">

Page 14: Efectuando Acceso a Datos

<asp:GridView ID="GridView1" AllowSorting="true" runat="server" DataSourceID="SqlDataSource1" AutoGenerateColumns="False"> <Columns> <asp:BoundField HeaderText="ID" DataField="au_id" SortExpression="au_id" /> <asp:BoundField HeaderText="Last Name" DataField="au_lname" SortExpression="au_lname" /> <asp:BoundField HeaderText="First Name" DataField="au_fname" SortExpression="au_fname" /> <asp:BoundField HeaderText="Phone" DataField="phone" SortExpression="phone" /> <asp:BoundField HeaderText="Address" DataField="address" SortExpression="address" /> <asp:BoundField HeaderText="City" DataField="city" SortExpression="city" /> <asp:BoundField HeaderText="State" DataField="state" SortExpression="state" /> <asp:BoundField HeaderText="Zip Code" DataField="zip" SortExpression="zip" /> <asp:CheckBoxField HeaderText="Contract" SortExpression="contract" DataField="contract" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" runat="server" SelectCommand="SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors]" ConnectionString="<%$ ConnectionStrings:Pubs %>" /> </form></body></html>

Web.config

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;"

Page 15: Efectuando Acceso a Datos

providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

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

Paginación de GridView

GridViewPaging_vb.aspx

<%@ Page Language="VB" %><html><head runat="server"> <title>Paging Data Using GridView</title></head><body> <form id="form1" runat="server"> <asp:GridView ID="GridView1" AllowSorting="true" AllowPaging="true" runat="server" DataSourceID="SqlDataSource1" AutoGenerateColumns="False"> <Columns> <asp:BoundField HeaderText="ID" DataField="au_id" SortExpression="au_id" /> <asp:BoundField HeaderText="Last Name" DataField="au_lname" SortExpression="au_lname" /> <asp:BoundField HeaderText="First Name" DataField="au_fname" SortExpression="au_fname" /> <asp:BoundField HeaderText="Phone" DataField="phone" SortExpression="phone" /> <asp:BoundField HeaderText="Address" DataField="address" SortExpression="address" /> <asp:BoundField HeaderText="City" DataField="city" SortExpression="city" /> <asp:BoundField HeaderText="State" DataField="state" SortExpression="state" /> <asp:BoundField HeaderText="Zip Code" DataField="zip" SortExpression="zip" /> <asp:CheckBoxField HeaderText="Contract" SortExpression="contract" DataField="contract" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" runat="server"

Page 16: Efectuando Acceso a Datos

SelectCommand="SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors]" ConnectionString="<%$ ConnectionStrings:Pubs %>" /> </form></body></html>

Web.config

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

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.

Ajustes del Paginador de GridView

Page 17: Efectuando Acceso a Datos

GridViewPagingSettings_vb.aspx

<%@ Page Language="VB" %><html><head runat="server"> <title>Pager Settings and Styles</title></head><body> <form id="form1" runat="server"> <asp:GridView ID="GridView1" AllowSorting="true" AllowPaging="true" PageSize="3" runat="server" DataSourceID="SqlDataSource1" AutoGenerateColumns="False"> <PagerSettings Mode="NextPreviousFirstLast" Position="TopAndBottom" FirstPageImageUrl="~/Images/First.gif" LastPageImageUrl="~/Images/Last.gif" NextPageImageUrl="~/Images/Next.gif" PreviousPageImageUrl="~/Images/Prev.gif" /> <PagerStyle ForeColor="White" HorizontalAlign="Right" BackColor="#284775" /> <Columns> <asp:BoundField HeaderText="ID" DataField="au_id" SortExpression="au_id" /> <asp:BoundField HeaderText="Last Name" DataField="au_lname" SortExpression="au_lname" /> <asp:BoundField HeaderText="First Name" DataField="au_fname" SortExpression="au_fname" /> <asp:BoundField HeaderText="Phone" DataField="phone" SortExpression="phone" /> <asp:BoundField HeaderText="Address" DataField="address" SortExpression="address" /> <asp:BoundField HeaderText="City" DataField="city" SortExpression="city" /> <asp:BoundField HeaderText="State" DataField="state" SortExpression="state" /> <asp:BoundField HeaderText="Zip Code" DataField="zip" SortExpression="zip" /> <asp:CheckBoxField HeaderText="Contract" SortExpression="contract" DataField="contract" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" runat="server" SelectCommand="SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors]" ConnectionString="<%$ ConnectionStrings:Pubs %>" /> </form></body></html>

Web.config

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True"

Page 18: Efectuando Acceso a Datos

providerName="System.Data.SqlClient" /> <add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

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

Page 19: Efectuando Acceso a Datos

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.

Actualización del GridView

GridViewUpdating_vb.aspx

<%@ Page Language="VB" %><html> <head runat="server"> <title>Updating Data Using GridView</title> </head> <body> <form id="form1" runat="server"> <asp:GridView ID="GridView1" AllowSorting="true" AllowPaging="true" Runat="server" DataSourceID="SqlDataSource1" AutoGenerateEditButton="true" DataKeyNames="au_id" AutoGenerateColumns="False"> <Columns> <asp:BoundField ReadOnly="true" HeaderText="ID" DataField="au_id" SortExpression="au_id" /> <asp:BoundField HeaderText="Last Name" DataField="au_lname" SortExpression="au_lname" /> <asp:BoundField HeaderText="First Name" DataField="au_fname" SortExpression="au_fname" /> <asp:BoundField HeaderText="Phone" DataField="phone" SortExpression="phone" /> <asp:BoundField HeaderText="Address" DataField="address" SortExpression="address" /> <asp:BoundField HeaderText="City" DataField="city" SortExpression="city" /> <asp:BoundField HeaderText="State" DataField="state" SortExpression="state" /> <asp:BoundField HeaderText="Zip Code" DataField="zip" SortExpression="zip" /> <asp:CheckBoxField HeaderText="Contract" SortExpression="contract" DataField="contract" />

Page 20: Efectuando Acceso a Datos

</Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" Runat="server" SelectCommand="SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors]" UpdateCommand="UPDATE [authors] SET [au_lname] = @au_lname, [au_fname] = @au_fname, [phone] = @phone, [address] = @address, [city] = @city, [state] = @state, [zip] = @zip, [contract] = @contract WHERE [au_id] = @au_id" ConnectionString="<%$ ConnectionStrings:Pubs %>" /> </form> </body></html>

Web.config

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

Page 21: Efectuando Acceso a Datos

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

Page 22: Efectuando Acceso a Datos

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:

Filtrado por QueryString

GridViewQueryString_vb.aspx<%@ Page Language="VB" %><html> <head runat="server"> <title>Filtering Data In A GridView Using a QueryString</title> </head> <body> <form id="form1" runat="server"> <h4>Enter a query string on the URL, for example: GridViewQueryString_vb.aspx?state=IN</h4>

Page 23: Efectuando Acceso a Datos

<asp:GridView ID="GridView1" AllowSorting="True" AllowPaging="True" Runat="server" DataSourceID="SqlDataSource1" AutoGenerateEditButton="True" DataKeyNames="au_id" AutoGenerateColumns="False"> <Columns> <asp:BoundField ReadOnly="true" HeaderText="ID" DataField="au_id" SortExpression="au_id" /> <asp:BoundField HeaderText="Last Name" DataField="au_lname" SortExpression="au_lname" /> <asp:BoundField HeaderText="First Name" DataField="au_fname" SortExpression="au_fname" /> <asp:BoundField HeaderText="Phone" DataField="phone" SortExpression="phone" /> <asp:BoundField HeaderText="Address" DataField="address" SortExpression="address" /> <asp:BoundField HeaderText="City" DataField="city" SortExpression="city" /> <asp:BoundField HeaderText="State" DataField="state" SortExpression="state" /> <asp:BoundField HeaderText="Zip Code" DataField="zip" SortExpression="zip" /> <asp:CheckBoxField HeaderText="Contract" SortExpression="contract" DataField="contract" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" Runat="server" SelectCommand="SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors] WHERE [state] = @state" UpdateCommand="UPDATE [authors] SET [au_lname] = @au_lname, [au_fname] = @au_fname, [phone] = @phone, [address] = @address, [city] = @city, [state] = @state, [zip] = @zip, [contract] = @contract WHERE [au_id] = @au_id" ConnectionString="<%$ ConnectionStrings:Pubs %>"> <SelectParameters> <asp:QueryStringParameter Name="state" QueryStringField="state" DefaultValue="CA" /> </SelectParameters> <UpdateParameters> <asp:Parameter Name="au_lname" /> <asp:Parameter Name="au_fname" /> <asp:Parameter Name="phone" /> <asp:Parameter Name="address" /> <asp:Parameter Name="city" /> <asp:Parameter Name="state" /> <asp:Parameter Name="zip" /> <asp:Parameter Name="contract" /> <asp:Parameter Name="au_id" /> </UpdateParameters> </asp:SqlDataSource> </form> </body></html>

Web.config

<?xml version="1.0"?>

Page 24: Efectuando Acceso a Datos

<configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

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:

Filtrado por DropDownList

GridViewDropDownList_vb.aspx

<%@ Page Language="VB" %><html> <head runat="server"> <title>Filtering Data In A GridView Using a DropDownList</title> </head> <body> <form id="form1" runat="server"> <b>Choose a state:</b> <asp:DropDownList ID="DropDownList1" DataSourceID="SqlDataSource2" AutoPostBack="true" DataTextField="state" Runat="server" /> <asp:SqlDataSource ID="SqlDataSource2" Runat="server" SelectCommand="SELECT DISTINCT [state] FROM [authors]" ConnectionString="<%$ ConnectionStrings:Pubs %>" />

Page 25: Efectuando Acceso a Datos

<br /> <br /> <asp:GridView ID="GridView1" AllowSorting="True" AllowPaging="True" Runat="server" DataSourceID="SqlDataSource1" AutoGenerateEditButton="True" DataKeyNames="au_id" AutoGenerateColumns="False"> <Columns> <asp:BoundField ReadOnly="true" HeaderText="ID" DataField="au_id" SortExpression="au_id" /> <asp:BoundField HeaderText="Last Name" DataField="au_lname" SortExpression="au_lname" /> <asp:BoundField HeaderText="First Name" DataField="au_fname" SortExpression="au_fname" /> <asp:BoundField HeaderText="Phone" DataField="phone" SortExpression="phone" /> <asp:BoundField HeaderText="Address" DataField="address" SortExpression="address" /> <asp:BoundField HeaderText="City" DataField="city" SortExpression="city" /> <asp:BoundField HeaderText="State" DataField="state" SortExpression="state" /> <asp:BoundField HeaderText="Zip Code" DataField="zip" SortExpression="zip" /> <asp:CheckBoxField HeaderText="Contract" SortExpression="contract" DataField="contract" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" Runat="server" SelectCommand="SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors] WHERE [state] = @state" UpdateCommand="UPDATE [authors] SET [au_lname] = @au_lname, [au_fname] = @au_fname, [phone] = @phone, [address] = @address, [city] = @city, [state] = @state, [zip] = @zip, [contract] = @contract WHERE [au_id] = @au_id" ConnectionString="<%$ ConnectionStrings:Pubs %>"> <SelectParameters> <asp:ControlParameter Name="state" ControlID="DropDownList1" /> </SelectParameters> <UpdateParameters> <asp:Parameter Name="au_lname" /> <asp:Parameter Name="au_fname" /> <asp:Parameter Name="phone" /> <asp:Parameter Name="address" /> <asp:Parameter Name="city" /> <asp:Parameter Name="state" /> <asp:Parameter Name="zip" /> <asp:Parameter Name="contract" /> <asp:Parameter Name="au_id" /> </UpdateParameters> </asp:SqlDataSource> </form> </body></html>

Web.config

Page 26: Efectuando Acceso a Datos

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

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 Namecomú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".

Page 27: Efectuando Acceso a Datos

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

Page 28: Efectuando Acceso a Datos

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.

"Caheando" SqlDataSource

GridViewCaching_vb.aspx

<%@ Page Language="VB" %><html> <head runat="server"> <title>Caching Data</title> </head> <body> <form id="form1" runat="server"> <b>Choose a state:</b> <asp:DropDownList ID="DropDownList1" DataSourceID="SqlDataSource2" AutoPostBack="true" DataTextField="state" Runat="server" /> <asp:SqlDataSource ID="SqlDataSource2" Runat="server" SelectCommand="SELECT DISTINCT [state] FROM [authors]"

Page 29: Efectuando Acceso a Datos

ConnectionString="<%$ ConnectionStrings:Pubs %>" /> <br /> <br /> <asp:GridView ID="GridView1" AllowSorting="True" AllowPaging="True" Runat="server" DataSourceID="SqlDataSource1" AutoGenerateEditButton="True" DataKeyNames="au_id" AutoGenerateColumns="False"> <Columns> <asp:BoundField DataField="TimeStamp" HeaderText="TimeStamp" ReadOnly="True"> <ItemStyle Font-Bold="True" /> </asp:BoundField> <asp:BoundField ReadOnly="True" HeaderText="ID" DataField="au_id" SortExpression="au_id" /> <asp:BoundField HeaderText="Last Name" DataField="au_lname" SortExpression="au_lname" /> <asp:BoundField HeaderText="First Name" DataField="au_fname" SortExpression="au_fname" /> <asp:BoundField HeaderText="Phone" DataField="phone" SortExpression="phone" /> <asp:BoundField HeaderText="Address" DataField="address" SortExpression="address" /> <asp:BoundField HeaderText="City" DataField="city" SortExpression="city" /> <asp:BoundField HeaderText="State" DataField="state" SortExpression="state" /> <asp:BoundField HeaderText="Zip Code" DataField="zip" SortExpression="zip" /> <asp:CheckBoxField HeaderText="Contract" SortExpression="contract" DataField="contract" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" Runat="server" SelectCommand="SELECT DatePart(second, GetDate()) As TimeStamp, [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors] WHERE [state] = @state" UpdateCommand="UPDATE [authors] SET [au_lname] = @au_lname, [au_fname] = @au_fname, [phone] = @phone, [address] = @address, [city] = @city, [state] = @state, [zip] = @zip, [contract] = @contract WHERE [au_id] = @au_id" ConnectionString="<%$ ConnectionStrings:Pubs %>" CacheDuration="5" EnableCaching="True"> <SelectParameters> <asp:ControlParameter Name="state" ControlID="DropDownList1" /> </SelectParameters> <UpdateParameters> <asp:Parameter Name="au_lname" /> <asp:Parameter Name="au_fname" /> <asp:Parameter Name="phone" /> <asp:Parameter Name="address" /> <asp:Parameter Name="city" /> <asp:Parameter Name="state" /> <asp:Parameter Name="zip" /> <asp:Parameter Name="contract" /> <asp:Parameter Name="au_id" /> </UpdateParameters> </asp:SqlDataSource>

Page 30: Efectuando Acceso a Datos

</form> </body></html>Web.config

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

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

Page 31: Efectuando Acceso a Datos

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:

Filtrando Entradas de Caché

GridViewCachingFilter_vb.aspx

<%@ Page Language="VB" %><html> <head runat="server"> <title>Filtering Cached Data</title> </head> <body> <form id="form1" runat="server"> <b>Choose a state:</b> <asp:DropDownList ID="DropDownList1" DataSourceID="SqlDataSource2" AutoPostBack="true" DataTextField="state" Runat="server" /> <asp:SqlDataSource ID="SqlDataSource2" Runat="server" SelectCommand="SELECT DISTINCT [state] FROM [authors]" ConnectionString="<%$ ConnectionStrings:Pubs %>" /> <br /> <br /> <asp:GridView ID="GridView1" AllowSorting="True" AllowPaging="True" Runat="server" DataSourceID="SqlDataSource1" AutoGenerateEditButton="True" DataKeyNames="au_id" AutoGenerateColumns="False"> <Columns> <asp:BoundField DataField="TimeStamp" HeaderText="TimeStamp" ReadOnly="True"> <ItemStyle Font-Bold="True" /> </asp:BoundField> <asp:BoundField ReadOnly="True" HeaderText="ID" DataField="au_id" SortExpression="au_id" />

Page 32: Efectuando Acceso a Datos

<asp:BoundField HeaderText="Last Name" DataField="au_lname" SortExpression="au_lname" /> <asp:BoundField HeaderText="First Name" DataField="au_fname" SortExpression="au_fname" /> <asp:BoundField HeaderText="Phone" DataField="phone" SortExpression="phone" /> <asp:BoundField HeaderText="Address" DataField="address" SortExpression="address" /> <asp:BoundField HeaderText="City" DataField="city" SortExpression="city" /> <asp:BoundField HeaderText="State" DataField="state" SortExpression="state" /> <asp:BoundField HeaderText="Zip Code" DataField="zip" SortExpression="zip" /> <asp:CheckBoxField HeaderText="Contract" SortExpression="contract" DataField="contract" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" Runat="server" SelectCommand="SELECT DatePart(second, GetDate()) As TimeStamp, [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors]" UpdateCommand="UPDATE [authors] SET [au_lname] = @au_lname, [au_fname] = @au_fname, [phone] = @phone, [address] = @address, [city] = @city, [state] = @state, [zip] = @zip, [contract] = @contract WHERE [au_id] = @au_id" ConnectionString="<%$ ConnectionStrings:Pubs %>" CacheDuration="5" EnableCaching="True" FilterExpression="state='{0}'"> <UpdateParameters> <asp:Parameter Name="au_lname" /> <asp:Parameter Name="au_fname" /> <asp:Parameter Name="phone" /> <asp:Parameter Name="address" /> <asp:Parameter Name="city" /> <asp:Parameter Name="state" /> <asp:Parameter Name="zip" /> <asp:Parameter Name="contract" /> <asp:Parameter Name="au_id" /> </UpdateParameters> <FilterParameters> <asp:ControlParameter ControlID="DropDownList1" Name="state" PropertyName="SelectedValue" /> </FilterParameters> </asp:SqlDataSource> </form> </body></html>Web.config

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" />

Page 33: Efectuando Acceso a Datos

<add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

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

Page 34: Efectuando Acceso a Datos

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.

SqlCacheInvalidation de la Fuente de Datos

GridViewCachingSql_vb.aspx

<%@ Page Language="VB" %><html> <head runat="server"> <title>Data Sources and SqlCacheInvalidation</title> </head> <body> <form id="form1" runat="server"> <b>Choose a state:</b> <asp:DropDownList ID="DropDownList1" DataSourceID="SqlDataSource2" AutoPostBack="true" DataTextField="state" Runat="server" /> <asp:SqlDataSource ID="SqlDataSource2" Runat="server" SelectCommand="SELECT DISTINCT [state] FROM [authors]" ConnectionString="<%$ ConnectionStrings:Pubs %>" /> <br /> <br /> <asp:GridView ID="GridView1" AllowSorting="True" AllowPaging="True" Runat="server" DataSourceID="SqlDataSource1" AutoGenerateEditButton="True" DataKeyNames="au_id" AutoGenerateColumns="False"> <Columns> <asp:BoundField DataField="TimeStamp" HeaderText="TimeStamp" ReadOnly="True"> <ItemStyle Font-Bold="True" /> </asp:BoundField> <asp:BoundField ReadOnly="True" HeaderText="ID" DataField="au_id" SortExpression="au_id" /> <asp:BoundField HeaderText="Last Name" DataField="au_lname" SortExpression="au_lname" /> <asp:BoundField HeaderText="First Name" DataField="au_fname" SortExpression="au_fname" /> <asp:BoundField HeaderText="Phone" DataField="phone" SortExpression="phone" /> <asp:BoundField HeaderText="Address" DataField="address" SortExpression="address" /> <asp:BoundField HeaderText="City" DataField="city" SortExpression="city" /> <asp:BoundField HeaderText="State" DataField="state" SortExpression="state" /> <asp:BoundField HeaderText="Zip Code" DataField="zip" SortExpression="zip" /> <asp:CheckBoxField HeaderText="Contract" SortExpression="contract" DataField="contract" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" Runat="server" SelectCommand="SELECT DatePart(second, GetDate()) As TimeStamp, [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors]"

Page 35: Efectuando Acceso a Datos

UpdateCommand="UPDATE [authors] SET [au_lname] = @au_lname, [au_fname] = @au_fname, [phone] = @phone, [address] = @address, [city] = @city, [state] = @state, [zip] = @zip, [contract] = @contract WHERE [au_id] = @au_id" ConnectionString="<%$ ConnectionStrings:Pubs %>" EnableCaching="True" FilterExpression="state='{0}'" SqlCacheDependency="Pubs:Authors"> <UpdateParameters> <asp:Parameter Name="au_lname" /> <asp:Parameter Name="au_fname" /> <asp:Parameter Name="phone" /> <asp:Parameter Name="address" /> <asp:Parameter Name="city" /> <asp:Parameter Name="state" /> <asp:Parameter Name="zip" /> <asp:Parameter Name="contract" /> <asp:Parameter Name="au_id" /> </UpdateParameters> <FilterParameters> <asp:ControlParameter ControlID="DropDownList1" Name="state" PropertyName="SelectedValue" /> </FilterParameters> </asp:SqlDataSource> </form> </body></html>

Web.config

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching>

Page 36: Efectuando Acceso a Datos

<sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

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

Master-Details w/ GridView and DetailsView

GridViewMasterDetails_vb.aspx

<%@ Page Language="VB" %><html><head runat="server"> <title>GridView DetailsView Master-Details (1 Page)</title></head><body> <form id="form1" runat="server"> <b>Choose a state:</b> <asp:DropDownList ID="DropDownList1" DataSourceID="SqlDataSource2" AutoPostBack="true" DataTextField="state" runat="server" />

Page 37: Efectuando Acceso a Datos

<asp:SqlDataSource ID="SqlDataSource2" runat="server" SelectCommand="SELECT DISTINCT [state] FROM [authors]" ConnectionString="<%$ ConnectionStrings:Pubs %>" /> <br /> <br /> <table> <tr> <td valign="top"> <asp:GridView ID="GridView1" AllowSorting="True" AllowPaging="True" runat="server" DataSourceID="SqlDataSource1" DataKeyNames="au_id" AutoGenerateColumns="False" Width="427px"> <Columns> <asp:CommandField ShowSelectButton="True" /> <asp:BoundField DataField="au_id" HeaderText="au_id" ReadOnly="True" SortExpression="au_id" /> <asp:BoundField DataField="au_lname" HeaderText="au_lname" SortExpression="au_lname" /> <asp:BoundField DataField="au_fname" HeaderText="au_fname" SortExpression="au_fname" /> <asp:BoundField DataField="state" HeaderText="state" SortExpression="state" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" runat="server" SelectCommand="SELECT [au_id], [au_lname], [au_fname], [state] FROM [authors] WHERE ([state] = @state)" ConnectionString="<%$ ConnectionStrings:Pubs %>"> <SelectParameters> <asp:ControlParameter ControlID="DropDownList1" Name="state" PropertyName="SelectedValue" Type="String" /> </SelectParameters> </asp:SqlDataSource> </td> <td valign="top"> <asp:DetailsView AutoGenerateRows="False" DataKeyNames="au_id" DataSourceID="SqlDataSource3" HeaderText="Author Details" ID="DetailsView1" runat="server" Width="275px"> <Fields> <asp:BoundField DataField="au_id" HeaderText="au_id" ReadOnly="True" SortExpression="au_id" /> <asp:BoundField DataField="au_lname" HeaderText="au_lname" SortExpression="au_lname" /> <asp:BoundField DataField="au_fname" HeaderText="au_fname" SortExpression="au_fname" /> <asp:BoundField DataField="phone" HeaderText="phone" SortExpression="phone" /> <asp:BoundField DataField="address" HeaderText="address" SortExpression="address" /> <asp:BoundField DataField="city" HeaderText="city" SortExpression="city" /> <asp:BoundField DataField="state" HeaderText="state" SortExpression="state" /> <asp:BoundField DataField="zip" HeaderText="zip" SortExpression="zip" />

Page 38: Efectuando Acceso a Datos

<asp:CheckBoxField DataField="contract" HeaderText="contract" SortExpression="contract" /> </Fields> </asp:DetailsView> <asp:SqlDataSource ConnectionString="<%$ ConnectionStrings:Pubs %>" ID="SqlDataSource3" runat="server" SelectCommand="SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors] WHERE ([au_id] = @au_id)"> <SelectParameters> <asp:ControlParameter ControlID="GridView1" Name="au_id" PropertyName="SelectedValue" Type="String" /> </SelectParameters> </asp:SqlDataSource> </td> </tr> </table> <br /> </form></body></html>

Web.config

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases>

Page 39: Efectuando Acceso a Datos

<add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

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.

Edición de DetailsView

GridViewMasterDetailsEdit_vb.aspx

<%@ Page Language="VB" %><html><head id="Head1" runat="server"> <title>GridView DetailsView Master-Details (with Editing)</title></head><script runat="server">

Protected Sub DetailsView1_ItemUpdated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DetailsViewUpdatedEventArgs) GridView1.DataBind() DropDownList1.DataBind() End Sub

Protected Sub GridView1_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs) DetailsView1.ChangeMode(DetailsViewMode.ReadOnly) End Sub

Protected Sub GridView1_Sorted(ByVal sender As Object, ByVal a As EventArgs) DetailsView1.ChangeMode(DetailsViewMode.ReadOnly) End Sub

Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs) DetailsView1.ChangeMode(DetailsViewMode.ReadOnly) End Sub Protected Sub GridView1_PageIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) DetailsView1.ChangeMode(DetailsViewMode.ReadOnly) End Sub

</script><body> <form id="form1" runat="server"> <b>Choose a state:</b>

Page 40: Efectuando Acceso a Datos

<asp:DropDownList ID="DropDownList1" DataSourceID="SqlDataSource2" AutoPostBack="true" DataTextField="state" runat="server" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged" /> <asp:SqlDataSource ID="SqlDataSource2" runat="server" SelectCommand="SELECT DISTINCT [state] FROM [authors]" ConnectionString="<%$ ConnectionStrings:Pubs %>" /> <br /> <br /> <table> <tr> <td valign="top"> <asp:GridView ID="GridView1" AllowSorting="True" AllowPaging="True" runat="server" DataSourceID="SqlDataSource1" DataKeyNames="au_id" AutoGenerateColumns="False" Width="427px" OnSelectedIndexChanged="GridView1_SelectedIndexChanged" OnSorted="GridView1_Sorted" OnPageIndexChanged="GridView1_PageIndexChanged"> <Columns> <asp:CommandField ShowSelectButton="True" /> <asp:BoundField DataField="au_id" HeaderText="au_id" ReadOnly="True" SortExpression="au_id" /> <asp:BoundField DataField="au_lname" HeaderText="au_lname" SortExpression="au_lname" /> <asp:BoundField DataField="au_fname" HeaderText="au_fname" SortExpression="au_fname" /> <asp:BoundField DataField="state" HeaderText="state" SortExpression="state" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" runat="server" SelectCommand="SELECT [au_id], [au_lname], [au_fname], [state] FROM [authors] WHERE ([state] = @state)" ConnectionString="<%$ ConnectionStrings:Pubs %>"> <SelectParameters> <asp:ControlParameter ControlID="DropDownList1" Name="state" PropertyName="SelectedValue" Type="String" /> </SelectParameters> </asp:SqlDataSource> </td> <td valign="top"> <asp:DetailsView AutoGenerateRows="False" DataKeyNames="au_id" OnItemUpdated="DetailsView1_ItemUpdated" DataSourceID="SqlDataSource3" HeaderText="Author Details" ID="DetailsView1" runat="server" Width="275px"> <Fields> <asp:BoundField DataField="au_id" HeaderText="au_id" ReadOnly="True" SortExpression="au_id" /> <asp:BoundField DataField="au_lname" HeaderText="au_lname" SortExpression="au_lname" /> <asp:BoundField DataField="au_fname" HeaderText="au_fname" SortExpression="au_fname" /> <asp:BoundField DataField="phone" HeaderText="phone" SortExpression="phone" /> <asp:BoundField DataField="address" HeaderText="address" SortExpression="address" />

Page 41: Efectuando Acceso a Datos

<asp:BoundField DataField="city" HeaderText="city" SortExpression="city" /> <asp:BoundField DataField="state" HeaderText="state" SortExpression="state" /> <asp:BoundField DataField="zip" HeaderText="zip" SortExpression="zip" /> <asp:CheckBoxField DataField="contract" HeaderText="contract" SortExpression="contract" /> <asp:CommandField ShowEditButton="True" /> </Fields> </asp:DetailsView> <asp:SqlDataSource ID="SqlDataSource3" runat="server" ConnectionString="<%$ ConnectionStrings:Pubs %>" SelectCommand="SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors] WHERE ([au_id] = @au_id)" UpdateCommand="UPDATE [authors] SET [au_lname] = @au_lname, [au_fname] = @au_fname, [phone] = @phone, [address] = @address, [city] = @city, [state] = @state, [zip] = @zip, [contract] = @contract WHERE [au_id] = @au_id"> <SelectParameters> <asp:ControlParameter ControlID="GridView1" Name="au_id" PropertyName="SelectedValue" Type="String" /> </SelectParameters> <UpdateParameters> <asp:Parameter Name="au_lname" Type="String" /> <asp:Parameter Name="au_fname" Type="String" /> <asp:Parameter Name="phone" Type="String" /> <asp:Parameter Name="address" Type="String" /> <asp:Parameter Name="city" Type="String" /> <asp:Parameter Name="state" Type="String" /> <asp:Parameter Name="zip" Type="String" /> <asp:Parameter Name="contract" Type="Boolean" /> <asp:Parameter Name="au_id" Type="String" /> </UpdateParameters> </asp:SqlDataSource> </td> </tr> </table> <br /> </form></body></html>

Web.config

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" />

Page 42: Efectuando Acceso a Datos

<add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

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.

Page 43: Efectuando Acceso a Datos

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.

Master-Details (Páginas Separadas)

GridViewMasterDetailsPage_vb.aspx

<%@ Page Language="VB" %><html> <head runat="server"> <title>GridView DetailsView Master-Details (2 Page)</title> </head> <body> <form id="form1" runat="server"> <b>Choose a state:</b> <asp:DropDownList ID="DropDownList1" DataSourceID="SqlDataSource2" AutoPostBack="true" DataTextField="state" Runat="server" /> <asp:SqlDataSource ID="SqlDataSource2" Runat="server" SelectCommand="SELECT DISTINCT [state] FROM [authors]" ConnectionString="<%$ ConnectionStrings:Pubs %>" /> <br /> <br /> <asp:GridView ID="GridView1" AllowSorting="True" AllowPaging="True" Runat="server" DataSourceID="SqlDataSource1" DataKeyNames="au_id" AutoGenerateColumns="False"> <Columns> <asp:BoundField ReadOnly="true" HeaderText="ID" DataField="au_id" SortExpression="au_id" /> <asp:BoundField HeaderText="Last Name" DataField="au_lname" SortExpression="au_lname" /> <asp:BoundField HeaderText="First Name" DataField="au_fname" SortExpression="au_fname" /> <asp:BoundField HeaderText="Phone" DataField="phone" SortExpression="phone" /> <asp:BoundField HeaderText="Address" DataField="address" SortExpression="address" /> <asp:BoundField HeaderText="City" DataField="city" SortExpression="city" /> <asp:BoundField HeaderText="State" DataField="state" SortExpression="state" /> <asp:BoundField HeaderText="Zip Code" DataField="zip" SortExpression="zip" /> <asp:CheckBoxField HeaderText="Contract" SortExpression="contract" DataField="contract" /> <asp:HyperLinkField HeaderText="View Details..." Text="View Details..." DataNavigateUrlFields="au_id" DataNavigateUrlFormatString="DetailsView_vb.aspx?ID={0}" /> </Columns> </asp:GridView>

Page 44: Efectuando Acceso a Datos

<asp:SqlDataSource ID="SqlDataSource1" Runat="server" SelectCommand="SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors] WHERE [state] = @state" ConnectionString="<%$ ConnectionStrings:Pubs %>"> <SelectParameters> <asp:ControlParameter Name="state" ControlID="DropDownList1" /> </SelectParameters> </asp:SqlDataSource> </form> </body></html>

DetailsView_vb.aspx

<%@ Page Language="VB" %><html> <head runat="server"> <title>DetailsView</title>

</head> <body> <form id="form1" runat="server"> <asp:DetailsView ID="DetailsView1" Runat="server" DataSourceID="SqlDataSource1" AutoGenerateRows="False" HeaderText="Books for Author" AllowPaging="True"> <Fields> <asp:BoundField HeaderText="au_id" DataField="au_id" SortExpression="au_id" /> <asp:BoundField HeaderText="title_id" DataField="title_id" SortExpression="title_id" /> <asp:BoundField HeaderText="title" DataField="title" SortExpression="title" /> <asp:BoundField HeaderText="type" DataField="type" SortExpression="type" /> <asp:BoundField HeaderText="price" DataField="price" SortExpression="price" /> <asp:BoundField HeaderText="notes" DataField="notes" SortExpression="notes" /> </Fields> </asp:DetailsView> <asp:SqlDataSource ID="SqlDataSource1" Runat="server" SelectCommand="SELECT dbo.authors.au_id, dbo.titles.title_id, dbo.titles.title, dbo.titles.type, dbo.titles.price, dbo.titles.notes FROM dbo.authors INNER JOIN dbo.titleauthor ON dbo.authors.au_id = dbo.titleauthor.au_id INNER JOIN dbo.titles ON dbo.titleauthor.title_id = dbo.titles.title_id WHERE (dbo.authors.au_id = @au_id)" ConnectionString="<%$ ConnectionStrings:Pubs %>"> <SelectParameters> <asp:QueryStringParameter Name="au_id" DefaultValue="213-46-8915" QueryStringField="ID" /> </SelectParameters> </asp:SqlDataSource> </form> </body></html>

Page 45: Efectuando Acceso a Datos

Web.config

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

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.

Page 46: Efectuando Acceso a Datos

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.

Inserción en Master-Details

GridViewMasterDetailsInsert_vb.aspx

<%@ Page Language="VB" %><script runat="server">

Protected Sub DetailsView1_ItemInserted(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DetailsViewInsertedEventArgs) If (Not e.Exception Is Nothing) Then

ErrorMessageLabel.Text = "An error occured while entering this record. Please verify you have entered data in the correct format." e.ExceptionHandled = True End If GridView1.DataBind() End Sub

Protected Sub DetailsView1_ItemUpdated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DetailsViewUpdatedEventArgs) GridView1.DataBind() End Sub

Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs) DetailsView1.ChangeMode(DetailsViewMode.ReadOnly) End Sub

Protected Sub GridView1_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs) DetailsView1.ChangeMode(DetailsViewMode.ReadOnly) End Sub

Page 47: Efectuando Acceso a Datos

Protected Sub GridView1_PageIndexChanged(ByVal sender As Object, ByVal e As EventArgs) DetailsView1.ChangeMode(DetailsViewMode.ReadOnly) End Sub

Protected Sub GridView1_Sorted(ByVal sender As Object, ByVal e As System.EventArgs) DetailsView1.ChangeMode(DetailsViewMode.ReadOnly) End Sub Protected Sub GridView1_RowDeleted(ByVal sender As Object, ByVal e As GridViewDeletedEventArgs) If (Not e.Exception Is Nothing) Then ErrorMessageLabel.Text = "Failed to DELETE due to foreign key contstraint on the table. You may only delete rows which have no related records." e.ExceptionHandled = True End If End Sub

Protected Sub DetailsView1_DataBound(ByVal sender As Object, ByVal e As EventArgs) If (DetailsView1.CurrentMode = DetailsViewMode.Insert) Then Dim stateTextBox As TextBox = CType(DetailsView1.Rows(6).Cells(1).Controls(0), TextBox) stateTextBox.Text = DropDownList1.SelectedValue stateTextBox.Enabled = False End If End Sub

</script><html><head id="Head1" runat="server"> <title>GridView DetailsView Master-Details (Insert)</title></head><body> <form id="form1" runat="server"> <b>Choose a state:</b> <asp:DropDownList ID="DropDownList1" DataSourceID="SqlDataSource2" AutoPostBack="true" DataTextField="state" runat="server" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged" /> <asp:SqlDataSource ID="SqlDataSource2" runat="server" SelectCommand="SELECT DISTINCT [state] FROM [authors]" ConnectionString="<%$ ConnectionStrings:Pubs %>" /> <br /> <br /> <table> <tr> <td valign="top"> <asp:GridView ID="GridView1" AllowSorting="True" AllowPaging="True" runat="server" DataSourceID="SqlDataSource1" DataKeyNames="au_id"

Page 48: Efectuando Acceso a Datos

AutoGenerateColumns="False" Width="500px" SelectedIndex="0" OnSelectedIndexChanged="GridView1_SelectedIndexChanged" OnPageIndexChanged="GridView1_PageIndexChanged" OnRowDeleted="GridView1_RowDeleted" OnSorted="GridView1_Sorted"> <Columns> <asp:CommandField ShowSelectButton="true" ShowDeleteButton="true" /> <asp:BoundField DataField="au_id" HeaderText="au_id" ReadOnly="True" SortExpression="au_id" /> <asp:BoundField DataField="au_lname" HeaderText="au_lname" SortExpression="au_lname" /> <asp:BoundField DataField="au_fname" HeaderText="au_fname" SortExpression="au_fname" /> <asp:BoundField DataField="state" HeaderText="state" SortExpression="state" /> </Columns> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:Pubs %>" SelectCommand="SELECT [au_id], [au_lname], [au_fname], [state] FROM [authors] WHERE ([state] = @state)" DeleteCommand="DELETE FROM [authors] WHERE [au_id] = @au_id"> <SelectParameters> <asp:ControlParameter ControlID="DropDownList1" Name="state" PropertyName="SelectedValue" Type="String" /> </SelectParameters> </asp:SqlDataSource> </td> <td valign="top"> <asp:DetailsView AutoGenerateRows="False" DataKeyNames="au_id" DataSourceID="SqlDataSource3" HeaderText="Author Details" ID="DetailsView1" runat="server" Width="275px" OnItemUpdated="DetailsView1_ItemUpdated" OnItemInserted="DetailsView1_ItemInserted" OnDataBound="DetailsView1_DataBound"> <Fields> <asp:BoundField DataField="au_id" HeaderText="au_id" ReadOnly="True" SortExpression="au_id" /> <asp:BoundField DataField="au_lname" HeaderText="au_lname" SortExpression="au_lname" /> <asp:BoundField DataField="au_fname" HeaderText="au_fname" SortExpression="au_fname" /> <asp:BoundField DataField="phone" HeaderText="phone" SortExpression="phone" /> <asp:BoundField DataField="address" HeaderText="address" SortExpression="address" /> <asp:BoundField DataField="city" HeaderText="city" SortExpression="city" /> <asp:BoundField DataField="state" HeaderText="state" SortExpression="state" /> <asp:BoundField DataField="zip" HeaderText="zip" SortExpression="zip" /> <asp:CheckBoxField DataField="contract" HeaderText="contract" SortExpression="contract" /> <asp:CommandField ShowEditButton="True" ShowInsertButton="True" /> </Fields> </asp:DetailsView>

Page 49: Efectuando Acceso a Datos

<asp:SqlDataSource ID="SqlDataSource3" runat="server" ConnectionString="<%$ ConnectionStrings:Pubs %>" SelectCommand="SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors] WHERE ([au_id] = @au_id)" UpdateCommand="UPDATE [authors] SET [au_lname] = @au_lname, [au_fname] = @au_fname, [phone] = @phone, [address] = @address, [city] = @city, [state] = @state, [zip] = @zip, [contract] = @contract WHERE [au_id] = @au_id" InsertCommand="INSERT INTO [authors] ([au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract]) VALUES (@au_id, @au_lname, @au_fname, @phone, @address, @city, @state, @zip, @contract)"> <SelectParameters> <asp:ControlParameter ControlID="GridView1" Name="au_id" PropertyName="SelectedValue" Type="String" /> </SelectParameters> <UpdateParameters> <asp:Parameter Name="au_lname" Type="String" /> <asp:Parameter Name="au_fname" Type="String" /> <asp:Parameter Name="phone" Type="String" /> <asp:Parameter Name="address" Type="String" /> <asp:Parameter Name="city" Type="String" /> <asp:Parameter Name="state" Type="String" /> <asp:Parameter Name="zip" Type="String" /> <asp:Parameter Name="contract" Type="Boolean" /> <asp:Parameter Name="au_id" Type="String" /> </UpdateParameters> <InsertParameters> <asp:Parameter Name="au_id" Type="String" /> <asp:Parameter Name="au_lname" Type="String" /> <asp:Parameter Name="au_fname" Type="String" /> <asp:Parameter Name="phone" Type="String" /> <asp:Parameter Name="address" Type="String" /> <asp:Parameter Name="city" Type="String" /> <asp:Parameter Name="state" Type="String" /> <asp:Parameter Name="zip" Type="String" /> <asp:Parameter Name="contract" Type="Boolean" /> </InsertParameters> </asp:SqlDataSource> </td> </tr> </table> <br /> <asp:Label ID="ErrorMessageLabel" EnableViewState="false" runat="server" /> </form></body></html>

Web.config

<?xml version="1.0"?><configuration> <connectionStrings> <!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

Page 50: Efectuando Acceso a Datos

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

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

Inserción en Master-Details (Páginas Separadas)

GridViewMasterDetailsInsertPage_vb.aspx

<%@ Page Language="VB" %>

<script runat="server">

Protected Sub GridView1_RowDeleted(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeletedEventArgs) If (Not e.Exception Is Nothing) Then ErrorMessageLabel.Text = "Failed to DELETE due to foreign key contstraint on the table. You may only delete rows which have no related records." e.ExceptionHandled = True End If End Sub

Page 51: Efectuando Acceso a Datos

</script>

<html><head runat="server"> <title>GridView DetailsView Master-Details (Edit and Insert Pages)</title></head><body> <form id="form1" runat="server"> <b>Choose a state:</b> <asp:DropDownList ID="DropDownList1" DataSourceID="SqlDataSource2" AutoPostBack="true" DataTextField="state" runat="server" /> <asp:SqlDataSource ID="SqlDataSource2" runat="server" SelectCommand="SELECT DISTINCT [state] FROM [authors]" ConnectionString="<%$ ConnectionStrings:Pubs %>" /> <br /> <br /> <asp:GridView ID="GridView1" AllowSorting="True" AllowPaging="True" runat="server" DataSourceID="SqlDataSource1" DataKeyNames="au_id" AutoGenerateColumns="False" Width="500px" OnRowDeleted="GridView1_RowDeleted"> <Columns> <asp:CommandField ShowDeleteButton="True" /> <asp:BoundField DataField="au_id" HeaderText="au_id" ReadOnly="True" SortExpression="au_id" /> <asp:BoundField DataField="au_lname" HeaderText="au_lname" SortExpression="au_lname" /> <asp:BoundField DataField="au_fname" HeaderText="au_fname" SortExpression="au_fname" /> <asp:BoundField DataField="state" HeaderText="state" SortExpression="state" /> <asp:HyperLinkField HeaderText="Edit..." Text="Edit..." DataNavigateUrlFields="au_id" DataNavigateUrlFormatString="DetailsViewEdit_vb.aspx?ID={0}" /> </Columns> </asp:GridView> <br /> <asp:HyperLink ID="HyperLink1" Text="Add New Author..." NavigateUrl="DetailsViewInsert_vb.aspx" 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)" DeleteCommand="DELETE FROM [authors] WHERE [au_id] = @au_id"> <SelectParameters> <asp:ControlParameter ControlID="DropDownList1" Name="state" PropertyName="SelectedValue" Type="String" /> </SelectParameters> <DeleteParameters> <asp:Parameter Name="au_id" Type="String" /> </DeleteParameters> </asp:SqlDataSource> <br /> <br />

Page 52: Efectuando Acceso a Datos

<asp:Label ID="ErrorMessageLabel" EnableViewState="false" runat="server" /> </form></body></html>

DetailsViewEdit_vb.aspx

<%@ Page Language="VB" %><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><script runat="server">

Protected Sub DetailsView1_ItemUpdated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DetailsViewUpdatedEventArgs) Response.Redirect("GridViewMasterDetailsInsertPage_vb.aspx") End Sub Protected Sub DetailsView1_ModeChanging(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DetailsViewModeEventArgs) If (e.CancelingEdit = True) Then Response.Redirect("GridViewMasterDetailsInsertPage_vb.aspx") End If End Sub</script><html xmlns="http://www.w3.org/1999/xhtml"><head runat="server"> <title>DetailsView Edit</title></head><body> <form id="form1" runat="server"> <asp:DetailsView DefaultMode="Edit" AutoGenerateRows="False" DataKeyNames="au_id" DataSourceID="SqlDataSource3" HeaderText="Edit Author" ID="DetailsView1" runat="server" Width="275px" OnItemUpdated="DetailsView1_ItemUpdated" OnModeChanging="DetailsView1_ModeChanging"> <Fields> <asp:BoundField DataField="au_id" HeaderText="au_id" ReadOnly="True" SortExpression="au_id" /> <asp:BoundField DataField="au_lname" HeaderText="au_lname" SortExpression="au_lname" /> <asp:BoundField DataField="au_fname" HeaderText="au_fname" SortExpression="au_fname" /> <asp:BoundField DataField="phone" HeaderText="phone" SortExpression="phone" /> <asp:BoundField DataField="address" HeaderText="address" SortExpression="address" /> <asp:BoundField DataField="city" HeaderText="city" SortExpression="city" /> <asp:BoundField DataField="state" HeaderText="state" SortExpression="state" /> <asp:BoundField DataField="zip" HeaderText="zip" SortExpression="zip" /> <asp:CheckBoxField DataField="contract" HeaderText="contract" SortExpression="contract" /> <asp:CommandField ShowEditButton="True" /> </Fields>

Page 53: Efectuando Acceso a Datos

</asp:DetailsView> <asp:SqlDataSource ID="SqlDataSource3" runat="server" ConnectionString="<%$ ConnectionStrings:Pubs %>" SelectCommand="SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors] WHERE ([au_id] = @au_id)" UpdateCommand="UPDATE [authors] SET [au_lname] = @au_lname, [au_fname] = @au_fname, [phone] = @phone, [address] = @address, [city] = @city, [state] = @state, [zip] = @zip, [contract] = @contract WHERE [au_id] = @au_id"> <SelectParameters> <asp:QueryStringParameter Name="au_id" QueryStringField="ID" Type="String" /> </SelectParameters> <UpdateParameters> <asp:Parameter Name="au_lname" Type="String" /> <asp:Parameter Name="au_fname" Type="String" /> <asp:Parameter Name="phone" Type="String" /> <asp:Parameter Name="address" Type="String" /> <asp:Parameter Name="city" Type="String" /> <asp:Parameter Name="state" Type="String" /> <asp:Parameter Name="zip" Type="String" /> <asp:Parameter Name="contract" Type="Boolean" /> <asp:Parameter Name="au_id" Type="String" /> </UpdateParameters> </asp:SqlDataSource> </form></body></html>

DetailsViewInsert_vb.aspx

<%@ Page Language="VB" %><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><script runat="server">

Protected Sub DetailsView1_ItemInserted(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DetailsViewInsertedEventArgs) Response.Redirect("GridViewMasterDetailsInsertPage_vb.aspx") End Sub Protected Sub DetailsView1_ModeChanging(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DetailsViewModeEventArgs) If (e.CancelingEdit = True) Then Response.Redirect("GridViewMasterDetailsInsertPage_vb.aspx") End If End Sub</script><html xmlns="http://www.w3.org/1999/xhtml"><head runat="server"> <title>DetailsView Insert</title></head><body> <form id="form1" runat="server"> <asp:DetailsView DefaultMode="Insert" AutoGenerateRows="False" DataKeyNames="au_id"

Page 54: Efectuando Acceso a Datos

DataSourceID="SqlDataSource3" HeaderText="Insert Author" ID="DetailsView1" runat="server" Width="275px" OnItemInserted="DetailsView1_ItemInserted" OnModeChanging="DetailsView1_ModeChanging"> <Fields> <asp:BoundField DataField="au_id" HeaderText="au_id" ReadOnly="True" SortExpression="au_id" /> <asp:BoundField DataField="au_lname" HeaderText="au_lname" SortExpression="au_lname" /> <asp:BoundField DataField="au_fname" HeaderText="au_fname" SortExpression="au_fname" /> <asp:BoundField DataField="phone" HeaderText="phone" SortExpression="phone" /> <asp:BoundField DataField="address" HeaderText="address" SortExpression="address" /> <asp:BoundField DataField="city" HeaderText="city" SortExpression="city" /> <asp:BoundField DataField="state" HeaderText="state" SortExpression="state" /> <asp:BoundField DataField="zip" HeaderText="zip" SortExpression="zip" /> <asp:CheckBoxField DataField="contract" HeaderText="contract" SortExpression="contract" /> <asp:CommandField ShowInsertButton="True" /> </Fields> </asp:DetailsView> <asp:SqlDataSource ID="SqlDataSource3" runat="server" ConnectionString="<%$ ConnectionStrings:Pubs %>" SelectCommand="SELECT [au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract] FROM [authors] WHERE ([au_id] = @au_id)" InsertCommand="INSERT INTO [authors] ([au_id], [au_lname], [au_fname], [phone], [address], [city], [state], [zip], [contract]) VALUES (@au_id, @au_lname, @au_fname, @phone, @address, @city, @state, @zip, @contract)"> <SelectParameters> <asp:QueryStringParameter Name="au_id" QueryStringField="ID" Type="String" /> </SelectParameters> <InsertParameters> <asp:Parameter Name="au_id" Type="String" /> <asp:Parameter Name="au_lname" Type="String" /> <asp:Parameter Name="au_fname" Type="String" /> <asp:Parameter Name="phone" Type="String" /> <asp:Parameter Name="address" Type="String" /> <asp:Parameter Name="city" Type="String" /> <asp:Parameter Name="state" Type="String" /> <asp:Parameter Name="zip" Type="String" /> <asp:Parameter Name="contract" Type="Boolean" /> </InsertParameters> </asp:SqlDataSource> </form></body></html>

Web.config

<?xml version="1.0"?><configuration> <connectionStrings>

Page 55: Efectuando Acceso a Datos

<!-- This connection is inherited from the ASP.NET Quickstart Web.config file Uncomment this section to edit the sample locally

<add name="Pubs" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=pubs;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Northwind" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Northwind;Persist Security Info=True" providerName="System.Data.SqlClient" /> <add name="Contacts" connectionString="Server=(local)\SQLExpress;Integrated Security=True;Database=Contacts;Persist Security Info=True" providerName="System.Data.SqlClient" /> --> <add name="NorthwindOLEDB" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb;" providerName="System.Data.OleDb" /> <add name="ContactsDatabase" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|Database.mdf;Integrated Security=True;User Instance=true;" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <pages styleSheetTheme="Default"/> <caching> <sqlCacheDependency enabled="true" pollTime="1000"> <databases> <add name="Pubs" connectionStringName="Pubs"/> </databases> </sqlCacheDependency> </caching> </system.web></configuration>

Web.config