sábado, 28 de junio de 2014

Acceso genérico a una base de datos a través de una clase de .NET


Hace poco puse una entrada sobre como conectar una aplicación a una base de datos.
En esta ocasión voy a desarrollar como podría ser una clase completa de conexión a base de datos, aunque esta se puede modificar para ampliar mucho más. Esta es la clase mínima. Después se puede ampliar lo que se quiera.


Clase genérica de conexión a Base de Datos


Para conectar nuestra aplicación a una base de datos crearemos todo lo relativo al manejo de dicha base en una clase llamada CBaseDatos en la que encapsularemos toda la funcionalidad relativa a la conexión con ella.

La clase deberá heredar de:
Imports System.Data.SqlClient
Imports System.Data

Y también de nuestra aplicación, en este caso un programa creado en nuestro namespace para manejo de la funcionalidad de una empresa genérica.

Imports MyNamespace.EmpresaGenerica

La clase en sí, contiene los métodos para la conexión y desconexión de la base de datos.

Public Class CBaseDatos

    'CONEXION A UNA BASE DE DATOS

    Public dBConexion As SqlClient.SqlConnection
    Public dBComando As SqlClient.SqlCommand
    Private dBDataSet As DataSet
    Private dbDataReader As SqlClient.SqlDataReader
    Private dbDataTable As DataTable
    Private dbDataAdapter As SqlClient.SqlDataAdapter

   
'Método genérico de conexión a base de datos
Public Sub conectar(ByVal CadConectarSql As String)
        dBConexion = New SqlClient.SqlConnection
        dBConexion.ConnectionString = CadConectarSql
        Try
            dBConexion.Open()
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    End Sub

'Método genérico de desconexión a base de datos
Public Sub desconectar()
      dBConexion.Close()
End Sub


'ejecución genérica de consultas de tipo SELECT
Private Function conSelect(ByVal cadConsul As String) As DataTable
        Dim dtTabla As New DataTable
        Try
            dBComando = New SqlClient.SqlCommand(cadConsul, dBConexion)
            dtTabla = New DataTable()
        Catch
            ' // MessageBox.Show(ex.Message);
            MsgBox("Se produjo un error al ejecutar la SELECT")
        End Try
        Return dtTabla
    End Function

'ejecución genérica de comandos SQL, INSERT, UPDATE Y DELETE (NO SELECT)
Private Function conNoSelect(ByVal cadNoSelect As String) As DataTable
        Dim dtTabla As New DataTable
        Try
            dBComando = New SqlClient.SqlCommand(cadNoSelect, dBConexion)
            dBComando.ExecuteNonQuery()
        Catch
            ' // MessageBox.Show(ex.Message);
            MsgBox("Se produjo un error al ejecutar el comando SQL")
        End Try
        Return dBComando.ExecuteScalar
    End Function

Los métodos expuestos arriba son los métodos privados que forman parte del núcleo principal de la clase, pero ahora para darle más funcionalidad y un interfaz con el exterior vamos a crear otros métodos públicos que harán uso de los métodos privados genéricos anteriormente expuestos, de este modo crearemos una interfaz con el exterior mucho más amigable con los métodos Consulta, Alta, Modificación etc, mucho más intuitivos para el programador que los nombres ConSelect y ConNoSelect
  
'Método genérico de consulta  de registros en una tabla de una base de 'datos. 'Este método público llama al método privado 'conSelect para la 'ejecución de una 'SELECT

Public Function Consulta(ByVal Tabla As String, ByVal ListaCampos As String, ByVal Condicion As String) As DataTable
        Dim strQRY As String
        Dim objGrabar As New CBaseDatos
        Dim Resulset As New DataTable

        strQRY = "SELECT " & ListaCampos & " FROM " & Tabla & " WHERE " & Condicion

        Resulset = objGrabar.conSelect(strQRY)

        Return Resulset

End Function

   
'Método genérico de alta de registros en una tabla de una base de datos. 'Este método público llama al método privado conNoSelect para la 'ejecución de un INSERT

Public Function Alta(ByVal Tabla As String, ByVal ListaValores As String) As Boolean
        Dim strQRY As String
        Dim objGrabar As New CBaseDatos

        strQRY = "INSERT INTO " & Tabla & " VALUES " & ListaValores

        objGrabar.conNoSelect(strQRY)

End Function

'Método genérico de modificación de registros en una tabla de una base 'de datos. Este método público llama al método privado conNoSelect para 'la ejecución de un UPDATE

Public Function ModificacionSimple(ByVal Tabla As String, ByVal Campo As String, ByVal valor As VariantType, ByVal Condicion As String) As Boolean
        Dim strQRY As String
        Dim objGrabar As New CBaseDatos

        strQRY = "UPDATE " & Tabla & " SET " & Campo & " = " & valor & " WHERE " & Condicion

        objGrabar.conNoSelect(strQRY)

End Function

'Método genérico de modificación de registros en una tabla de una base 'de datos. Este método público llama al método privado conNoSelect para 'la ejecución de un UPDATE
'este método es igual que el de arriba pero en vez de modificar un solo 'campo modifica varios, para ello hay que cerciorarse de que el 'parámetro ListaCamposyValores viene correctamente formado. Para ello 'podemos crear otro método que lo chequee.

Public Function ModificacionMultiple(ByVal Tabla As String, ByVal ListaCamposyValores As String, ByVal Condicion As String) As Boolean
        Dim strQRY As String
        Dim objGrabar As New CBaseDatos

        strQRY = "UPDATE " & Tabla & " SET " & ListaCamposyValores & " WHERE " & Condicion

        objGrabar.conNoSelect(strQRY)

End Function

'método genérico de borrado de registros en una tabla de una base de 'datos. Este método público llama al método privado conNoSelect para la 'ejecución de un DELETE. Borra directamente de la base de datos una 'línea o un grupo de líneas

Public Function Baja(ByVal Tabla As String, ByVal Condicion As String) As Boolean
        Dim strQRY As String
        Dim objGrabar As New CBaseDatos

        strQRY = "DELETE " & Tabla & " WHERE " & Condicion

        objGrabar.conNoSelect(strQRY)

End Function


Estas serían las funciones básicas de manejo de base de datos, a partir de aquí se puede ampliar todo lo que se quiera, en este caso he incluido algunas para obtener las tablas de una base de datos y las columnas de una tabla dada.

'UTILIDADES

'Devuelve los nombres de las tablas de una base de datos dada
Public Function NombresTablas(ByVal nombreBase As String, ByVal CadenaConexion As String) As String()
         
        Dim dataTable As New Data.DataTable()
        Dim i As Integer
        Dim nomTablas() As String
        ReDim nomTablas(0)
        nomTablas(0) = ""
        If dBConexion Is Nothing Then
            dBConexion = New Data.SqlClient.SqlConnection(CadenaConexion)
        End If
        If dBConexion.State <> ConnectionState.Open Then
            dBConexion.Open()
        End If

        Dim schemaDA As New Data.SqlClient.SqlDataAdapter("SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_TYPE", dBConexion)

        schemaDA.Fill(dataTable)
        i = dataTable.Rows.Count - 1
        If i > -1 Then
            ReDim nomTablas(i)

            For i = 0 To dataTable.Rows.Count - 1
                nomTablas(i) = dataTable.Rows(i).Item("TABLE_NAME").ToString()
            Next
        End If
        Return nomTablas

End Function

'Devuelve los nombres de las columnas de una tabla dada
Public Function NombresColumnas(ByVal nombreTabla As String) As String()
        Dim columna As Data.DataColumn
        Dim i, j As Integer
        Dim nomCol() As String

        j = dBDataSet.Tables(nombreTabla).Columns.Count - 1
        ReDim nomCol(j)
        For i = 0 To j
            columna = dBDataSet.Tables(nombreTabla).Columns(i)
            nomCol(i) = columna.ColumnName
        Next
        Return nomCol
    End Function

También se pueden incluir métodos para manejo de ListWiew y combos relacionados con la Base de datos.

'Borra de la base de datos la línea o líneas elegidas en un control 'listview
Public Sub Eliminar(ByVal List As ObjectByVal NombreTabla As String)
        ' Eliminar la fila indicada
        Dim fila As Data.DataRow
        
        fila = CType(List.SelectedItems(0).Tag, Data.DataRow)
        dBDataSet.Tables(NombreTabla).Rows.Remove(fila)
        List.Items.Remove(List.SelectedItems(0))


End Sub

'Este método se llama AsignarCabeceraLista y tendrá dos 'implementaciones, una indicando sólo el nombre del ListView y la otra 'en la que además se indicará un control ComboBox en el cual se 'asignarán también esos nombres de las columnas de la tabla.

Public Sub AsignarCabeceraLista(ByVal List As Object, ByVal NombreTabla As String)

        Dim i As Integer

        Dim lasColumnas() As String
        lasColumnas = NombresColumnas(NombreTabla)

        If Not lasColumnas Is Nothing Then
            For i = 0 To lasColumnas.Length - 1
                List.Columns.Add(lasColumnas(i), 100)
            Next
        End If

    End Sub

    Public Sub AsignarCabeceraLista(ByVal List As Object, ByVal NombreTabla As String, ByVal cboCampos As Object)
        Dim i As Integer
        '
        AsignarCabeceraLista(List, NombreTabla)
        cboCampos.Items.Clear()
        For i = 0 To List.Columns.Count - 1
            cboCampos.Items.Add(List.Columns(i).Text)
        Next

        If cboCampos.Items.Count > 0 Then
            cboCampos.SelectedIndex = 0
        End If
    End Sub

 'rellenará un control ListView con los datos de la tabla cargada en el 'objeto DataSet.

    Friend Sub LLenarLista(ByVal unListView As Object, ByVal NombreTabla As String)
        Dim i As Integer
        Dim lwItem As Object
        Dim fila As Data.DataRow

        lwItem = ""

        unListView.Items.Clear()
        For Each fila In dBDataSet.Tables(NombreTabla).Rows
            For i = 0 To unListView.Columns.Count - 1
                If i = 0 Then
                    lwItem = unListView.Items.Add(fila(i).ToString)
                    lwItem.Tag = fila
                Else
                    lwItem.SubItems.Add(fila(i).ToString)
                End If
            Next
        Next
    End Sub
End Class

Estas funciones son básicas, a partir de aquí se puede añadir más funciones o agregar funcionalidad nueva a las existentes. Siempre con el objetivo de reducir el código fuente del programa origen pero sin hacer perder la funcionalidad genérica a las funciones aquí descritas.

2 comentarios:

  1. Que buena explicación, mejor no se puede. Te felicito y me ayudaste a estudiar mucho!

    ResponderEliminar
  2. Eso lo explico aqui: http://analisisyprogramacionoop.blogspot.com.es/2013/07/como-conectar-una-aplicacion-net-una.html

    ResponderEliminar