1. Otros. 1.1 Introducción. Una vez visto los objetos de conexión, vamos a ver una serie de objetos que realizan también tareas dentro del acceso a una base de datos. 1.2 BindingSource. Este objeto tiene como misión el realizar el enlace entre dos objetos que no pueden enlazarse directamente por carecer del recurso adecuado. Normalmente el enlace de un origen de datos y uno de visualización se realiza a través de la propiedad DataSource, pero cuando no se dispone de ella, se puede utilizar este objeto para realizar dicho enlace. El objeto más utilizado en la visualización es el datagridview, este objeto dispone de la propiedad DataSource, por lo que su enlace no representa mayor problema. Primero, proporciona una capa de direccionamiento indirecto al enlazar los controles de un formulario a los datos. Esto se lleva a cabo enlazando el componente BindingSource a su origen de datos y enlazando a continuación los controles del formulario al componente BindingSource. Todas la demás interacción con los datos, incluido el desplazamiento, la ordenación, el filtrado y la actualización, se lleva a cabo con llamadas al componente BindingSource. El enlace se lleva a cabo a través de la propiedad DataBindings. Veamos su utilización. Primero los objetos que se van a utilizar. Dim Dim Dim Dim Dim Dim Conexion As New System.Data.OleDb.OleDbConnection Adaptador As New System.Data.OleDb.OleDbDataAdapter Tabla As DataTable = ObjDataSet.Tables("Provincia") EnlaceTabla As New BindingSource Actualizador As New OleDb.OleDbCommandBuilder(Adaptador) CadenaSql As String = "Select * from provincia order by codprov" Y además TextBox, Campo01, que se supone ya en el formulario. Enlace. El enlace se realiza con la propiedad DataBinding del objeto TextBox. Private Sub Enlaces() Campo00.DataBindings.Add("Text", EnlaceTabla, "CodProv") Campo01.DataBindings.Add("Text", EnlaceTabla, "DenomCas") Campo02.DataBindings.Add("Text", EnlaceTabla, "DenomVal") End Sub Los parámetros son ("Text", La propiedad que se va a enlazar, entre comillas, EnlaceTabla, El objeto que se enlaza al TextBox, el que tiene los datos. "CodProv") El nombre del campo en el objeto de origen de los datos, nombre en la tabla. El objeto para poderlo utilizar debe de tener datos, he aquí la carga del mismo. 1 Este ejemplo es con DataSet Private Sub CargaDatos() ObjDataSet.Clear() If Conexion.State = ConnectionState.Closed Then Conexion.Open() Adaptador = New OleDb.OleDbDataAdapter(CadenaSql, Conexion) Adaptador.Fill(ObjDataSet) EnlaceTabla.DataSource = ObjDataSet.Tables(0) ' ("Provincia") Actualizador = New OleDb.OleDbCommandBuilder(Adaptador) Conexion.Close() End Sub El mismo, pero con un DataTable Private Sub CargaDatos() If Conexion.State = ConnectionState.Closed Then Conexion.Open() Adaptador = New OleDb.OleDbDataAdapter(CadenaSql, Conexion) Adaptador.Fill(Tabla) EnlaceTabla.DataSource = Tabla Actualizador = New OleDb.OleDbCommandBuilder(Adaptador) Conexion.Close() End Sub Podemos borrar el elemento actual de la tabla, registro actual en el formulario.. EnlaceTabla.RemoveCurrent() Iniciar los objetos de TextBox en blanco para añadir un nuevo registro. EnlaceTabla.AddNew() Una vez que se cumplimentan los datos, si quiere grabarse ha de ejecutarse el método EnlaceTabla.EndEdit. Este objeto además permite realizar tareas de navegación, con los métodos que observamos en el ejemplo. EnlaceTabla.MoveFirst() EnlaceTabla.MovePrevious() EnlaceTabla.MoveNext() EnlaceTabla.MoveLast() Y el proceso de actualización es el que sigue. Private Sub Actualizar() Conexion.Open() Try Adaptador.Update(CType(EnlaceTabla.DataSource, DataTable)) MsgBox("Datos actualizados.", MsgBoxStyle.Information, Me.Text) Catch ex As OleDb.OleDbException MsgBox("Datos existentes", MsgBoxStyle.Critical, Me.Text) End Try Conexion.Close() End Sub 2 La actualización debe de arrancar, puede, del evento clic de un button, en el que se ejecuta: EnlaceTabla.EndEdit() Actualizar() La primera línea finaliza la edición del registro que se añadió nuevo, o que se estaba editando. La segunda llama al procedimiento de actualización. Si deseamos cancelar los cambios que se hayan hecho en el registro actual, podemos ejecutar EnlaceTabla.CancelEdit() Y los cambios en el registro actual se desharán. Todo lo referente al objeto BindingSource podemos encontrarlo en el siguiente Link. http://msdn2.microsoft.com/es-es/library/system.windows.forms.bindingsource(VS.80).aspx 1.3 CommandBuilder. Es un objeto que genera de forma automática el código SQL necesario para proceder a la actualización de una tabla de la base de datos. No es posible utilizarlo para las tablas que se generen de forma provisional, pues estás tablas suelen estar compuestas por datos de varias tablas, o para tablas en las que los datos a añadir son una colección de varios registros. La utilización es relativamente sencilla. En un DataSet el objeto OleDbCommandBuilder puede ser reutilizado y no es necesario tener uno para cada tabla del DataSet. Va asociado con un objeto DataTable y un DataAdapter, pues el conjunto permite la gestión de los datos. El ejemplo que sigue carga los datos en un DataGridView, y lo deja enlazado a la tabla, de tal forma que con muy poco más es suficiente para la visualización y la edición de los datos de la tabla. La utilización del CommandBuilder es como sigue: Dim ComandoActualizar As OleDb.OleDbCommandBuilder A continuación su asignación, no debe moverse del sitio pues sino no dispone de la información necesaria para generar el código SQL ' Crear un 'commandbuilder' que genere el SQL Update/Insert/Delete ComandoActualizar = New OleDb.OleDbCommandBuilder(Adaptador) Y el procedimiento completo es el que sigue, para poder tener una visión algo más amplia. Public Sub CargaDataGrid( _ ByVal Conexion As System.Data.OleDb.OleDbConnection, _ ByRef Adaptador As System.Data.OleDb.OleDbDataAdapter, _ ByRef EnlaceTabla As BindingSource, _ ByRef ObjDataGrid As DataGridView, _ ByVal CadenaSql As String) Dim ComandoActualizar As OleDb.OleDbCommandBuilder Dim Tabla As New DataTable Try ' Crear un nuevo adaptador de datos Adaptador = New OleDb.OleDbDataAdapter(CadenaSql, Conexion) ' Crear un 'commandbuilder' que genere el SQL Update/Insert/Delete ComandoActualizar = New OleDb.OleDbCommandBuilder(Adaptador) 3 ' Llenar la tabla Adaptador.Fill(Tabla) ' Enlazar la tabla ObjDataGrid.DataSource = Tabla Catch ex As OleDb.OleDbException MsgBox(ex.Message, MsgBoxStyle.Information) End Try End Sub Si en lugar de utilizarlo en un DataTable, se usa en una tabla dentro de un DataSet, no hay cambio alguno, pues el objeto funciona a nivel de DataTable, no de DataSet, por lo que no hay ningún cambio en su utilización, la definición y el uso es exactamente igual. Todo lo referente al objeto OleDbCommandBuilder podemos encontrarlo en el siguiente Link. http://msdn2.microsoft.com/es-es/library/system.data.oledb.oledbcommandbuilder(VS.80).aspx 1.4 Command. Nos permite realizar diversos tipos de operaciones con la base de datos. Almacena básicamente el código SQL para interactuar con la base de datos, pero dispone de varias funcionalidades. En el ejemplo que sigue se usa en la generación de un objeto DataReader para cargar un ListBox con una consulta incrustada. Su definición es Dim Comando As New System.Data.OleDb.OleDbCommand Después primero le asignamos el código SQL ' Contenido del comando Comando.CommandText = CadenaSQL Después le indicamos de que tipo es el código SQL que se le asigna, una cadena de texto, o una consulta almacenada, en el ejemplo le indicamos que es una cadena de texto. ' Tipo de comando a ejecutar Comando.CommandType = CommandType.Text Y el siguiente paso es asignarle el origen de datos que se va a utilizar. ' Conexión a utilizar, configurada previamente. Comando.Connection = Conexion Pasada la fase de definición, se ejecuta el código SQL, asignando el resultado de su ejecución a un objeto DataReader. ' Ejecución de SQL Reader = Comando.ExecuteReader En este caso se ha usado el método ExecuteReader, pero disponemos de otra faceta, para cuando no hay que devolver datos, ExecuteNonQuery, para las instrucciones Update, Delete por ejemplo. 4 Veamos el procedimiento completo. Public Sub CargaListaAutores( _ ByRef Lista As ListBox, _ ByVal Conexion As System.Data.OleDb.OleDbConnection) Dim CadenaSQL As String Dim Comando As New System.Data.OleDb.OleDbCommand Dim Reader As System.Data.OleDb.OleDbDataReader Dim Objeto As ItemLista Lista.Items.Clear() CadenaSQL = "Select Codigo, Nombre " & _ "From Autores " & _ "Order By Nombre" Try ' Abrir la base de datos. Conexion.Open() ' Contenido del comando Comando.CommandText = CadenaSQL ' Tipo de comanndo a ejecutar Comando.CommandType = CommandType.Text ' Conexión a utilizar, configurada previamente. Comando.Connection = Conexion ' Ejecución de SQL Reader = Comando.ExecuteReader Try While Reader.Read Objeto = New ItemLista(Trim(Reader.Item("Nombre").ToString), _ Reader.Item("Codigo").ToString) Lista.Items.Add(Objeto) End While Catch ex As OleDb.OleDbException MsgBox(ex.Message, MsgBoxStyle.Information, "Leer reader") End Try Catch Ex As OleDb.OleDbException MsgBox(Ex.Message, MsgBoxStyle.Information, "Crear reader") End Try Conexion.Close() End Sub Si en lugar de utilizar una cadena SQL, usamos una consulta almacenada en la base de datos, en este caso en Access, los cambios son estos. ' Nombre de la consulta en la base de datos. Comando.CommandText = "Usp_UpdateProvincia" ' Tipo de comando a ejecutar Comando.CommandType = CommandType.StoredProcedure ' Conexión a utilizar, configurada previamente. Comando.Connection = Conexion Hay que pensar que el resto es el mismo, solo se ha cambiado la fuente del código SQL, que ahora está almacenado en la base de datos, y cuando la abra lo capturará. 5 La consulta puede ser que incorpore parámetros, como es el caso del ejemplo que se actualizan datos en la tabla, en ese caso hay que añadir el valor de los parámetros. ' Añadir parámetros de la consulta a ejecutar. Comando.Parameters.Add("@Cod", OleDb.OleDbType.Char, 2).Value = Campo01.Text Comando.Parameters.Add("@DeC", OleDb.OleDbType.Char, 11).Value = Campo02.Text Comando.Parameters.Add("@DeV", OleDb.OleDbType.Char, 11).Value = Campo03.Text El significado de los parámetros es el que sigue. "@Cod", Es el nombre del parámetro en la consulta almacenada. OleDb.OleDbType.Char, 2) Es el tipo de dato que se envía, texto de dos caracteres de longitud. .Value = Campo01.Text Es la forma de darle el valor que se ha de grabar en la base de datos. El código SQL en la consulta que está almacenada en Access es. UPDATE Provincia SET Provincia.CodProv = @Cod, Provincia.DenomCas = @DeC, Provincia.DenomVal = @DeV WHERE (((Provincia.CodProv)=[@Codigo])); Otro ejemplo del uso del Command es el que sigue. La definición es la misma y la parametrización también. Dim Comando As New System.Data.OleDb.OleDbCommand ' código SQL a utilizar Comando.CommandText = CadenaSQL ' Tipo de comanndo a ejecutar Comando.CommandType = CommandType.Text ' Conexión a utilizar, configurada previamente. Comando.Connection = Conexion El cambio viene a la hora de la ejecución que como no devuelve datos, se usa otro método. Cuantos = Comando.ExecuteNonQuery Cuantos es una variable entera que recibe cuantos registros han sido afectados por la ejecución en éste caso será uno correcto y cero error. Por eso la variable Fallo será true cuando Cuantos valga cero. Este es el procedimiento completo. Private Sub GrabarMovimiento( _ ByVal Conexion As System.Data.OleDb.OleDbConnection, _ ByRef Comando As System.Data.OleDb.OleDbCommand, _ ByVal Fech As String, _ ByVal Edit As String, _ ByVal TipT As String, _ ByVal Titu As String, _ ByVal Cant As String, _ ByRef Fallo As Boolean) 6 Dim Dim Dim Dim Dim Cuantos As Integer TiMv As String = "1" ' entradas de almacen Nume As String = Campo01.Text Docu As String = Campo02.Text CadenaSQL As String CadenaSQL = "INSERT INTO Movimientos ( Fecha, Tipo, Codigo, " & _ "TipTitulo, Titulo, TipMov, Cantidad, " & _ "FacAbo, Numero ) " & _ "Values ('" & Fech & "', " & _ "'" & Edit & "', " & _ "'" & Docu & "', " & _ "'" & TipT & "', " & _ "'" & Titu & "', " & _ "'" & TiMv & "', " & _ "'" & Cant & "', " & _ "'0', " & _ "'" & Nume & "') " ' código SQL a utilizar Comando.CommandText = CadenaSQL ' Tipo de comando a ejecutar Comando.CommandType = CommandType.Text ' Conexión a utilizar, configurada previamente. Comando.Connection = Conexion Try Cuantos = Comando.ExecuteNonQuery Fallo = Cuantos = 0 Catch ex As OleDb.OleDbException Fallo = True MsgBox(ex.Message, MsgBoxStyle.Information) End Try End Sub Todo lo referente al objeto Command podemos encontrarlo en el siguiente Link. http://msdn2.microsoft.com/es-es/library/system.data.oledb.oledbcommand(VS.80).aspx 1.5 DataView. Este objeto permite obtener datos de una tabla a partir de unos criterios definidos. Tiene la característica que permite seleccionar las filas de una tabla que hayan sido modificadas, borradas o insertadas, siempre que no se haya hecho el volcado o la actualización de los datos en la base de datos. También se puede indicar porque campo queremos que nos ordene los datos que se van a filtrar. Los pasos son: Asignar la tabla origen de la información. ' Asignación de la tabla ObjDataView.Table = Tabla Si deseáramos modificar el contenido de la vista, cambiaríamos a true estos valores. ' No se permite edición. ObjDataView.AllowDelete = False ObjDataView.AllowEdit = False ObjDataView.AllowNew = False 7 Indicar el contenido del campo a utilizar en el filtro. ' campo a usar en el filtro. ObjDataView.RowFilter = "Tipo = '" & Tipo & "'" Que filas deseamos visualizar, se puede seleccionar, entre las actuales, solo borradas, insertadas, etc. ' Cuales ObjDataView.RowStateFilter = DataViewRowState.CurrentRows El campo por el cual queremos la ordenación. ' campo a usar para la ordenación ObjDataView.Sort = "RazonSocial" Después solo queda recorrer el objeto para capturar el resultado del filtrado. For X = 0 To ObjDataView.Count - 1 Objeto = _ New ItemLista(Trim(ObjDataView(X).Row.Item("RazonSocial").ToString), _ ObjDataView(X).Row.Item("Tipo").ToString, _ ObjDataView(X).Row.Item("Codigo").ToString) Lista.Items.Add(Objeto) Next El código completo es el que sigue. Public Sub CargaListaClientesDS(ByVal Tabla As DataTable, _ ByRef Lista As ListBox, _ ByVal Tipo As String) Dim X As Integer Dim Objeto As ItemLista Dim ObjDataView As DataView ' Crear el objeto ObjDataView = New DataView(Tabla) ' Definir parámetros With ObjDataView ' Asignación de la tabla .Table = Tabla ' No se permite edición. .AllowDelete = False .AllowEdit = False .AllowNew = False ' campo a usar en el filtro .RowFilter = "Tipo = '" & Tipo & "'" .RowStateFilter = DataViewRowState.CurrentRows ' campo a usar para la ordenación .Sort = "RazonSocial" End With Lista.Items.Clear() For X = 0 To ObjDataView.Count - 1 Objeto = _ New ItemLista(Trim(ObjDataView(X).Row.Item("RazonSocial").ToString), _ ObjDataView(X).Row.Item("Tipo").ToString, _ ObjDataView(X).Row.Item("Codigo").ToString) Lista.Items.Add(Objeto) Next End Sub 8 Todo lo referente a este objeto lo podemos encontrar en el siguiente Link. http://msdn2.microsoft.com/es-es/library/system.data.dataview(VS.80).aspx 1.6 DataRelation. Este objeto permite definir las relaciones dentro de un DataSet. Su utilización es como sigue. Private Sub CrearRelacion() ' ' Crear relaciones ' ObjDataSet.Relations.Clear() Dim RelGruAlu As DataRelation = _ ObjDataSet.Relations.Add("GrupoAlumno", _ ObjDataSet.Tables("Grupos").Columns("CodGrupo"), _ ObjDataSet.Tables("Alumnos").Columns("Grupo")) Dim RelAluAsi As DataRelation = _ ObjDataSet.Relations.Add("AlumnosAsignaturas", _ ObjDataSet.Tables("Alumnos").Columns("Exped"), _ ObjDataSet.Tables("AlumnAsig").Columns("Exped")) End Sub Donde ObjDataSet.Relations.Add("GrupoAlumno", _ es el nombre de la relación dentro del DataSet. ObjDataSet.Tables("Grupos").Columns("CodGrupo"), _ es el campo de la tabla principal, “Grupos”. ObjDataSet.Tables("Alumnos").Columns("Grupo")) es el campo en la tabla secundaria, “Alumnos”. Se pueden añadir las relaciones necesarias para el control del DataSet. Cuando se añade una relación, se comprueba que está se cumple y si no es así se genera un error. Como se puede observar es para el DataSet, pero en la base de datos tendremos definidas las relaciones de integridad para la gestión de la misma. Con esas relaciones definidas para una tarea de mantenimiento realizada fuera de un DataSet, es suficiente, ya que con declararlas como no eliminar en cascada, a la hora de intentar borrar un registro con datos dependientes se genera un error que se puede capturar con el Try Catch del tipo OleDbException. Vemos un ejemplo en el que se borra un registro en la tabla, sin que haya ningún código en concreto para la gestión de una relación. Los pasos son : Generar el código SQL, Asignar los parámetros del objeto OleDbCommand, Ejecutar el código SQL Controlar los errores 9 Podemos ver el procedimiento completo. Private Sub Borrar() Dim Comando As New System.Data.OleDb.OleDbCommand Dim Cuantos As Integer Dim CadenaSQl As String CadenaSQl = "Delete From Editoriales " & _ "Where (Editoriales.Codigo = '" & Campo01.Text & "');" Conexion.Open() ' Nombre de la consulta en la base de datos. Comando.CommandText = CadenaSQl ' Tipo de comando a ejecutar Comando.CommandType = CommandType.Text ' Conexión a utilizar, configurada previamente. Comando.Connection = Conexion Try Cuantos = Comando.ExecuteNonQuery Select Case Cuantos Case Is <> 0 MsgBox("Datos borrados", MsgBoxStyle.Information, Me.Text) Case Else ' cuando hay error salta el Catch, y sino MsgBox("Error en borrado", MsgBoxStyle.Information, Me.Text) End Select Catch ex As OleDb.OleDbException MsgBox(ex.Message, MsgBoxStyle.Information, "Error en inserción") End Try Conexion.Close() ' Liberar recursos Comando.Dispose() Comando = Nothing End Sub Podemos ver la ventana con las propiedades de una relación entre dos tablas, conviene hacer hincapié en el apartado de no activar la opción de eliminar en cascada, de esa forma podemos evitar algún que otro disgusto. Con esos pasos es suficiente para la gestión de la relación. En un DataSet el uso de relaciones mejora la visualización de los datos, con el DataGrid, por ejemplo, ya que se realiza prácticamente una navegación entre los datos origen y los dependientes. Solo es asignar el DataSet al DataGrid. 10 Todo lo referente al DataRelatión lo podemos encontrar en. http://msdn2.microsoft.com/es-es/library/system.data.datarelation(VS.80).aspx 1.7 Restriction. Representa la restricción de una acción impuesta a un conjunto de columnas en una relación entre clave principal y clave externa cuando se elimina o actualiza un valor o una fila. Paso a paso, con una tabla de campo único. Declarar los objetos que vamos a utilizar. ' Declarar las columnas de las relaciones para las restriciones Dim ColPrincipal As DataColumn Dim ColSecundaria As DataColumn Dim Restriccion As ForeignKeyConstraint Eliminar anteriores restricciones. ObjDataSet.Tables("Grupos").Constraints.Clear() Definir las columnas, campos que interviene en la relación de las tablas. ' Establecer los parámetros, grupos alumnos ColPrincipal = ObjDataSet.Tables("Grupos").Columns("CodGrupo") ColSecundaria = ObjDataSet.Tables("Alumnos").Columns("Grupo") Crear la restricción. Restriccion = New ForeignKeyConstraint("RestGrupoAlumno", _ ColPrincipal, _ ColSecundaria) Definir las condiciones en borrado, actualización. ' Establecer condiciones de la restricción Restriccion.DeleteRule = Rule.Cascade Restriccion.UpdateRule = Rule.Cascade Restriccion.AcceptRejectRule = AcceptRejectRule.Cascade Añadir la restricción a las tablas. ' Añadir la restricción ObjDataSet.Tables("Alumnos").Constraints.Add(Restriccion) Una vez creada la restricción, hay que marcar su utilización. ' Establecer a True la propiedad EnforceConstraints, ' forzar las restricciones. ObjDataSet.EnforceConstraints = True 11 El ejemplo completo queda como sigue. Private Sub CrearRestriccion() ' Declarar las columnas de las relaciones para las restriciones Dim ColPrincipal As DataColumn Dim ColSecundaria As DataColumn Dim Restriccion As ForeignKeyConstraint ObjDataSet.Tables("Grupos").Constraints.Clear() ' Establecer los parámetros, grupos alumnos ColPrincipal = ObjDataSet.Tables("Grupos").Columns("CodGrupo") ColSecundaria = ObjDataSet.Tables("Alumnos").Columns("Grupo") Restriccion = New ForeignKeyConstraint("RestGrupoAlumno", _ ColPrincipal, _ ColSecundaria) ' Establecer condiciones de la restricción Restriccion.DeleteRule = Rule.Cascade Restriccion.UpdateRule = Rule.Cascade Restriccion.AcceptRejectRule = AcceptRejectRule.Cascade ' Añadir la restricción ObjDataSet.Tables("Alumnos").Constraints.Add(Restriccion) ' Añadir la restricción ObjDataSet.Tables("AlumnAsig").Constraints.Add(Restriccion) ' Establecer a True la propiedad EnforceConstraints, ' forzar las restricciones. ObjDataSet.EnforceConstraints = True End Sub El ejemplo anterior es con un solo campo en la clave. El siguiente es con tablas de varios campos en su clave principal. Private Sub CrearRestriccion() ' Declarar las columnas de las relaciones para las restriciones Dim ColPri(3) As DataColumn Dim ColSec(3) As DataColumn Dim Restriccion As ForeignKeyConstraint ' Establecer los parámetros, asignaturas con asignaturas de alumnos ColPri(0) = ObjDataSet.Tables("Areas").Columns("CodNiv") ColPri(1) = ObjDataSet.Tables("Areas").Columns("CodEtapa") ColPri(2) = ObjDataSet.Tables("Areas").Columns("CodFam") ColPri(3) = ObjDataSet.Tables("Areas").Columns("CodArea") ColPri(0) ColPri(1) ColPri(2) ColPri(3) = = = = ObjDataSet.Tables("AlumnAsig").Columns("Nivel") ObjDataSet.Tables("AlumnAsig").Columns("Etapa") ObjDataSet.Tables("AlumnAsig").Columns("Familia") ObjDataSet.Tables("AlumnAsig").Columns("CodAsig") Restriccion = New ForeignKeyConstraint("RestAlumAsigAreas", _ ColPri(3), _ ColSec(3) 12 ' Establecer condiciones de la restricción Restriccion.DeleteRule = Rule.Cascade Restriccion.UpdateRule = Rule.Cascade Restriccion.AcceptRejectRule = AcceptRejectRule.Cascade ' Añadir la restricción ObjDataSet.Tables("AlumnAsig").Constraints.Add(Restriccion) ' Establecer a True la propiedad EnforceConstraints, ' forzar las restricciones. ObjDataSet.EnforceConstraints = True End Sub Todo lo referente al objeto lo podemos encontrar en el link: http://msdn2.microsoft.com/es-es/library/system.data.foreignkeyconstraint(VS.80).aspx 1.8 Definición de claves. El uso de las tablas conlleva la gestión de los registros y el control de las duplicidades y la localización de los datos, para ello se hace necesario el uso de clave principal. Hay dos caminos para conseguirlo, uno es capturarlas de la base de datos, y el otro generarlas. Para las tablas existentes en la base de datos, lo suyo es capturarlo, OleDbDataAdpater.FillSchema, es el método. Para las que se crean de forma provisional en el programa hay que crearlas. Veamos los dos sistemas. La captura de las claves de la tabla es sencilla. Try ObjDataSet.Tables("Titulos").Rows.Clear() ' limpia la tabla Catch ex As NullReferenceException Finally ' LLenar el adaptador Adaptador.Fill(Tabla) ' Capturar la clave desde la tabla Adaptador.FillSchema(Tabla, SchemaType.Source) End Try Solo queda comentar que es la ejecución de la línea con el método FillSchema. Su utilización posteriormente puede ser como sigue en el ejemplo, en el cual se utiliza para realizar la actualización del campo de entradas acumuladas. La explicación es : Creamos el array para la clave, es una clave de dos campos. Dim Clave(1) As Object Asignamos los datos al array. Clave(0) = CType(Fila.Item("Tipo"), Object) Clave(1) = CType(Fila.Item("Codigo"), Object) Creamos un objeto DataRow para capturar el registro existente con el método Find. Dim Registro As System.Data.DataRow Registro = ObjDataSet.Tables("Titulos").Rows.Find(Clave) 13 Si se encuentra se actualiza el dato, para ello se utiliza el valor del índice del registro leído. If Not (Registro Is Nothing) Then Cual = ObjDataSet.Tables("Titulos").Rows.IndexOf(Registro) SalAcu = CLng(Registro.Item("SalAcu").ToString) SalAcu = SalAcu + CLng(Fila.Item("Cantidad").ToString) ObjDataSet.Tables("Titulos").Rows(Cual).Item("Salacu") = SalAcu End If Y el código completo. Private Sub ActualizarStock(ByVal Fila As DataRow) Dim Clave(1) As Object Dim SalAcu As Long Dim Cual As Integer Clave(0) = CType(Fila.Item("Tipo"), Object) Clave(1) = CType(Fila.Item("Codigo"), Object) Dim Registro As System.Data.DataRow Registro = ObjDataSet.Tables("Titulos").Rows.Find(Clave) Try If Not (Registro Is Nothing) Then Cual = ObjDataSet.Tables("Titulos").Rows.IndexOf(Registro) SalAcu = CLng(Registro.Item("SalAcu").ToString) SalAcu = SalAcu + CLng(Fila.Item("Cantidad").ToString) ObjDataSet.Tables("Titulos").Rows(Cual).Item("Salacu") = SalAcu End If Catch ex As OleDb.OleDbException MsgBox(ex.Message, MsgBoxStyle.Information, _ "Error en actualización stocks") End Try End Sub La definición de una clave principal se realiza así, restricción de valor único llamado en VB. Se define el array y la clave. Dim ColPri(1) As DataColumn Dim Clave As UniqueConstraint se le asigna los valores pertinentes. ColPri(0) = Tabla.Columns("Tipo") ColPri(1) = Tabla.Columns("Codigo") se crea el objeto Clave, con el nombre “Principal”, indicando que es clave principal, “,True)”. Clave = New UniqueConstraint("Principal", ColPri, True) Se añade a la tabla. Tabla.Constraints.Add(Clave) 14 Y todo completo queda. Private Sub CrearClave(ByVal Tabla As System.Data.DataTable) Dim ColPri(1) As DataColumn Dim Clave As UniqueConstraint ColPri(0) = Tabla.Columns("Tipo") ColPri(1) = Tabla.Columns("Codigo") Clave = New UniqueConstraint("Principal", ColPri, True) Tabla.Constraints.Add(Clave) End Sub Lo referente a UniqueConstraint se puede acceder desde http://msdn2.microsoft.com/es-es/library/system.data.uniqueconstraint(VS.80).aspx 1.9 Transacción. Es el objeto que nos va a permitir realizar los procesos de actualización con la seguridad de poder mantener la integridad de la base de datos. La definición y utilización del mismo es como sigue. ' Definición del objeto Dim Transaccion As System.Data.OleDb.OleDbTransaction ' Inicio de la transacción Transaccion = Conexion.BeginTransaction(IsolationLevel.Chaos) ' Final correcto de la transacción Transaccion.Commit() ' Final por error de la transacción, abortar. Transaccion.Rollback() Solo queda integrar cada instrucción en el punto adecuado del programa. Cuando el proceso de transacción está en marcha, se ha de tener en cuenta no lanzarlo para un proceso muy amplio, si no en unidades lógicas de actualización, no para un programa completo vamos. Como el proceso de actualización se ejecuta para varias tablas, cada una de esas tablas se actualiza con su DataAdapter, y en cada uno de esos procesos de actualización es necesario la presencia de un objeto OleDbCommand, el objeto OleDbCommand necesita que se le pase el objeto Transacción que estemos utilizando en ese momento, por lo tanto el uso del objeto OleDbCommand quedaría como sigue: ' Cadena SQL Comando.CommandText = CadenaSQL ' Tipo de comando a ejecutar Comando.CommandType = CommandType.Text ' Conexión a utilizar, configurada previamente. Comando.Connection = Conexión ' Asignación del objeto transacción en curso. Comando.Transaction = Transaccion Por lo tanto en el procedimiento de actualización de cada una de las tablas del programa hay que hacer el cambio o que esté presente ' Asignación del objeto transacción en curso. Comando.Transaction = Transaccion para su correcto funcionamiento. El ejemplo que sigue empieza comprobando si hay cambios en el DataSet. 15 If ObjDataSet.HasChanges Then Después si es así, se entra en el proceso de actualizar. Se inicia la transacción. Conexion.Open() ' Inicio de la transacción Transaccion = Conexion.BeginTransaction(IsolationLevel.Chaos) Se asigna cada OleDbCommand con el objeto conexión y con la transacción. Comando.Connection = Conexion Comando.Transaction = Transaccion Se capturan los cambios en el DataSet copiándolos a un nuevo DataSet, en el ejemplo todos, modificados, borrados e insertados. ObjDataSetCamb = ObjDataSet.GetChanges(DataRowState.Modified _ Or DataRowState.Added _ Or DataRowState.Deleted) Después se repite tabla a tabla el proceso de actualización. Try AdapTitul.Update(ObjDataSetCamb.Tables("Titulos").GetChanges) ObjDataSet.Merge(ObjDataSet.Tables("Titulos")) ObjDataSet.AcceptChanges() Catch ex As ArgumentNullException Fallo = True End Try Hasta llegar al final en el que se comprueba como ha ido, y se confirma o se aborta la transacción. If Not Fallo Then ' Final correcto de la transacción Transaccion.Commit() MsgBox("Base actualizada.", MsgBoxStyle.Information, _ "Actualizando dataset") Else ' Final por error de la transacción Transaccion.Rollback() MsgBox("Actualización abortada.", MsgBoxStyle.Critical, _ "Actualizando dataset") End If 16 El ejemplo es completo: Private Sub ActualizarBaseDatos(ByVal Fallo As Boolean) Dim Transaccion As System.Data.OleDb.OleDbTransaction Try Dim ObjDataSetCamb As New DataSet If ObjDataSet.HasChanges Then Conexion.Open() ' Inicio de la transacción Transaccion = Conexion.BeginTransaction(IsolationLevel.Chaos) Comando.Connection = Conexion Comando.Transaction = Transaccion ComTiTi.Connection = Conexion ComTiTi.Transaction = Transaccion ComTitu.Connection = Conexion ComTitu.Transaction = Transaccion ComMovi.Connection = Conexion ComMovi.Transaction = Transaccion ObjDataSetCamb = ObjDataSet.GetChanges(DataRowState.Modified _ Or DataRowState.Added _ Or DataRowState.Deleted) Try AdapTitul.Update(ObjDataSetCamb.Tables("Titulos").GetChanges) ObjDataSet.Merge(ObjDataSet.Tables("Titulos")) ObjDataSet.AcceptChanges() Catch ex As ArgumentNullException Fallo = True End Try If Not Fallo Then Try AdapMovim.Update(ObjDataSetCamb.Tables("Movimientos").GetChanges) ObjDataSet.Merge(ObjDataSet.Tables("Movimientos")) ObjDataSet.AcceptChanges() Catch ex As ArgumentNullException Fallo = True End Try End If If Not Fallo Then ' Final correcto de la transacción Transaccion.Commit() MsgBox("Base actualizada.", MsgBoxStyle.Information,”Actualizar") Else ' Final por error de la transacción Transaccion.Rollback() MsgBox("Actualización abortada.", MsgBoxStyle.Critical, "Actualizar") End If Conexion.Close() Else MsgBox("No se han realizado cambios", MsgBoxStyle.Information, _ "Actualizando dataset") End If Catch ex As System.Data.OleDb.OleDbException MsgBox(ex.Message, MsgBoxStyle.Information, "Actualizando dataset") End Try End Sub Todo lo referente al objeto lo podemos encontrar en el link: http://msdn2.microsoft.com/es-es/library/system.transactions.transaction(VS.80).aspx 17