1 ADO .NET Desconectado. ADO.NET proporciona un conjunto de componentes para crear aplicaciones distribuidas de uso compartido de datos. Dichos componentes están diseñados para separar el acceso a los datos de la manipulación de los mismos. Como ya sabemos, estos componentes son: DataSet y el proveedor de datos de .NET Framework, que es un conjunto de componentes entre los que se incluyen los objetos: conexión (Connection), de órdenes (Command), lector de datos (DataReader) y el adaptador de datos (DataAdapter). Un formulario Windows, para acceder a los datos de un origen de datos lo puede realizar de dos modos: - Conectado. Interactuando directamente con la base de datos. - Desconectado. Utilizando un adaptador de datos para leer la información de la base de datos y almacenarla en el conjunto de datos (DataSet). Posteriormente, cuando se requiera escribir en el origen de datos, se volverá a utilizar el adaptador, que tomará los datos del conjunto de datos (DataSet). ADO.NET y Windows Forms proporcionan otros componentes que se pueden utilizar para mostrar los datos. Incluyen controles como DataView, DataGrid, que pueden ser enlazados a datos, y propiedades de enlace a datos en la mayoría de controles estándares de Windows, como los controles TextBox, Label, ComboBox y ListBox. Vista de datos. La clase DataView permite representar los datos de la clase DataTable (de una tabla), creando múltiples vistas de los mismos, puesto que permite editar, ordenar y filtrar, buscar y navegar por un conjunto de datos determinado. Adaptador de datos. Clases DataAdapter y TableAdapter. Un adaptador de datos es un conjunto de objetos utilizado para intercambiar datos entre el origen de datos y el conjunto de datos (DataSet). Utiliza los objetos Connection, Command y DataReader implícitamente para poblar un objeto DataSet y para actualizar la fuente de datos central con los cambios efectuados en el DataSet. Existe una clase DataAdapter para cada proveedor de datos: OleDbDataAdapter, SqlDataAdapter, etc. Generalmente, cada adaptador de datos, intercambia datos entre una sola tabla del origen de datos y un solo objeto DataTable del conjunto de datos. Lo normal es utilizar tantos adaptadores como tablas tenga el conjunto de datos. De esta forma, cada tabla del conjunto de datos tendrá su correspondiente tabla en el origen de datos. A partir de la versión Visual Studio 2005 aparecen los objetos TableAdapter. Se pueden pensar en un TableAdapter como un DataAdapter que lleva integrado un objeto de conexión y la capacidad de contener varias consultas. En resumen. El DataAdapter conecta el DataSet con el origen de datos. Esta conexión la establece a través de los comandos (objetos Command): - De Selección, para obtener datos de la base de datos. SELECT * FROM Tabla - De Acción, para actualizar datos en la base de datos Inserción INSER INTO ..... Eliminación DELETE ... Actualización UPDATE ..... 2 Para poder realizar estas operaciones de conexión con el origen de datos, DataAdapter dispone de 4 propiedades a las que les asigna cada uno de estos 4 comandos: SelectCommand = Comando de Selección InsertCommand = Comando de Inserción DeleteCommand = Comando de Eliminación UpdateCommand = Comando de Actualización 'Crea un DataAdapter Dim da As OleDbDataAdapter da = New OleDbDataAdapter 'lo conecta Dim Cmd As New OleDbCommand(“Select * From Tabla”, Cn) da.SelectCommand = Cmd El DataAdapter no sólo conecta el origen de datos (BD) con el conjunto de datos (DataSet) si no que permite cargar o llenar el DataSet con los datos correspondientes desde el origen de datos. Para ello dispone del método Fill, que ejecuta el comando de selección y carga los datos en el DataSet. Nº Reg Afectados = da.Fill (ds, “AliasTabla”) El método Fill crea en el DataSet una tabla con el nombre asignado en su 2º argumento Se suele utilizar un DataAdapter por cada tabla del conjunto de datos (DataSet) Para actualizar la BD con la información del DataSet, DataAdapter dispone del método Update. Cuando se ejecuta este método, DataAdapter revisa todas las filas de la tabla del DataSet, comprobando si cada una de ellas ha sido modificada, añadida o eliminada y ejecuta el comando de acción correspondiente para modificar, añadir o eliminar esa fila del origen de datos. Nº de filas Afectadas = da.Update Resumen de los miembros. Propiedades: SelectCommand. Objeto de la clase Command que se va a utilizar para ejecutar una sentencia Select de SQL. InsertCommand. Objeto de la clase Command, que se va a utilizar para realizar una inserción de datos. UpdateCommand. Objeto de la clase Command que se va a utilizar para realizar una modificación de los datos. DeleteCommand. Objeto de la clase Command que se va a utilizar para realizar una eliminación de datos. Métodos: Fill. Agrega o refresca filas en un DataSet con los datos procedentes de un DataAdapter. Devuelve un Integer con el nº de filas afectadas. FillSchema. Agrega una DataTable al DataSet con la estructura de la tabla con sus claves, etc. No las relaciones. Update. Actualiza la fuente de datos mediante los comandos de Acción. Solo escribe las modificaciones (filas que han variado su contenido). 3 Conjunto de datos. La Clase DataSet. Un conjunto de datos incluye una o más tablas basadas en las tablas del origen de datos y también puede incluir información acerca de las relaciones entre esas tablas y las restricciones para los datos que pueda contener cada tabla. El componente central de la arquitectura sin conexión es la clase de objetos DataSet, perteneciente al espacio de nombres System.Data, que se puede utilizar con múltiples y distintos orígenes de datos. Como se ha visto anteriormente, en una tabla del DataSet cada fila contiene un estado. Al cargar el DataSet se asigna a cada fila del DataTable el estado “sin cambios”. A medida que se van realizando operaciones con las filas, a cada fila actualizada se le asigna el valor correspondiente a la operación realizada con ella: añadida, modificada o eliminada. El DataSet y sus objetos disponen de un conjunto de métodos que permiten actualizar o conocer el estado de las filas de cada una de sus tablas. Miembros más característicos de DataSet y sus objetos. La clase DataSet incluye: - la colección DataTableCollection de objetos DataTable (tablas de datos) - y la colección DataRelationCollection de objetos DataRelation (relaciones entre las tablas). La clase DataTable incluye: - la colección DataRowCollection de objetos DataRow (filas de la tabla) - la colección DataColumnCollection de objetos DataColumn (campos) - y la colección Constraint (restricciones). La clase DataRow tiene la propiedad RowState, que permite saber si la fila cambió y de qué modo, desde que la DataTable se cargó por primera vez. Alguno de sus valores puede ser: - Added - Deleted - Modified - Unchanged. Propiedades de DataSet CaseSensitive. Propiedad que indica si las comparaciones de texto dentro de las tablas distinguen entre mayúsculas y minúsculas. Por defecto tiene el valor False. DataSetName. Establece o devuelve mediante una cadena de texto el nombre del objeto DataSet. HasErrors. Devuelve un valor lógico para indicar si existen errores dentro de las tablas del DataSet. Relations. Esta propiedad devuelve una colección de objetos DataRelation, que representan todas las relaciones existentes entre las tablas del objeto DataSet. Tables. Devuelve una colección de objetos DataTable, que representan a cada una de las tablas existentes dentro del objeto DataSet. Métodos Clear. Elimina todos los datos almacenados en el objeto DataSet, vaciando todas las tablas contenidas en el mismo. No borra las tablas AcceptChanges. Pone el estado de las filas a “Sin Cambios”. Las clases DataRow y DataTable también tienen métodos AcceptChanges. Si se llama a AcceptChanges en el nivel de DataTable, se llama al método AcceptChanges para cada DataRow. De igual forma, si se invoca a AcceptChanges en DataSet, se llama a AcceptChanges en cada una de las tablas de DataSet. De esta forma, se puede invocar el método desde varios niveles. Al llamar al método AcceptChanges de DataSet, se permite 4 invocar el método en todos los objetos subordinados (por ejemplo, tablas y filas) con sólo una llamada. Cuando se llama a AcceptChanges en DataSet, cualquier objeto DataRow que aún se encuentre en modo de edición finalizará correctamente sus modificaciones. La propiedad RowState de cada DataRow también cambia; las filas Added y Modified se convierten en Unchanged y se quitan las filas Deleted. GetChanges. Devuelve un nuevo objeto DataSet con todas las tablas y sólo las filas que hayan tenido cambios. HasChanges. Devuelve true o false para indicar si se han realizado cambios al contenido del DataSet desde que fue cargado o bien desde que se realizó la última llamada al método AcceptChanges. RejectChanges. Abandona todos los cambios realizados en las tablas contenidas en el objeto DataSet desde que fue cargado el objeto o bien desde la última vez que se lanzó el método AcceptChanges. Merge. Toma los contenidos de un DataSet y los mezcla con los de otro DataSet, de forma que contendrá los datos de ambos objetos DataSet. 5 Procedimientos El procedimiento para obtener un conjunto de datos (Cargar un DataSet) a través de un DataAdapter es el siguiente: 1. Crear la conexión, el adaptador y el conjunto de datos (DataSet). 2. Crear los comandos necesarios y asignarlos al adaptador. 3. Cargar el DataSet a través del método Fill del adaptador. Carga de un DataSet con una sentencia SQL. Dim da As OleDbDataAdapter 'Crea un Adaptador da = New OleDbDataAdapter 'Crea un DataAdapter con la consulta Dim Sql As String = "Select * from Tabla" Dim da As New OleDbDataAdapter(Sql, Cn) 'o Crea un adaptador y le asigna un comando de consulta da = New OleDbDataAdapter Dim Cmd As New OleDbCommand(Sql, Cn) da.SelectCommand = Cmd - Llenado de una tabla (DataTable) del DataSet a través del conector DataAdapter El método Fill del DataAdapter crea en el DataSet una tabla con el nombre asignado en su 2º argumento. Esa DataTable se carga con los datos del DataAdapter. 'Crear el DataSet o vaciarlo si ya estaba creado 'Crea el DataSet ds = New DataSet 'o Dim ds As New DataSet 'Vacia el DataSet ds.Clear() 'LLena el DataSet con una tabla Cn.Open() da.Fill(ds, "AliasTabla") Cn.Close() 'llena las primeras 100 filas de la tabla Cn.Open() da.Fill(ds, 0, 100, "100Autores") Cn.Close() - Mostrar los datos. Para ello se enlaza el DataSet cargado con el DataGrid 'mediante las propiedades DataSource y DataMenber With Me. DataGrid1 .DataSource = ds .DataMember = "AliasTabla" End With 'mediante el método SetDataBinding DataGrid1.SetDataBinding(ds, "AliasTabla") 6 - Crear los comandos. Utiliza comandos parametrizados 'Crear los comandos necesarios y asignarlos al adaptador 'Comando sin parámetros 'Crea el comando de Selección Dim Cmd As New OleDbCommand("Select * from Authors", Cn) 'asigna el comando al adaptador da.SelectCommand = Cmd 'Utilizar comando parametrizado 'crea un Ob Command de Inserción Dim sql As String = "Insert Into Authors (Author, [Year Born]) Values(?,?)" Dim CmdInsercion As New OleDbCommand(sql, Cn) 'añade los parámetros a la colección With CmdInsercion.Parameters.Add("P1", OleDbType.VarChar, 50) 'Asigna valor al parámetro al ejecutarse el comando. 'Asigna el valor de la columna referenciada en la fila actual .SourceColumn = "Author" End With With CmdInsercion.Parameters.Add("P2", OleDbType.SmallInt) .SourceColumn = "Year Born" End With 'asigna el comando al adaptador da.InsercionCommand = CmdInsercion 7 Ejemplo. Crear un DataSet con la tabla Authors de Biblio.mdb y mostrar los autores en un DataGrid. Utilizando un DataAdapter Imports System.Data.OleDb Public Class Form1 Inherits System.Windows.Forms.Form Public Class Form1 Inherits System.Windows.Forms.Form ... 'Declarar Dim Cn As Dim ds As Dim da As los objetos: conexion, DataSet y DataAdapter OleDbConnection DataSet OleDbDataAdapter Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 'Crea conexión Cn = New OleDbConnection Cn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Biblio.mdb;" 'Crea Adaptador da = New OleDbDataAdapter 'Crear los comandos necesarios y asignarlos al adaptador Dim Cmd As New OleDbCommand("Select * from Authors", Cn) da.SelectCommand = Cmd 'Crea el DataSet ds = New DataSet Call CargarDatos() End Sub Private Sub CargarDatos() 'Carga y muestra los datos 'Carga el DataSet y enlace con DataGrid para mostrar los datos 'Vacia el DataSet ds.Clear() 'Llena el DataSet con una tabla Cn.Open() da.Fill(ds, "Autores") Cn.Close() 'enlaza el DataSet con el DataGrid With Me.DGridAutores .DataSource = ds .DataMember = "Autores" End With End Sub End Class 8 Ejercicio. Añadir nuevas filas escribiendo el usuario el nombre y año de nacimiento del autor. Partiendo del ejemplo anterior. Añadir un botón para grabar la nueva fila y dos cajas de texto para poder escribir el nombre y año de nacimiento del autor. Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 'Crea conexión Cn = New OleDbConnection Cn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Biblio.mdb;" 'Crea Adaptador da = New OleDbDataAdapter 'Crear los comandos necesarios y asignarlos al adaptador Dim Cmd As New OleDbCommand("Select * from Authors", Cn) da.SelectCommand = Cmd '¡ojo! el campo Au_Id es autonumérico, no se le asigna valor 'Utiliza comando parametrizado 'crea un Ob Command con parámetros Dim sql As String = "Insert Into Authors (Author, [Year Born]) Values(?,?)" Dim CmdInsercion As New OleDbCommand(sql, Cn) 'asigna el comando al adaptador da.InsertCommand = CmdInsercion 'añade los parámetros a la colección da.InsertCommand.Parameters.Add(New OleDbParameter("P1", OleDbType.VarChar)) da.InsertCommand.Parameters.Add(New OleDbParameter("@P2", OleDbType.Integer)) 'Crea el DataSet ds = New DataSet Call CargarDatos() End Sub Private Sub BtoGrabar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtoGrabar.Click Dim NReg As Integer 'nº de registros afectados Try 'asigna valor a los parámetros del comando de inserción da.InsertCommand.Parameters(0).Value = Me.TxtAutor.Text da.InsertCommand.Parameters(1).Value = CInt(Me.TxtAño.Text) 'ejecuta el comando Cn.Open() NReg = da.InsertCommand.ExecuteNonQuery Cn.Close() Call CargarDatos() Catch ex As Exception MsgBox(ex.ToString) End Try End Sub Ejercicio de Navegación por regs. Al ejercicio anterior añadirle: 3 Etiquetas, 3 Cajas de Texto, 4 Botones: Primero, Anterior, Siguiente y Último 9 Navegación y edición de Registros. Para las operaciones de navegación por la tabla es obtener del DataSet, la tabla que necesitamos mediante su colección Tables, y a su vez, a la colección Rows de esa tabla, pasarle el número de fila/registro al que vamos a desplazarnos. Es decir: - Obtener la tabla del DataSet mediante su colección Tables y la fila actual mediante la colección Rows de esa tabla Dim nFila As Integer ' Posición fila actual 'establecer el indicador de la fila a mostrar en la tabla Me.nFila = 0 'primera fila, u otro valor Me.nFila = Me.ds.Tables(0).Rows.Count – 1 'última fila 'obtener un ob DataRow con la fila actual Dim drfila As DataRow drfila = ds.Tables("Autores").Rows(nFila) Para las operaciones de edición: - Se debe utilizar los miembros del objeto tabla del DataSet. - Una vez terminado el proceso de edición, se actualizará el almacén de datos original con el contenido del DataSet, empleando el DataAdapter Se puede utilizar el objeto CommandBuilder para simplificar el código. La misión del objeto CommandBuilder es la de construir automáticamente los comandos de consulta, inserción y eliminación, y asignárselos al DataAdapter. 'Crea CommandBuilder*****sólo es necesario para operaciones de edición Dim ComBuil As OleDbCommandBuilder = New OleDbCommandBuilder(da) Las operaciones de edición más habituales son: - Añadir una nueva fila - Modificar el valor de los campos de una fila - Eliminar una fila - Actualizar el origen de datos con el contenido del DataSet Procedimientos para codificar las operaciones de edición. Añadir una fila a una tabla de un DataSet Siempre que se inserta o añade una nueva fila se posiciona al final de la tabla. - Obtener un nuevo objeto fila de la tabla, con el método NewRow. Asignar valor a sus campos. Añadir el objeto fila a la colección de filas de la tabla. 'crea una fila nueva en la tabla del DataSet Dim drFila As DataRow drFila = Me.ds.Tables("Autores").NewRow 'asigna valor a los campos de la nueva fila drFila("Au_Id") = Me.TxtAu_Id.Text drFila.Item("Author") = Me.TxtAuthor.Text drFila(2) = Me.TxtAño.Text 'añade el objeto fila a la colección de filas Me.ds.Tables(0).Rows.Add(drFila) 10 Modificar los datos de una fila. No se debe modificar el valor de los campos clave. - Obtener en un objeto DataRow la fila cuyos campos se desean modificar. Dichos valores se presentan al usuario para que los pueda modificar Asignar nuevo valor a los campos 'obtener el objeto fila 'en la que estamos posicionados Dim drFila As DataRow drFila = Me.ds.Tables("Autores").Rows(Me.nFila) 'Modificar los campos With drFila .Item(1) = Me.TxtAuthor.Text .Item(2) = Me.TxtAño.Text End With Actualizar el origen de datos con el contenido del DataSet Se utiliza el método Update del DataAdapter El método Update toma uno de los siguientes argumentos: Un objeto DataTable Un DataSet más un DataTable Una matriz de objetos DataRow El método Update devuelve el número de filas que han sido actualizadas con éxito. Cuando se ejecuta Update, el DataAdapter verifica la propiedad RowState de cada fila. Si el estado es: Added, DataAdapter ejecuta el comando SQL especificado en la propiedad InsertCommand Modified, se ejecuta el comando especificado en UpdateCommand Deleted, se ejecuta el comando especificado en DeleteCommand - Crear los comandos parametrizados de inserción, actualización y eliminación Asignar los comandos a las propiedades InsertCommand, UpdateCommand y DeleteCommand del DataAdapter Actualizar la BD mediante el método Update de DataAdapter Recordar: Las instrucciones de consulta con parámetros definen qué parámetros de entrada y de salida se deben crear. Para crear un parámetro, se utiliza el método Parameters.Add o el constructor Parameter con el fin de especificar el nombre de columna, tipo de datos y tamaño. En el caso de tipos de datos intrínsecos, como Integer, no es necesario incluir el tamaño o puede especificar el tamaño predeterminado. 11 'Crear los comandos con parámetros 'crea el comando Insertar Dim SqlInsert As String = "INSERT INTO Authors (Author, [Year Born]) VALUES (?, ?)" Dim cmdInsert As New OleDbCommand(SqlInsert, Cn) 'crea los parámetros y los añade a la coleccion With cmdInsert 'crea el parámetro y configura sus propiedades Dim Par As OleDbParameter = .CreateParameter .Parameters.Add(Par) 'este es el nombre de la columna en el DataTable 'permite asignar el valor al parámetro antes de ejecutarse .Parameters(0).SourceColumn = "Author" 'se puede utilizar el valor original de cada DataRow .Parameters(0).SourceVersion = DataRowVersion.Original '2º parámetro Par = .CreateParameter .Parameters.Add(Par) .Parameters(1).SourceColumn = "Year Born" End With 'crea el comando Borrar Dim cmdDelete As New OleDbCommand("DELETE FROM Authors WHERE Au_Id = ?", Cn) With cmdDelete.Parameters.Add("P1", OleDbType.Integer) .SourceColumn = "Au_Id" .SourceVersion = DataRowVersion.Current 'la que tiene por defecto End With 'crea el comando Actualizar Dim SqlActualizar As String SqlActualizar = "UPDATE Authors SET Author = ?, [Year Born] = ? WHERE Au_Id=?" Dim cmdUpdate As New OleDbCommand(SqlActualizar, Cn) With cmdUpdate.Parameters.Add("P1", OleDbType.Char, 50) .SourceColumn = "Author" End With With cmdUpdate.Parameters.Add("P2", OleDbType.SmallInt) .SourceColumn = "Year Born" End With With cmdUpdate.Parameters.Add("P1", OleDbType.Integer) .SourceColumn = "Au_Id" .SourceVersion = DataRowVersion.Current 'la que tiene por defecto End With 'asigna los comandos a las With Da .InsertCommand .DeleteCommand .UpdateCommand End With propiedades xxxCommand de DataAdapter = cmdInsert = cmdDelete = cmdUpdate 'Actualiza la BD mediante el método Update de DataAdapter 'enviar las filas modificadas en DataSet a BD Dim RegActualizados As Integer Cn.Open() RegActualizados = Me.Da.Update(Me.Ds, "Autores") MessageBox.Show("Reg actualizados " & RegActualizados) Cn.Close() Otro modo: Utiliza el objeto CommandBuilder para creaar los comandos: 12 utiliza CommandBuilder para generar las tres propiedades Command 'Crea un ob auxiliar CommandBuilder para este DataAdapter 'Para que los nombres de campos con caracteres especiales no den error: ‘encierra el nombre de los campos entre corchetes Dim cmdBuilder As New OleDbCommandBuilder(Me.Da) With cmdBuilder .QuotePrefix = "[" .QuoteSuffix = "]" End With 'Asigna los comandos a las With Da .InsertCommand = .DeleteCommand = .UpdateCommand = End With propiedades xxxCommand de DataAdapter cmdBuilder.GetInsertCommand cmdBuilder.GetDeleteCommand cmdBuilder.GetUpdateCommand 'Enviar las filas modificadas en DataSet a BD Dim RegActualizados As Integer Cn.Open() RegActualizados = Me.Da.Update(Me.Ds, "Autores") Cn.Close()