viernes, octubre 13, 2006

Unir 2 tablas de diferentes bases de datos

esta es rapida. lo unico importante aqui es que deben estar en la misma instancia de SQL las bases de datos, y si hay problemas, es solo cuestion de revisar la configuracion del COLLATE

select * from BD1..Tabla1inner join BD2..Tabla2On BD1..Tabla1.Camp=BD2..Tala2.Camp COLLATE Compatibility_52_409_30003

miércoles, agosto 30, 2006

El WEB.CONFIG

Vamos a darle un vistaso al webconfig de asp.net 2.


<?xml version="1.0"
?>
<configuration>
<connectionStrings>
<!-- En esta seccion como podemos ver podemos definir todas las cadenas de conexion que necesitemos para accesar a las bases de datos, cabe mencionar la forma en la que estoy llamando al server, ya que lo hago especificando el protocolo, la ip y el puerto por el cual estoy accediendo al servidor de SQL, esto es ya que me he topado con problemas con aplicaciones de asp.net2 que corren con el sistema operativo de Windows XP SP2, y Windows 2000 SP 4 ya que marcaban que no encontraban el servidor al utilizar name pipes -->

<add name="MiBase" connectionString=" server=TCP:192.162.1.3,1433; database=MiBase; UID=muusuario; PWD=mipass; Connect Timeout=60;Connection LifeTime=5" providerName="System.Data.SqlClient"
/>

</connectionStrings>

</configuration>

<!--Bueno aqui estoy definiendo un tema que aplicara a todo el sitio -->
<pages theme="Default">
<!--aqui importamos todos los name spaces que normalmente utilizo, y con esto me ahorro el estar importando los namespaces pagina por pagina, ya que se importan en toda la aplicacion-->
<namespaces>

<add namespace="System.Data"/>
<add namespace="System.Data.SQLClient"/>
<add namespace="System.Web"/>
<add namespace="System.Net"/>
<add namespace="System.IO"/>
<add namespace="System.Collections.Generic"/>
<add namespace="System.Globalization"/>

</namespaces>


<!--Aqui se estan importando controles que he creado y que se pueden importar como las etiquetas de asp.net-->
<controls>
<add tagPrefix="MiControl" namespace="Tabstrip"/>
<add tagPrefix="MiControl" namespace="datepicker"/>


<!--Aqui se estan importando web user Controls que he creado y que se pueden importar como las etiquetas de asp.net-->
<add tagPrefix="MiControl" namespace="WebMenu"/>
</controls>



Poco a poco voy a ir indroduciendo mas informacion del webconfig

miércoles, agosto 23, 2006

AutoPostBack en FORMS con JavaScript

Esta página HTML tiene la capacidad de recibir un parámetro que puede definir el "action" de un formulario y además recibir varios parámetros para enviarlos al URL del action.

Ejemplifiquemos esto.

Tenemos 3 páginas A.htm, M.htm y Z.htm.

La página A contendrá un link hacia M que enviará ciertos parámetros, por ejemplo:
<a target="_blank" href="M.htm?action=Z.htm
&param1=value1&param2=value2&param2=value2"></a>

De ese modo M (que es la página que vamos a crear) recibirá todos los parámetros y los enviará a Z.htm.


<script language="javascript">
<!--//
function Envia()
{
var query = window.location.search.substring(1);
var vars = query.split("&");
var BlnHayAction = 0;

for (var i=0;i<vars.length;i++)
{
var pair = vars[i].split("=");
{
if (pair[0] == "action"){document.Form1.action = pair[1]; BlnHayAction = 1;}
}
}

if (BlnHayAction == 1)
{
for (var i=0;i<vars.length;i++)
{
var pair = vars[i].split("=");
if (pair[0] == "param1"){document.getElementById("param1").value = pair[1];}
if (pair[0] == "param2"){document.getElementById("param2").value = pair[1];}
if (pair[0] == "param3"){document.getElementById("param3").value = pair[1];}
}

document.Form1.submit();
}
}

//-->
</script>

<html><body onload="Envia()">

<form id="Form1" name="Form1" method="post">
<input name="param1" type="hidden" id="param1">
<input name="param2" type="hidden" id="param2">
<input name="param3" type="hidden" id="param3">
</form>

</body></html>

Validar el tipo de archivo a subir

Bueno vamos a ver una manera rapida de hacer una revision del tipo de archivo que deseamos cargar a nuestro servidor, para que de esta manera decidamos si es valido el tipo de archivo que el usuario desea subir.

Este codigo va en el botón de cargar los archivos

Dim MyFileCollection As HttpFileCollection = Request.Files
Response.Write(Request.Files.Count)
For Loop1 as integer = 0 To MyFileCollection.Count - 1
If MyFileCollection(Loop1).ContentType = "video/mpeg" Then 'no pasa
Response.Write("mpg
")
ElseIf MyFileCollection(Loop1).ContentType = "audio/mpeg" Then 'no pasa
Response.Write("mp3
")
ElseIf MyFileCollection(Loop1).ContentType = "text/plain" Then 'no pasa
'accion
ElseIf MyFileCollection(Loop1).ContentType = "image/jpeg" Then
'accion
ElseIf MyFileCollection(Loop1).ContentType = "image/gif" Then
'accion
ElseIf MyFileCollection(Loop1).ContentType = "application/xml" Then
'accion
ElseIf MyFileCollection(Loop1).ContentType = "application/msword" Then
'accion
ElseIf MyFileCollection(Loop1).ContentType = "application/x-zip-compressed" Then 'no pasa
'accion
ElseIf MyFileCollection(Loop1).ContentType = "video/x-ms-wmv" Then
'accion
ElseIf MyFileCollection(Loop1).ContentType = "application/octet-stream" Then
'accion
ElseIf MyFileCollection(Loop1).ContentType = "application/vnd.ms-excel" Then
'accion
ElseIf MyFileCollection(Loop1).ContentType = "video/x-ms-wmv" Then
'accion
End If
Next Loop1

En cuanto tenga mas informacion ampliare este ejemplo.

jueves, agosto 17, 2006

Una estrategia para evitar SQL Injection

Cuando accesamos a la base de datos buscando información o bien insertando nueva información corremos el riesgo de ser atacados mediante SQL Injection.

¿Por qué o cómo pasa esto?
Cuando un usuario introduce información en los formularios que hemos creado en nuestra página, éste texto es concatenado de algún modo en sentencias SQL para realizar queries. Por ejemplo, cuando un usuario escribe su UserName y su Password formamos una sentencia como esta:
SELECT * FROM Usuarios WHERE UserName='usr1' AND Password='pwd1'
Como vemos, las comillas simples delimitan cadenas en la sentencia.

¿Qué pasaría si el usuario en lugar de escribir [usr1] escribiera ['; DROP--]
La sentencia sería:
SELECT * FROM Usuarios WHERE UserName=''; DROP--' AND Password='pwd1'

Seguramente el usuario verá un error en la aplicación. Pero observemos:
1. El punto y coma provocó finalizar una sentencia
2. Los dos guiones provocan que el texto siguiente sea un simple comentario
3. Drop podría ser utilizado para borrar completamente una tabla.

En fin, no queremos ahondar en este tema, pero sí prevenir este tipo de ataques, así que en el caso de nuestras aplicaciones que corren en Web podemos utilizar dos rutinas. Una de ellas que convierta cualquier texto en código HTML, incluyendo la comilla simple, y sólo así guardar la información en la base de datos. La otra tomará texto de la base de datos, que viene en formato HTML y de ser necesario la decodificará para mostrarla correctamente (en controles textbox principalmente).

Function TextFromBD(ByVal StrText As String, ByVal BlnType As Boolean) As String
'BlnType: (0 - Para mostrar en HTML) (1 - Para mostrar en TextBox)
If BlnType = True Then
StrText = Server.HtmlDecode(StrText)
StrText = Replace(StrText, "&#39", "")
StrText = Replace(StrText, "&#3", "")
StrText = Replace(StrText, "&#lt", "")
StrText = Replace(StrText, "&#l", "")
StrText = Replace(StrText, "&#gt", "")
StrText = Replace(StrText, "&#g", "")
StrText = Replace(StrText, "&#", "")
Else
StrText = Replace(StrText, ControlChars.CrLf, "<br>")
StrText = Replace(StrText, ControlChars.Tab, "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;")
End If
Return StrText
End Function

Function TextToBD(ByVal StrText As String, ByVal IntLeft As String) As String
StrText = Trim(StrText)
StrText = Server.HtmlEncode(StrText)
StrText = Replace(StrText, "'", "&#39;")
StrText = Replace(Replace(StrText, "<", "&lt;"), ">", "&gt;")
If IntLeft > 0 Then StrText = Left(StrText, IntLeft)
Return StrText
End Function

Expresiones regulares

Para validar URLs:
http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?

Para validar correo:
^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$

Para validar cadenas sin caracteres especiales:
^[a-zA-Z0-9ñÑáéíóúÁÉÍÓÚ]+$

Para validar RFC
^[a-zA-Z]{3,4}(\d{6})((\D|\d){3})?$

Función general para utilizar las expresiones:

Ejemplifiquemos con la expresión regular que valida el correo. Esta función regresará False cuando strIn sea un correo no válido.

Function IsValidEmail(ByVal strIn As String) As Boolean
  Return Regex.IsMatch(strIn, ("^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"))
End Function

¿Cómo crear un DataSet que contenga llave primaria?

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>

viernes, julio 28, 2006

Función VB.NET para manejo de errores

Cuando ocurre algún error en nuestro código muchas veces preferimos mostrarle al usuario un mensaje simple como "Hay problemas técnicos, intente más tarde" y recibir justo en ese momento un correo informándonos dónde ocurrió el error y por qué ocurrió.

La función RutinaError se utiliza cada vez que un error es cachado en nuestro código. Recibe 4 parámetros: mensaje de error, ubicación donde ocurrió el error, una cadena cualquiera que nos permite enviar más información y por último el nombre de la rutina donde ocurrió el error.

Como vemos, no es necesario especificar ningún correo origen o destino. Al final explicaremos esto.

A continuación tenemos las rutinas involucradas:


Function RutinaError(ByVal strMsg As String, ByVal strTrack As String, ByVal StrMoreBody As String, ByVal StrNombreRutina As String) As String
Dim MensajeGeneralError As String
MensajeGeneralError = "&nbsp;<image src='Advise.gif'>"
MensajeGeneralError += "<span style='color:red;font-weight:bold;size:11px;font-family:arial;'"
MensajeGeneralError += "<b> Ha ocurrido un error en la comunicación. Puede ser ocasionado por saturación en el sistema o por su conexión de Internet. Por favor intente de nuevo más tarde.</b>"
MensajeGeneralError += "</span><br><br>"

Dim StrDest As String = ""
StrDest += "user@mipagina.com
Dim StrRem As String = ""
StrRem += "errores@mipagina.com"

Dim strServerName As String = Request.ServerVariables("LOCAL_ADDR")
Dim StrRemoteAddr As String = Request.ServerVariables("REMOTE_ADDR")
Dim strBody As String
strBody = "<FONT face='Verdana' size='1'>"
strBody += "<b>Servidor: </b>" & strServerName & "<br><br>"
strBody += "<b>Mensaje: </b>" & strMsg & "<br><br>"
strBody += "<b>Error: </b>" & strTrack & "<br><br>"
strBody += "<b>Rutina: </b>" & StrNombreRutina & "<br><br>"
strBody += "<b>IP Cliente: </b>" & StrRemoteAddr & "<br><br>"
strBody += StrMoreBody
strBody += "</FONT>"
Dim StrSubject As String = "[" & strServerName & "] Problema en " & Request.ServerVariables("URL")

'ENVIO DE CORREO
If InStr(strBody, "aborted") = 0 Then EnvioCorreo(StrDest, StrRem, StrSubject, strBody)

Return MensajeGeneralError
End Function


Sub EnvioCorreo(ByVal strPara As String, ByVal strDe As String, ByVal strTitulo As String, ByVal strCuerpo As String)
Dim objCorreo As New System.Web.Mail.MailMessage
objCorreo.To = strPara
objCorreo.From = strDe
objCorreo.Subject = strTitulo
objCorreo.Body = strCuerpo
objCorreo.BodyFormat = MailFormat.Html
System.Web.Mail.SmtpMail.Send(objCorreo)
End Sub


Veamos un pequeño ejemplo de cómo se implementaría:


Try
    {... aquí ocurrirá algún error ...}
Catch ex As Exception
    Dim StrMensaje As String = ""
    StrMensaje = RutinaError(ex.Message, ex.StackTrace, "Más texto", "RutinaX()")
    Response.Write(StrMensaje)
End Try


Los correos han sido especificados dentro de RutinaError y no dentro del bloque Catch. De este modo cuando el correo errores@mipagina.com cambie a errores@mipaginanueva.com entonces sólo será necesario un pequeño cambio y todos los errores que se generen en nuestro sitio irán a parar al nuevo correo.

Si tienen alguna duda de cómo implementarlo no duden en contactarnos.

Cálculos y manejo de cadenas

Function CambiaMes(intMes As Integer) As String
  If intMes = 1 Then CambiaMes = "Ene"
  If intMes = 2 Then CambiaMes = "Feb"
  If intMes = 3 Then CambiaMes = "Mar"
  If intMes = 4 Then CambiaMes = "Abr"
  If intMes = 5 Then CambiaMes = "May"
  If intMes = 6 Then CambiaMes = "Jun"
  If intMes = 7 Then CambiaMes = "Jul"
  If intMes = 8 Then CambiaMes = "Ago"
  If intMes = 9 Then CambiaMes = "Sep"
  If intMes =10 Then CambiaMes = "Oct"
  If intMes =11 Then CambiaMes = "Nov"
  If intMes =12 Then CambiaMes = "Dic"
End Function


La siguiente función calcula la edad dependiendo de la fecha proporcionada.
Function CalculaEdad(ByVal dtFechaNac As Date) As Integer
  Dim Edad As Integer = DateTime.Today.Year - dtFechaNac.Year
  If DateTime.Today.Month > dtFechaNac.Month Then Edad -= 1
  If dtFechaNac.Month = DateTime.Today.Month Then
    If DateTime.Today.Day > dtFechaNac.Day Then Edad -= 1
  End If
  Return Edad
End Function


Function DateToDisplay(ByVal dtFecha As Date, ByVal StrFormato As String) As String
Dim StrFechaDia, StrFechaMes, StrFechaAno As String

StrFechaDia = dtFecha.Day
StrFechaMes = dtFecha.Month
If dtFecha.Day > 0 And dtFecha.Day < 10 Then StrFechaDia = "0" & dtFecha.Day
If dtFecha.Month > 0 And dtFecha.Month < 10 Then StrFechaMes = "0" & dtFecha.Month
StrFechaAno = dtFecha.Year

If StrFormato = "0" Then
Return StrFechaDia & "/" & StrFechaMes & "/" & StrFechaAno
ElseIf StrFormato = "A" Then
Return StrFechaDia & "-" & CambiaMes(dtFecha.Month) & "-" & StrFechaAno
End If
End Function

Funciones VB.NET para manejo de base de datos

Resumen de Rutinas:
1. GetDataSetBD(String SQL)
2. EjecutaComando(String SQL)





Function GetDataSetBD(ByVal StrSQL As String) As DataSet
Dim dbConnection As New SqlConnection(ConfigurationManager.AppSettings("MyDataBaseKey"))
Dim objAdapter As New SqlDataAdapter(StrSQL, dbConnection)
Dim ds As New DataSet()
Try
objAdapter.Fill(ds)
Catch ex As Exception
Throw New Exception(ex.Message, ex.InnerException)
Finally
objAdapter.Dispose()
If dbConnection.State = ConnectionState.Open Then dbConnection.Close()
dbConnection.Dispose()
End Try
Return ds
End Function


Overloads Function EjecutaComando(ByVal strsql As String, ByVal sqlparametros As SqlParameter()) As Boolean
Dim bolaccion As Boolean
Dim objcn As New SqlConnection(ConfigurationManager.AppSettings("MyDataBaseKey"))
objcn.Open()

Dim objcmd As SqlCommand = objcn.CreateCommand()
Dim objTran As SqlTransaction
objTran = objcn.BeginTransaction()
objcmd.Connection = objcn
objcmd.Transaction = objTran
Try
With objcmd
.CommandText = strsql
For Each objpar As SqlParameter In sqlparametros
.Parameters.Add(objpar)
Next
.ExecuteNonQuery()
End With
objTran.Commit()
bolaccion = True
Catch ex As SqlException
objTran.Rollback()
If objcn.State = ConnectionState.Open Then objcn.Close()
objTran.Dispose()
objcmd.Dispose()
objcn.Dispose()

Throw New Exception(ex.Message, ex.InnerException)
Finally
If objcn.State = ConnectionState.Open Then objcn.Close()
objTran.Dispose()
objcmd.Dispose()
objcn.Dispose()
End Try
Return bolaccion
End Function

Overloads Function EjecutaComando(ByVal strsql As String, ByVal sqlparametros As SqlParameter(), ByVal tipo As Integer) As Integer
Dim intID As Integer
Dim objcn As New SqlConnection(ConfigurationManager.AppSettings("MyDataBaseKey"))
objcn.Open()

Dim objcmd As SqlCommand = objcn.CreateCommand()
Dim objTran As SqlTransaction
objTran = objcn.BeginTransaction()
objcmd.Connection = objcn
objcmd.Transaction = objTran
Try
With objcmd
.CommandText = strsql
For Each objpar As SqlParameter In sqlparametros
.Parameters.Add(objpar)
Next
intID = .ExecuteScalar
End With
objTran.Commit()

Catch ex As SqlException
objTran.Rollback()
If objcn.State = ConnectionState.Open Then objcn.Close()
objTran.Dispose()
objcmd.Dispose()
objcn.Dispose()

Throw New Exception(ex.Message, ex.InnerException)
Finally
If objcn.State = ConnectionState.Open Then objcn.Close()
objTran.Dispose()
objcmd.Dispose()
objcn.Dispose()
End Try
Return intID
End Function

Sub RunSQLBD(ByVal StrSQL As String)
Dim dbConnection As New SqlConnection(ConfigurationManager.AppSettings("MyDataBaseKey"))
Dim dbCommand As New SqlCommand(StrSQL, dbConnection)
Try
dbConnection.Open()
dbCommand.ExecuteNonQuery()
Finally
dbConnection.Close()
End Try
End Sub
Function RunSQLBD_ID(ByVal StrSQL As String) As String
Dim dbConnection As New SqlConnection(ConfigurationManager.AppSettings("MyDataBaseKey"))
Dim dbCommand As New SqlCommand(StrSQL, dbConnection)
Dim StrResult As String
Try
dbConnection.Open()
StrResult = dbCommand.ExecuteScalar()
Finally
dbConnection.Close()
End Try
Return StrResult
End Function

jueves, julio 27, 2006

Confirm en ASP.NET

Bueno despues de haber revisado infinidad de ideas para poder crear un boton de confirmacion a la ora ya sea de insertar un registro, editar o borrar el registro, he aqui la que me parecio mas practica.

Primero el script de confirmacion:


< script type="text/javascript">
function Notifica(elem,Notif)
{
//elem es el elemento a editar
//Notif es la accion que se haria, este ejemplo realiza 2 acciones diferentes
if (Notif==1)
{
return confirm( 'Se marcara como X: '+ elem+ ', ¿esta usted de acuerdo?');
}
else
{
return confirm( 'Se marcara y: '+ elem+ ', ¿esta usted de acuerdo?');
}
}
<script>


Yo estoy utilizando la version 2 de .net y empleando la nueva propiedad de onclientClick.
Nota: Aqui lo estoy haciendo en un gridview y por eso estoy llamando a mi objeto con sender

sender.onclientClick = "return Notifica('" & sender.CommandName & "','" & sender.CommandArgument & "');"

para los que usan la version 1.1:

sender.Attributes.Add("onclick","return Notifica('" & sender.CommandName
& "','" & sender.CommandArgument & "');")



Para finalizar debo de hacer notar que debe de llebar siempre el return funcion()
ya que a la hora de aceptar realizaria el postback normal de nuestro boton, o si se rechaza no se ejecutara el postback.





Yo lo he estado utilizando y me funciona, y espero que a ustedes tambien.

Cualquier duda o comentario no duden.

lunes, julio 24, 2006

Repositorio

Hace tiempo que vengo utilizando la siguiente url: http://www.codekeep.net/ para poder almacenar rutinas reutilizables

Ya tengo algunas aunque no son muchas, pero han sido vistas bastantes ocaciones creo yo.

¿Que mas hay?

No solo es para .NET ahi encontraran bastantes rutinas para SQL, jscript, asp, php, perl, xml, etc..

La verdad que si quieren tener en un solo lugar las rutinas que mas utilizan y para que no esten abriendo otros proyectos en las que utilizaron las rutinas, realmente si es de gran ayuda este sitio ya que centralizan su información.

Lo unico es que pide registrarse, y esto es gratuito.

Ojala y les sea de ayuda.

Objetivo del Blog

Vamos a tratar de crear una buena fuente de información, para desarrollo de .net, y alguno que otro articulo de tecnología relacionada, como SQL.

Tratare de centrarme en ASP.NET
Controles de usuario ASCX
Y alguna que otra utileria que espero les sirva.