Generador de Código

De acuerdo a una investigación realizada en interne un generador de código permite agilizar el desarrollo de aplicaciones o proyectos de base de datos. Esto se hace con el propósito de ahorrar tiempo y a la vez programar dentro de todo algo, es una buena forma de no caer en la típica programación con toda la lógica pegada a la interfaz al estilo VB6 cuando necesitas hacer algo rápido. Así queda algo elegante sin que ello nos involucre horas y horas de programar capas. Todo esto empezó hace mucho tiempo desde que empezó a tratar de trabajar o desarrollar aplicaciones que trabajan con datos y se dieron cuenta que era muy tedioso eso de ir escribiendo todo el código para realizar un simple select, insert, update o delete, sobretodo si la tabla con la que estaba trabajando tenía como 30 campos o más, entonces la pregunta del millón ¿Habrá alguna forma de facilitar este trabajo o que tipo de acción se quiere ejecutar sobre cierta tabla?

Algunas personas al tener la herramienta de Internet trataron de realizar búsquedas de información al respecto no encontrándose mayor cosa por lo que se procedió a trabajar para desarrollar uno por si mismo.

El resultado es una clase que primero se hizo para una aplicación desarrollada en Visual Basic 6.0 y funcionó y ahora se transformo a VB.NET, el código es:

Option Strict Off
Imports System.Data.SqlClient
''' <summary>
''' Encapsula métodos necesarios para obtener información
''' interna de tablas de SQL Server.
''' </summary>
''' <remarks>Victor Viscarra © 2007</remarks>
Public Class CN_SQL
   Private cs As String
   Public Enum SQLTYPE
    eSelect
    eInsert
    eUpdate
    eDelete
  End Enum
 
 
  Public Sub New(ByVal CadenaConexion As String)
    cs = CadenaConexion
  End Sub
 
  ''' <summary>
  ''' Obtiene la lista de campos que contiene una tabla.
  ''' </summary>
  ''' <param name=Tabla">Nombre de la tabla.</param>"
  Private Function GetListaCampos(ByVal Tabla As String) As DataTable
    Dim dt As New DataTable
    Dim sql As String
    Dim cpk As Integer
 
    sql = "select a.name as Nombre, cast(a.colorder as int) as Orden,"
    sql += " cast(b.name as varchar) as Tipo,"
    sql += " Length = "
    sql += "   case"
    sql += "     when b.name = 'char' or b.name = 'varchar' then cast(a.length as int)"
    sql += "     else cast(a.xprec as int)"
    sql += "   end,"
    sql += "'' as Descripcion, 0 as isPK, a.isNullable,"
    sql += " isIdentity = "
    sql += "   case"
    sql += "     when a.autoval is null then 0"
    sql += "     else 1"
    sql += "   end"
    sql += " from syscolumns a inner join systypes b"
    sql += " on (a.xtype = b.xtype and a.xusertype = b.xusertype)"
    sql += " where a.id = (object_id(N'[dbo].[{0}]'))"
    sql += " order by colorder"
 
    sql = String.Format(sql, Tabla)
 
    Try
      Dim da As New SqlDataAdapter(sql, cs)
      da.Fill(dt)
 
      ' Llena los datos de descripción y primary key.
      If Not dt Is Nothing Then
        For Each dr As DataRow In dt.Rows
          dr("Descripcion") = Me.GetDesCampo(Tabla, dr("Nombre").ToString)
          dr("isPK") = Me.IsPKCampo(Tabla, dr("Nombre").ToString)
          If CInt(dr("isPK")) = 1 Then cpk += 1
        Next
 
      End If
    Catch ex As Exception
      Throw ex
    End Try
 
    Return dt
  End Function
 
  ''' <summary>
  ''' Obtiene la descripción de un campo de la Base de Datos.
  ''' </summary>
  ''' <param name=Tabla">Nombre de la tabla propietaria del campo</param>"
  ''' <param name=Campo">Campo del que se quiere obtener la descripción</param>"
  Private Function GetDesCampo(ByVal Tabla As String, ByVal Campo As String) As String
    Dim sql As String
    Dim ret As String = Nothing
    Dim o As Object
    Dim cn As New SqlConnection(cs)
 
    sql = "select value from ::fn_listextendedproperty ('MS_Description','user','dbo','table',"
    sql += "'{0}','column','{1}')"
    sql = String.Format(sql, Tabla, Campo)
 
    Try
      Dim cm As New SqlCommand
 
      cn.Open()
      With cm
        .Connection = cn
        .CommandType = CommandType.Text
        .CommandText = sql
        .ExecuteNonQuery()
        o = .ExecuteScalar()
      End With
 
      If Not o Is Nothing Then
        ret = o.ToString()
      End If
 
    Catch ex As Exception
      If TypeOf ex Is InvalidCastException Then
        ret = Nothing
      Else
        Throw ex
      End If
    Finally
      If cn.State <> ConnectionState.Open Then
        cn.Close()
      End If
      cn.Dispose()
    End Try
 
    Return ret
  End Function
 
  ''' <summary>
  ''' Obtiene un valor que indica si un campo forma parte del Primary Key
  ''' de la tabla de acuerdo al parámetro pasado.
  ''' </summary>
  ''' <param name=Tabla">Nombre de la tabla propietaria del campo</param>"
  ''' <param name=Campo">Campo del que se quiere obtener la información</param>"
  Private Function IsPKCampo(ByVal Tabla As String, ByVal Campo As String) As Integer
    Dim ret As Integer
    Dim c As String = String.Empty
    Dim sql As String
    Dim o As Object
    Dim cn As New SqlConnection(cs)
 
    sql = "select 'T' from sysindexkeys ik, sysindexes i"
    sql += " Where ik.id = i.id"
    sql += " and ik.indid = i.IndId"
    sql += " and ik.colid = (select colid from  syscolumns"
    sql += " where id = (object_id(N'[dbo].[{0}]'))"
    sql += " and name = '{1}')"
    sql += " and i.id = (object_id(N'[dbo].[{0}]'))"
    sql += " and i.name in (SELECT name"
    sql += " From sysobjects"
    sql += " where (xtype = 'PK'))"
 
    sql = String.Format(sql, Tabla, Campo)
 
    Try
      Dim cm As New SqlCommand
      cn.Open()
 
      With cm
        .Connection = cn
        .CommandType = CommandType.Text
        .CommandText = sql
        o = .ExecuteScalar()
      End With
 
      If Not o Is Nothing Then
        c = o.ToString()
      End If
 
      If c = "T" Then ret = 1
    Catch ex As Exception
      If TypeOf ex Is InvalidCastException Then
        ret = 0
      Else
        Throw ex
      End If
    Finally
      If cn.State <> ConnectionState.Open Then
        cn.Close()
      End If
      cn.Dispose()
    End Try
 
    Return ret
  End Function
 
  ''' <summary>
  ''' Genera una Sentencia SQL (DML) de acuerdo al nombre de la tabla y tipo
  ''' que se desee.
  ''' </summary>
  ''' <param name=Tabla">Nombre de la tabla</param>"
  ''' <param name=TipoDML">Tipo de DML que se quiere obtener</param>"
  Public Function GeneraSQL(ByVal Tabla As String, ByVal TipoDML As SQLTYPE) As String
    Dim i, controlPK, fc As Integer
    Dim sql As String = String.Empty
    Dim sql2 As String
    Dim dt As DataTable
 
    ' Obtiene todos los datos necesarios de cada
    ' campo existente de la tabla
    Try
      dt = Me.GetListaCampos(Tabla)
    Catch ex As Exception
      dt = Nothing
      Throw ex
    End Try
 
    If Not dt Is Nothing Then
      fc = dt.Rows.Count - 1
 
      Select Case TipoDML
        Case SQLTYPE.eSelect
          ' Genera la cadena SQL <<Select>>
          sql = "SELECT "
          For Each dr As DataRow In dt.Rows
            sql += dr("Nombre")
            If i < fc Then
              sql += ", "
              i += 1
            Else
              sql += vbCrLf + "FROM {0}"
            End If
          Next
 
        Case SQLTYPE.eInsert
          ' Genera la cadena SQL <<Insert Into>>
          sql = "INSERT INTO {0}" + vbCrLf + " ("
          sql2 = "VALUES" + vbCrLf + " ("
 
          For Each dr As DataRow In dt.Rows
            If dr("isIdentity") = 0 Then
              sql += dr("Nombre")
              sql2 += "@" + dr("Nombre")
              If i < fc Then
                sql += ", "
                sql2 += ", "
              Else
                sql += ")"
                sql2 += ")"
                sql += vbCrLf + sql2
              End If
            End If
            i += 1
          Next
 
        Case SQLTYPE.eUpdate
          ' Genera la cadena SQL <<Update>>
          sql = "UPDATE {0}" + vbCrLf + "SET "
          controlPK = 0
          For Each dr As DataRow In dt.Rows
            If dr("isPK") = 0 Then
              controlPK += 1
              sql += dr("Nombre") + " = @" + dr("Nombre")
              If i < fc Then
                sql += ", " + vbCrLf
              Else
                sql += vbCrLf + "WHERE "
              End If
            End If
            i += 1
          Next
 
          If controlPK = 0 Then
            sql = "NO SE PUEDE GENERAR EL COMANDO UPDATE PARA LA TABLA {0}"
            sql += " DEBIDO A QUE NO POSEE CAMPOS ACTUALIZABLES."
            sql = String.Format(sql, Tabla)
            Throw New Exception(sql)
          End If
 
          controlPK = 0
          i = 0
          For Each dr As DataRow In dt.Rows
            If CInt(dr("isPK")) = 1 Then
              controlPK += 1
              sql += dr("Nombre") + " = @" + dr("Nombre")
              sql += vbCrLf + " AND "
            End If
          Next
 
          If controlPK = 0 Then
            sql = "NO SE PUEDE GENERAR EL COMANDO UPDATE PARA LA TABLA {0}"
            sql += " DEBIDO A QUE NO POSEE PRIMARY KEY."
            sql = String.Format(sql, Tabla)
            Throw New Exception(sql)
            Exit Select
          End If
 
          sql = sql.Substring(0, sql.Length - 7)
 
        Case SQLTYPE.eDelete
          ' Genera la cadena SQL <<Delete>>
          sql = "DELETE FROM {0}"
          sql += vbCrLf + "WHERE "
 
          controlPK = 0
          For Each dr As DataRow In dt.Rows
            If CInt(dr("isPK")) = 1 Then
              controlPK += 1
              sql += dr("Nombre") + " = @" + dr("Nombre")
              sql += vbCrLf + " AND "
            End If
          Next
 
          If controlPK = 0 Then
            sql = "NO SE PUEDE GENERAR EL COMANDO UPDATE PARA LA TABLA {0}"
            sql += " DEBIDO A QUE NO POSEE PRIMARY KEY."
            sql = String.Format(sql, Tabla)
            Throw New Exception(sql)
            Exit Select
          End If
           sql = sql.Substring(0, sql.Length - 7)
      End Select
 
      If Not String.IsNullOrEmpty(sql) Then
        sql = String.Format(sql, Tabla)
      End If
     End If
     Return sql
  End Function
End Class