Notas .NET

Thursday, August 02, 2007

Ejecutar sentencias SQL de modo práctico

Para ello utilizaremos una función llamada RunSQLBD que puede llamarse de dos formas: una es simplemente enviándole como parámetro una sentencia SQL y la otra es enviándole la sentencia más los parámetros SQL que deseemos utilizar.

En ambos casos las sentencias deben ser de tipo INSERT, UPDATE o DELETE. Los parámetros SQL evitan que alguien realice el famoso SQL Injection, modificando nuestras sentencias a su antojo y realizando estragos en nuestra base de datos.

Recordemos que .NET es un lenguaje que permite la abstracción, así que veamos primero cómo utilizar la función RunSQLBD para mostrar lo sencillo que es. Supongamos que tenemos una aplicación que manipula una libreta de direcciones y el usuario ha proporcionado los datos necesarios y es momento de guardar un nuevo registro. La siguiente función recibe la información (que previamente hemos validado) y realiza la inserción.


Sub GuardarLibretaDireccion(ByVal StrNombre As String, ByVal StrCorreo As String, ByVal StrNotas As String, ByVal BlnActivo As Boolean)
Dim StrSQL As String = ""
StrSQL &= " INSERT INTO LibretaDirecciones(Nombre, Correo, Notas, Activo)"
StrSQL &= " VALUES(@Titulo, @Correo, @Notas, @Activo)"
Try
Dim Parametros(3) As OleDbParameter
Parametros(0) = New OleDbParameter("@Nombre", OleDbType.VarChar, 255)
Parametros(1) = New OleDbParameter("@Correo", OleDbType.VarChar, 100)
Parametros(2) = New OleDbParameter("@Notas", OleDbType.LongVarChar, 8000)
Parametros(3) = New OleDbParameter("@Activo", OleDbType.Boolean)
Parametros(0).Value = StrNombre
Parametros(1).Value = StrCorreo
Parametros(2).Value = StrNotas
Parametros(3).Value = BlnActivo
RunSQLBD(StrSQL, Parametros)
Catch ex As Exception
'AQUÍ REALIZAMOS ALGUNA ACCIÓN SI ALGO FALLA (QUIZÁ QUIERAS RECIBIR POR CORREO EL DETALLE DEL ERROR.
End Try
End Sub


Ahora veamos el mismo ejemplo pero sin utilizar parámetros SQL:


Sub GuardarLibretaDireccion(ByVal StrNombre As String, ByVal StrCorreo As String, ByVal StrNotas As String, ByVal BlnActivo As Boolean)
Dim StrSQL As String = ""
StrSQL &= " INSERT INTO LibretaDirecciones(Nombre, Correo, Notas, Activo)"
StrSQL &= " VALUES('" & StrNombre & "', '" & StrCorreo & "', '" & StrNotas & "', '" & BlnActivo & "')"
Try
RunSQLBD(StrSQL, Parametros)
Catch ex As Exception
'AQUÍ REALIZAMOS ALGUNA ACCIÓN SI ALGO FALLA (QUIZÁ QUIERAS RECIBIR POR CORREO EL DETALLE DEL ERROR.
End Try
End Sub


Fácil, ¿no?

Ahora sí! Aquí están las dos funciones que lo hacen todo. Hemos colocado Overloads para que se entienda que ambas tendrán el mismo nombre. Si al llamar a RunSQLBD le colocamos un solo parámetro se ejecutará la primera, si por otro lado colocamos 2 parámetros entonces simplemente se ejecutará la segunda.


Public Overloads Sub RunSQLBD(ByVal StrSQL As String)
Dim dbConnection As New OleDbConnection(System.Web.Configuration.WebConfigurationManager.AppSettings(GetBDKey()))
Dim dbCommand As New OleDbCommand(StrSQL, dbConnection)
Try
dbConnection.Open()
dbCommand.ExecuteNonQuery()
Catch ex As Exception
Throw New Exception(ex.Message, ex.InnerException)
'THROW NOS PERMITIRÁ CACHAR EL ERROR EN CUALQUIER LUGAR DONDE SE MANDE LLAMAR ESTA RUTINA
Finally
dbConnection.Close()
End Try
End Sub

Public Overloads Sub RunSQLBD(ByVal StrSQL As String, ByVal SqlParametros As OleDbParameter())
Dim dbConnection As New OleDbConnection(System.Web.Configuration.WebConfigurationManager.AppSettings(GetBDKey()))
Dim dbCommand As New OleDbCommand(StrSQL, dbConnection)
For Each objpar As OleDbParameter In SqlParametros
dbCommand.Parameters.Add(objpar)
Next

Try
dbConnection.Open()
dbCommand.ExecuteNonQuery()
dbConnection.Close()
Catch ex As Exception
Throw New Exception(ex.Message, ex.InnerException)
'THROW NOS PERMITIRÁ CACHAR EL ERROR EN CUALQUIER LUGAR DONDE SE MANDE LLAMAR ESTA RUTINA
Finally
dbConnection.Close()
End Try
End Sub


Es bastante código para estas dos funciones. Pero de esta forma se ejecutarán sentencias de un modo seguro y no tendremos que escribir todo este código cada vez que queramos realizar una acción en la base de datos.

Ah! Eso de "MiBasedeDatos" significa que hemos creado en el archivo web.config una etiqueta de tipo:

<appSettings>
<add key="MiBasedeDatos" value="Provider=Microsoft.Jet.OLEDB.4.0; Data Source=C:\...\Northwind.mdb"/>
</appSettings>


Este ejemplo se basa en Access; si estás utilizando SQL tendrás que cambiar los objetos OleDb a SQL, por ejemplo OleDbParameter por SqlParameter. Y claro, el "value" que aparece en el código perteneciente al web.config cambiará por una cadena de conexión que sea correcta para tu base de datos SQL.

Manejando llaves primarias en un DataSet

Supongamos que requerimos una tabla que tenga dos columnas: ID y Nombre. La primera columna (ID) deberá ser llave primaria y además será de tipo "UniqueIdentifier" de modo que nos permita almacenar valores alfanuméricos y que no se repitan.

Dim dsProcesadas As New DataSet("MiDataSet")
Dim newTable1 As New DataTable("MiTabla1")
dsProcesadas.Tables.Add(newTable1)

newTable1.Columns.Add("ID", Type.GetType("System.Guid"), "")
newTable1.Columns.Add("Nombre", Type.GetType("System.String"), "")

Dim key(0) As DataColumn
key(0) = newTable1.Columns("ID")
newTable1.PrimaryKey = key


¿Cómo añado registros?

Es muy fácil, podemos crear una rutina muy aparte que lo haga y que reciba como parámetros los valores de las columnas. No nos confundamos, veamos el ejemplo:

Sub CreaRegistro(ByVal GuidID As Guid,ByVal StrNombre As String, ByRef ds As DataSet)
   Dim row As DataRow
   row = ds.Tables("MiTabla1").NewRow
   ds.Tables("MiTabla1").Rows.Add(row)
   row("ID") = GuidID
   row("Nombre") = StrNombre
End Sub


Juntemos todo el código en la siguiente función:

<script runat="server">

Sub Page_Load()
   Dim ds As new DataSet
   ds = GetDataSetPrincipal()
   '# A PARTIR DE AQUI PODEMOS HACER Y DESHACER
   '# EN EL DATASET
End Sub

Function GetDataSetPrincipal() As DataSet
   Dim ds As New DataSet("MiDataSet")
   Dim newTable1 As New DataTable("MiTabla1")
   ds.Tables.Add(newTable1)

   newTable1.Columns.Add("ID", Type.GetType("System.Guid"), "")
   newTable1.Columns.Add("Nombre", Type.GetType("System.String"), "")

   Dim key(0) As DataColumn
   key(0) = newTable1.Columns("ID")
   newTable1.PrimaryKey = key

   Dim GuidJuan As Guid = New Guid("9CD8769C-7DEB-40A0-828E-DBB03556B451")
   Dim GuidPedro As Guid = New Guid("9CD8769C-7DEB-40A0-828E-DBB03556B452")
   Dim GuidAna As Guid = New Guid("9CD8769C-7DEB-40A0-828E-DBB03556B453")
   CreaRegistro(GuidJuan, "Juan" , ds)
   CreaRegistro(GuidPedro, "Pedro" , ds)
   CreaRegistro(GuidAna, "Ana" , ds)

   Return ds
End Function

Sub CreaRegistro(ByVal GuidID As Guid,ByVal StrNombre As String, ByRef ds As DataSet)
   Dim row As DataRow
   row = ds.Tables("MiTabla1").NewRow
   ds.Tables("MiTabla1").Rows.Add(row)
   row("ID") = GuidID
   row("Nombre") = StrNombre
End Sub

</script>