Blog de tecnologia y desarrollo.

Unable to find the requested .Net Framework Data Provider

February 14th, 2008

Precisamente hablando del MySql Connector para .Net, resulta que cuando quiere uno instalar la aplicacion en un servidor, puede haber un error como el que sigue:

 System.ArgumentException: Unable to find the requested .Net Framework Data Provider. It may not be installed.

Despues de romperme un poco la cabeza, descubrí el origen del problema: El MySQL provider no esta agregado a los DBProviderFactories. Normalmente esto lo hace el instalador del MySql connector, y lo hizo en mi equipo de desarrollo, pero no en el de producción.

La solución mas inmediata es, pues instalarlo en el servidor. Sin embargo, si no quieres o no estas en la posibilidad de meterle mano al servidor, puedes agregar lo siguiente a tu web.config:

<system.data>
<DbProviderFactories>
<add name=”MySQL Data Provider” invariant=”MySql.Data.MySqlClient” description=”.Net Framework Data Provider for MySQL” type=”MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=5.1.4.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d” />
</DbProviderFactories>
</system.data>

Cuidando unicamente de que la version sea la que corresponda a la de nuestro connector.

También, no esta de mas checar que la dll de MySql.Data vaya en la carpeta bin ;-)

Conectar el .Net framework con MySql (con total soporte para ADO)

February 14th, 2008

Recientemente me salio la necesidad de manipular algunos datos a una base de MySql en lugar del ya tradicional SQL Server. Mi primera opcion fue hacer mi desarrollo en PHP. Pero luego se me ocurrio buscar una forma limpia de conectar el ASP.Net con MySQL. Afortunadamente, si la hay. El MySql Connector/Net

La mas nueva version (5.1), ya esta perfectamente integrada con el Visual Studio 2005. Y manipular las bases de datos exactamente de la misma manera que ya estamos acostumbrados. Les recomiendo que lo prueben!

Un bonito y util ejercicio

December 12th, 2007

En días pasados, y por la época, me salio la necesidad de organizar un intercambio (si, esos en que se reúnen un grupo de amigos y/o conocidos y se intercambian regalos entre ellos). Como tenia la necesidad de que fuera secreto, decidí que la mejor solución (y la mas Geek), sería crear un programita que hiciera el sorteo por mi, y enviara correo a los interesados sin que yo me enterara de quien le dará a quien. El programita me represento algunos retos pequeños pero divertidos. Quiza lo mas divertido de aprender fue como funciona la clase Random y como podemos mandar Mails usando el SMTP de GMail.

No suena a demasiado dificil hacer un sorteo de intercambio programaticamente. Y en realidad, lo es. Solo que el generador de numeros aleatorios del .Net Framework no es realmente aleatorio. Veran.

Para usarlo empleamos una sentencia mas o menos asi:
Random rnd = new Random(int base)
Donde base es el limite superior para los aleatorios. Para generar un numero aleatorio de una serie (ojo, dije serie) usamos
rnd.Next();
El asunto esta en que, si siempre le damos la misma base, siempre obtendremos los mismos resultados. No podemos crear un objeto Random cada que lo queramos usar, tenemos que usar uno solo y dejar que solito “randomize” la serie. Despues de algunas aproximaciones, lo que termine por hacer es darle una base de los milisegundos de la hora.
Random rnd = new Random(DateTime.Now.Millisecond)
Y como necesitaba que los numeros aleatorios estuvieran dentro de un rango (el tamaño de la lista de participantes), simplemente use el modulo:
rnd.Next() % elQueDa.Count //elQueDa es un ArrayList con mi lista de participantes.
Una vez solucionado ese asunto, tenia que hacer que enviara los mails a los participantes. Para ello, use la bendita clase SmtpClient del framework. En realidad es muy facil de usar. Mi codigo se explica solo:
System.Net.NetworkCredential credential = new System.Net.NetworkCredential
(Properties.Settings.Default.SmtpUserName,Password);
SmtpClient senderClient = new SmtpClient(Properties.Settings.
Default.SmtpAddress,Convert.ToInt32(Properties.Settings.Default.SmtpPort));
senderClient.EnableSsl = Properties.Settings.Default.SmtpEnableSsl;
senderClient.UseDefaultCredentials = false;
senderClient.Credentials = credential;

MailMessage Mail = new MailMessage();
Mail.From = new MailAddress(Properties.Settings.Default.MailFromAddress);
Mail.Subject = Properties.Settings.Default.MailSubject;
senderClient.Send(Mail)
Unicamente hay que preocuparse de cachar las excepciones adecuadas. Por si les paso de noche, Properties.Settings.Default es la forma de acceder a las propiedades guardadas en el archivo de configuracion, asi que no busquen en la documentacion propiedades como SmtpEnableSsl por que yo las di de alta asi en los Settings.

Hay que recordar que GMail trabaja con SSL. Para ello la linea de
senderClient.EnableSsl = Properties.Settings.Default.SmtpEnableSsl;
Y tambien que hay que configurar el puerto (465 o 587, si uno da TimeOut, intentas con el otro).

Y ya, la verdad es que es muy facil hacer este tipo de cosas con el .Net Framework. Si le quieren echar un ojo, aqui encuentran el codigo. Y si les interesa descargarlo para su uso, pueden entrar a esta pagina (hacemos uso de las ventajas del Click Once Deployent). No me esforce demasiado en las validaciones, asi que asegurense de meter bien los datos.

Seleccionar el texto de un Textbox en ASP.Net

November 15th, 2007

Ahora que estoy entendiendo el paradigma de ASP.Net (cuando lo conoci me confundi mucho pues no era como trabajar con ASP tradicional pero tampoco como trabajar con windows forms) le estoy tomando mucho cariño.

Un Textbox en ASP.Net no tiene una propiedad o metodo definido para seleccionar el texto dentro de el. Pero no hay problema, por que podemos usar el querido y confiable Javascript para ello.

Una forma sencilla de lograr esto es mediante el uso del metodo Attributes.Add() de nuestra caja de texto. Basta poner el siguiente codigo en el Load del codebehind de nuestra forma:

TxtBusqueda.Attributes.Add("onfocus", "SetSelected();");

Con esto, a la hora de “renderear” nuestro control, en la declaracion veremos algo como esto:

<input name="TxtBusqueda" type="text" ... onfocus="SetSelected();" />

Como vemos, agregamos ese atributo a nuestra caja de texto. Ahora, solamente hay que escribir en el HTML de nuestra forma el codigo adecuado:

<script type="text/javascript">
function SetSelected()
{
document.form1.TxtBusqueda.select();
}
</script>

Como vemos, esto puede ser muy util pues podemos controlar finamente el comportamiento de cualquier control, pudiendo agregar, por ejemplo, comportamiento para otros eventos, asi como atributos que no forman parte de nuestros controles ASP.Net ∞

SOAPopera: Publicando un web service I

September 25th, 2007

Una de los “ultimos” gritos de la moda en cuanto al desarrollo, son los web services. En lo particular, me parecen una tecnologia excelente, por que permiten “consumir” servicios ya construidos sin preocuparte de la plataforma en la que esten construidos. Para muchas aplicaciones, una arquitectura basada en servicios Web puede ser el santo grial… si lo sabes utilizar correctamente.

Hace poco en mi trabajo (empresa X), surgio la necesidad de que la empresa Y trabajara con algunos de nuestros datos para una validacion en una aplicacion propia. Por politicas de X, esta prohibido liberar a externos cualquier base de datos. De manera que necesitabamos que la aplicacion hecha por Y consultara los datos de X sin tener acceso real a los datos… parecia un caso perfecto para los WebServices.

Como buen .Netero, se que es muy facil construir un WebService en VS. De hecho, tengo algunos trabajando en la red interna, sin problemas (notes que dije red interna). Esa es mi experiencia con ellos, asi que sabia que construir un simple WebService que consultara una base de datos y regresara una cadena, no seria problema alguno.

Y no lo fue. Las pruebas estuvieron bien, el servicio parecia que hacia lo que tenia que hacer. Publicamos, y todo bien, de nuevo probe y todo funcionaba bien. Incluso hice un pequeño ejecutable que lo consumia, y de nuevo, todo sin problemas.

Pero a la hora de pedirles a los usuarios que lo probaran, simplemente no se podian conectar. Descartamos tullidez de su parte por que en esta pagina (muy buena para las pruebas, por cierto) tampoco estaba funcionando, asi que el problema caia en mi cancha.

Me pase largas y tediosas horas tratando de decifrar la solucion, pero, al menos ese dia, no lo logre. Despues de mas de 10 horas de cambiar el codigo sin exito comienzas a alucinar, asi que decidi que seria mejor continuar al dia siguiente con la cabeza mas fresca.

Al dia siguiente, inmediatamente note algo extraño (descansar ayuda). La pagina de pruebas de SOAP leia correctamente el WSDL, pero luego, buscaba la funcion en una direccion mas o menos asi:

http://192.168.1.100/servicio/funcion.asmx

En lugar de buscarla en:

 http://empresaX.com/servicio/funcion.asmx

No habia notado nada raro hasta que cai en la cuenta de que la direccion 192.168.x.x es una IP local, no publica. Al leer el WSDL, en efecto, encontre la causa del problema

<wsdl:port name=ServicioSoap binding=tns:ServicioSoap>

<soap:address location=https://192.168.1.100/servicio/funcion.asmx />

</wsdl:port>

El problema radicaba en que el WSDL (que es automaticamente generado), estaba tomando la IP de la maquina en la que residia (lo cual estaria bien si estuviera directo a internet, pero al estar detras de un Router, la cosa se ponia diferente). De manera que en realidad el WSDL hacia que la aplicacion preguntara por una servicio en una direccion que jamas encontraria.

Dar con el problema fue casi tan dificil como dar con la solucion. En mis busquedas practicamente todos compartian el problema, pero ningun la solucion. Encontre muchas soluciones que no funcionaban o no me convencian. Hasta que llege al benito blog de Kirk Allen Evans, donde habia una solucion para un problema similar.

El chiste aqui es  crear una clase derivada de SoapExtensionReflector que nos permitira hacer cambios en el WSDL en tiempo de ejecucion. Debe verse algo asi:

namespace EmpresaX.ServicioWeb
{
    public class LocalAddressReflector : SoapExtensionReflector
    {
        public override void ReflectMethod()
        {
            
        }

        public override void ReflectDescription()
        {
            ServiceDescription description = ReflectionContext.ServiceDescription;
            foreach (Service service in description.Services)
            {
                foreach (Port port in service.Ports)
                {
                    foreach (ServiceDescriptionFormatExtension extension in port.Extensions)
                    {
                        SoapAddressBinding binding = extension as SoapAddressBinding;
                        if (null != binding)
                        {
                            binding.Location = binding.Location.Replace
				(Properties.Settings.Default.LocalAddress, Properties.Settings.Default.PublicAddress);
                        }
                    }
                }
            }
        }
    }
}

Properties.Settings.Default.LocalAddress y PublicAddress no son mas que valores en el archivo de configuracion que corresponden a las direcciones local y publica del servicio. Lo deje en el archivo de configuracion para no tener que recompilar la aplicacion si estas direcciones cambiaban.

Despues, en el archivo de configuracion, En la seccion de Configuration/System.Web hay que agregar lo siguiente (para asegurarnos que nuestra clase sea cargada).

<webServices>

<soapExtensionReflectorTypes>

<add type=EmpresaX.ServicioWeb.LocalAddressReflector, WebService/>

</soapExtensionReflectorTypes>

</webServices>

Cabe destacar que el nombre “localAddresReflector” lo puedes reemplazar por el que tu hayas elegido para tu clase, y que “WebService” no es mas que el nombre del folder (en este caso, raiz) donde se encuentra mi clase (sin eso, no funciona).

Y eso hizo el truco,  cuando lo ejecute, el WSDL ya estaba correcto:

<wsdl:port name=ServicioSoap binding=tns:ServicioSoap>

<soap:address location=https://empresaX/servicio/funcion.asmx />

</wsdl:port>

Cabe mencionar que esto puede ser util no solo para mi caso, sino para cambiar, por ejemplo, “http” por “https” (el caso de Kirk), o bien, para especificar un puerto diferente para el web service (agregando “:1234″ al final de la direccion).

Y ahi tuve la solucion, despues de apenas 15 horas de trabajar en ella. Espero que si llegas a este post en un caso desesperado, te resulte de utilidad.

Por que el boton de borrar no borra (GridView)

August 27th, 2007

Apenas le estoy agarrando la onda al control DataGridView de .Net Framweork y ahora me estoy peleando con el control GridView de ASP.Net 2.0. Por mucho, me gusta mas la forma en que se maneja esto en windows forms, pero nada es perfecto.

En fin, el punto es que yo ponia bien bonitos mis controles para borrar un registro desde el GridView pero no pasaba nada, el borrado no se hacia. Al final, descubri que hay que especificarle al GridView cual es la llave de nuestra tabla, por medio de la la propiedad DataKeyNames. Hecho esto, el borrado funciona tan bonito como deberia.

Subir archivos via ASP.net facil y rapido

August 24th, 2007

Anteriormente, si se deseaba hacer una  pagina web que tuviera la funcionalidad de subir archivos de la maquina cliente al servidor, usando cosas como el ASP 3, habia que hacer un pequeño circo. Lo mas sensato era buscar alguna DLL de terceros que hiciera el trabajo por ti (como ASP Smart Upload). Y aun si, habia que configurar varias cosas.

Ahora que tuve la necesidad de hacerlo de nuevo, decidi hacer uso de ASP.Net 2.0. Y me encontre con que la tarea se ha facilitado enormemente gracias al contrlo FileUpload.

Basta con poner el control en la pagina que desees, hacer un poco de validaciones y voila! tienes un archivo directo a tu servidor. Unicamente hay que hacer las validaciones necesarias. Pero me parecio increible que con unos cuantos clics pudieras tener esta funcionalidad que antes costara tanto trabajo.

En esta pagina esta la documentacion necesaria para el uso de la clase. Como veran , bastante sencillita.

Un gusano para prevenir ataques de Wordpress

August 2nd, 2007

Leyendo esta entrada en Kriptopolis me entere de algunas nuevas vulnerabilidades descubiertas para la nueva versión de Wordpress, que en efecto, están reportadas en el blog de MyBeni.

Curiosa y afortunadamente, el mismo descubridor de las vulnerabilidades creo un gusano que, mediante XSS, te ayuda a cerrar las mencionadas vulnerabilidades. Es completamente seguro y facil de usar, y también un poco escalofriante, pues te enteras de lo que se puede hacer a través de un ataque de este tipo. Lo recomiendo ampliamente si eres administrador de un blog de wordpress y usas la version 2.2.1

Obtener solo la fecha en una consulta de SQL

July 23rd, 2007

En SQL server, no hay una manera directa de obtener solo la parte de fecha de un campo “DateTime” y no existe unicamente un tipo de campo “Date”. Por lo tanto, si queremos obtener en una consulta, por ejemplo, los registros del dia de hoy, la siguiente consulta no funcionaria:

 select * from tabla where fecha = GetDate()

Esto se debe a que GetDate() nos regresa la fecha y hora en que se ejecuta la consulta, no solo la fecha.

Para hacer posible esta comparacion, podriamos hacer que todas los DateTime se refirieran a las 0:00:00 del dia en cuestion, convirtiendo la fecha a Float y luego usando la funcion Floor de la siguiente manera:

 Cast(Floor(Cast(fecha as Float))as DateTime)

De esta manera, primero convertimos la fecha, y con floor le quitamos todos los minutos y horas “sobrantes”. Despues convertimos de nuevo a DateTime, lo que nos regresara la fecha, con la hora 0. Para poder hacer la comparacion habria que convertir de la misma manera la funcion GetDate().

Como borrar registros duplicados de SQL (the ultimate solution)

July 17th, 2007

El dia de hoy en mi trabajo se me presento un problema con registros duplicados de una tabla de SQL server, asi que me di a la tarea que aqueja a todo DBA alguna vez en su vida. Como borrar registros duplicados de una tabla?  (yo no soy DBA, por cierto, pero aqui hay que hacerla de todo).

En principio de cuentas, una registro duplicado nos habla de un mal diseño, o de base de datos o de procedimientos. En este caso el problema es un error de procedimiento, pero por ahora no hablaremos de eso.

Mi caso esta asi: Tengo una tabla con ID numerico, y un numero x de campos. De esos campos, los registros duplicados son iguales a excepcion de 1 campo (aparte del ID, por supuesto). La tabla tiene algo mas de 4 millones de registros, y habia unos 400,000 duplicados (algunos mas de 6 veces).

Googleando un poco encontre varias soluciones que van desde hacer un Select distinct a una tabla temporal, vaciar la tabla original y luego copiar lo de la temporal a la original (lo cual resulta muy lento), hasta un script que hacia uso de cursores, y que nunca pude hechar a andar.

Todo fue hasta que por ahi me encontre con una solucion simple, rapida y efectiva, que solo requiere que tu tabla contenga un ID numerico. Si no lo tiene, pues lo puedes agregar facilmente, y si despues no lo requieres, puedes borrarlo.

De lo que se trata es de seleccionar la ID mas baja (o mas alta) de cada conjunto de duplicados (tenemos que definir que es un duplicado), y borrar las demas. Simple, no?

Para ejemplificarlo vamos a suponer que tenemos nuestra tabla “Tabla1″.  Esta tabla tiene los campos “ID”, “Campo1″, Campo2″ y “Campo3″. Lo primero que tenemos que hacer es identificar que campos tienen que ser iguales para que un registro sea repetido. Puede ser que sean todos los campos o solo un conjunto de estos. En este ejemplo vamos a decir que Campo2 y 3 son necesarios. Una vez identificados lo que tenemos que hacer es ejecutar la siguiente consulta.

 delete from Tabla1 where Id >
(
Select min(Id) from Tabla1 Tbl1 where Tabla1.Campo2 = Tbl1.Campo2 and Tabla1.Campo3 = Tbl1.Campo3
)

Es todo! Simple, no? Funciona igual si cambias el signo por “<” y  usas “max” en lugar de “min”. En este caso en lugar de conservar el Id mas baja, conservas la mas alta.

Simple y rapido ademas. Mi consulta demoro poco mas de un minuto en buscar y borrar los duplicados en mi tabla de 4 millones. Espero que esta solucion los salve de mas de una. ∞