27 dic 2010

Failed to access IIS metabase.

Estaba ejecutando una aplicación aspnet 2.0 en un windows XP (sí aun habemos personas que ejecutamos XP) y se me presentó el error: Failed to access IIS metabase.

Lo que ocurrió es que se había instalado primero el Visual Studio 2005 que el IIS, por lo que sucede es que el aspnet no esta bien registrado en el IIS. Para asegurarme de que quede bien instalado ejecuté las siguientes sentencias en el command.


aspnet_regiis -u
aspnet_regiis -i

una vez realizado ya pude ejecutar el sitio en cuestión.

8 oct 2010

Buscando el error pero en ingles

Tienes un error y necesitas buscar (googlear) la informacion al respecto para ver si puedes corregirlo. Pero oh! el error esta en español y por más que buscas no encuentras información útil al respecto, puedes ponerte a traducir el error al inglés con la esperanza de que quede lo más parecido a como realmente se muestra ese mismo error en "idioma original" o puedes buscar aquí:

http://finderr.net/search

y estar seguro de que ese error en español es de esta forma en inglés. De esas páginas que se necesitan muy de vez en cuando, pero cuando se necesitan realmente se necesitan.

13 jul 2010

ERROR: Formularios Web no se muestran en el Diseño

Me acaba de suceder un comportamiento frustrante del Visual Studio 2005, por alguna extraña razón que aun no acierto a comprender, me dejó de mostrar el diseño de las paginas web. Me explico: tengo una pagina web con un formulario, el Source(html) se muestra bien pero si le doy clic en Desing se pega un rato, termina no mostrando nada y queda inestable en general el Visual Studio (combinación de teclas que no funcionan, etc...)
Después de desesperados intentos de resucitación sin resultado, di con la solución:

- Cerramos el VS2005.
- Abrimos el Command Prompt de VS 2005.
- Digitamos lo siguiente:
devenv /resetsettings
devenv /setup

y Gloria! Aleluya! ya volvió a servir la vista de diseño. Que raros estos achaques que le dan a Visual Studio algunos son realmente frustrantes....

17 jun 2010

Update con valores de otra tabla

Algunas cosillas se nos va cómo es que hay que hacerlas, a veces son un tanto lógicas y nos parece que son sencillas pero por una u otra no le llegamos.

Eso me pasó recientemente cuando necesitaba actualizar la tabla1 con valores de la tabla2 (en realidad debía pasar los datos de una a otra y borrar la tabla2).

Para ser este tipo de Update una sintaxis que funciona en SQL server 2005 es la siguiente
UPDATE T1
SET T1.campo1 = T2.campo1,
T1.cammpo2 = T2.campo2
FROM Tabla1 T1 INNER JOIN Tabla2 T2
ON T2.campoLlave = T1.campoLlave

En la tabla 2 lo lógico es que debe haber un solo registro devuelto por la sentencia Join con respecto a la Tabla1.

26 abr 2010

__doPostBack

ASP.Net 2.0. Me imagino que se habrán dado cuenta de que en las páginas de asp.net se implementa un curioso metodo llamado __doPostBack, de hecho nosotros lo podemos utilizar dentro de las funciones del lado del cliente de los controles de .Net como por ejemplo en el OnClientClick de un botón. Pero ¿que es lo que hace? y ¿cómo lo hace?

Básicamente realiza un submit de la página, ó sea, dispara el postback, como claramente indica su nombre. El método recibe dos parámetros: __EVENTTARGET y __EVENTARGUMENT El primero de los cuales recibe el nombre del control que dispara el postback y el segundo el método de dicho control.

Ahora, ¿cómo hace esta función para hacer el postback? El código que se genera automáticamente es mas o menos este:
< input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" / >
< input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" / >

function __doPostBack(eventTarget, eventArgument) 
{
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) 
    {
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}
Como se puede ver, son simplemente dos textbox ocultos y una función que los utiliza para el disparar el submit. Como lo dije antes, este código es generado automáticamente siempre y cuando haya al menos un control en la página que tenga al atributo “AutoPostBack” en True. Si lo queremos es usar la funcion sin que haya controles con el AutoPostBack en True, pues manualmente ponemos el código anterior en la página y listo.

Ahora bien, nosotros podemos hacen uso del método con los parámetros vacíos __doPostBack("",""); Esto dispara el submit y se hubiera algún método del lado del servidor que no se hayan ejecutado (por tener el AutoPostBack en False, por ejemplo) entonces ahora también se dispararán. Este método se usa en los treeview que no disponen de método de postback, cuando se utilizan con checkboxes, para que cuando hagan click en alguno de ellos se ejecute el método postback correspondiente.

Pero hay algo más. La realización de un simple Submit con controles ocultos significa que podemos capturar los valores de los parámetros(valores del los controles ocultos) del lado del servidor y por ende, ejecutar lo que nos parezca más conveniente en determinadas circunstancias. Así por ejemplo podemos hacer una función javascript como esta:
function MiPostBack ()
 {
     var o = window.event.srcElement;
     if ( o.tagName == "INPUT" && o.type == "checkbox")
     {                   
          __doPostBack("MiControl","MiEvento");
      } 
} 
La cual la podemos usar en el OnClick de un treeview para indicar de que si hace clic sobre un checkbox del treeview haga submit con estos parámetros. Y en el método Page_Load del lado del servidor tener algo similar a esto
string controlName = Request.Params.Get("__EVENTTARGET");
string controlEvent = Request.Params.Get("__EVENTARGUMENT");
if ((controlName=="MiControl")&&(controlEvent=="MiEvento"))
{
   UnMetodoCualquiera();
}
Así podemos ejecutar UnMetodoCualquiera(); cuando se produce un clic en alguno de los checkboxes del treeview (esto se realizó asi en un proyecto real para evitar que cuando se marcaba un checkbox padre se dispara el postback por cada hijo que también se marcaba automáticamente). Para mi esto abre más de una posibilidad para usar el __doPostBack cuando requiere ejecutar código del servidor.

16 abr 2010

Tablas temporales y cursores

Mi amigo Pigo Sama realizó un interesante articulo sobre tablas temporales y cursores.

Es interesante como señala la satanizacion de algunas de las características disponibles en motores de base de datos cuando, como bien lo indica Pigo, es cuestión de moderación y orientación en su uso.

4 mar 2010

Usar FOREACH ó SELECT

Hay veces en las que no nos detenemos a pensar, cómo estamos haciendo alguna tarea, simplemente no nos cuestionamos, tal vez porque estamos ajustados con los tiempos de entrega o peor aún, porque siempre lo hemos realizado así y no nos interesa preguntarnos si esaes la mejor forma. Debemos mantener la sana costumbre de preguntarnos cómo funcionan las cosas y si lo que hacemos es la mejor manera de alcanzar el objetivo.

Pues luego de esta introducción la pregunta: Tengo un DataTable y necesito encontrar una o varias filas según un criterio dado ¿Qué es mejor: recorrerlo con un foreach o hacer un select que me devuelva un arreglo de DataRows?.
Antes de contestar nada mejor lo probamos y salimos de las dudas. La prueba debe constar de lo siguiente:

- Crear el DataTable y Llenarlo con 2 millones de registros.
- Ejecutar un SELECT del DataTable, midiendo el tiempo que tarda.
- Ejecutar un FOREACH con el mismo criterio. Midiendo el tiempo que tarda

Paso 1 Creando el datatable y llenándolo de datos


/* Creamos el datatable con dos columnas*/
DataTable dt = new DataTable();
DataColumn dc1 = new DataColumn("Col1", System.Type.GetType("System.Int32"));
DataColumn dc2 = new DataColumn("Col2", System.Type.GetType("System.String"));
dt.Columns.Add(dc1);
dt.Columns.Add(dc2);

/* Llenamos el datable con dos millones de registros*/
DataRow dr;
DateTime paso1 = DateTime.Now;
for (double i = 1; i < 2000001d; i++)
{
dr = dt.NewRow();
dr["Col1"] = i;
dr["Col2"] = "Datos de Prueba";
dt.Rows.Add(dr);
}
DateTime paso2 = DateTime.Now;
TimeSpan span = paso2.Subtract(paso1);
Console.WriteLine("total de Registros: " + dt.Rows.Count.ToString("N2"));
Console.WriteLine("Tiempo que dura la carga en milisegundos: " + span.TotalMilliseconds);


Paso 2 Setear la variable que vamos a usar la búsqueda y ejecutar el SELECT


/*Columna a encontrar*/
int var = 1234567;

/*Ejecutando el SELECT*/
paso1 = DateTime.Now;
DataRow[] arreglo = dt.Select("Col1 = " + var);
paso2 = DateTime.Now;
span = paso2.Subtract(paso1);
Console.WriteLine("Fin SELECT Total encontrados: " + arreglo.Length.ToString("N2"));
Console.WriteLine("Tiempo que duró el SELECT en Milisegundos: " + span.TotalMilliseconds);


Paso 3 Ejecutar el FOREACH


/* Ejecutando el FOREACH*/
ArrayList arraylist = new ArrayList();
paso1 = DateTime.Now;
foreach (DataRow dtr in dt.Rows)
{
if (Convert.ToDouble(dtr["Col1"]) == var)
{
arraylist.Add(dtr["Col1"]);
}
}
paso2 = DateTime.Now;
span = paso2.Subtract(paso1);
Console.WriteLine("Fin FOREACH Total encontrados: " + arraylist.Count.ToString("N2"));
Console.WriteLine("Tiempo que duró el FOREACH en milisegundos: " + span.TotalMilliseconds);


Esta es una prueba sencilla y hecha de forma apresurada, pero válida dentro de lo que necesitamos probar. El código está en una consola y está hecho en VS 2005 y en una maquina virtual con Windows 2003. El resultado de las pruebas fue el siguiente:


total de Registros: 2.000.000,00
Tiempo que dura la carga en milisegundos: 7000,0656
Fin SELECT Total encontrados: 1,00
Tiempo que duró el SELECT en milisegundos: 5638,1072
Fin FOREACH Total encontrados: 1,00
Tiempo que duró el FOREACH en milisegundos: 470,6768


Por lo que podemos ver la prueba arroja que la carga de dos millones de registros en la DataTable duro 7 segundos, ejecutar el SELECT 5 segundos y medio y el FOREACH menos de medio segundo. Se ejecutaron varias corridas y los resultados fueron muy similares. Triunfador sin discusión: el FOREACH.

Ahora, que pasa si al DataTable le ponemos una llave. Al poner una llave debemos tener en cuenta que el engine ADO.NET, debe indexar la tabla lo que se puede llevar a cabo durante la carga de información, cada vez que se añade una fila (si creamos la llave antes de carga la información) o al crear la llave si la creamos luego de la carga de datos. Para propósitos de la prueba lo coloqué después de la carga y antes de la variable de búsqueda para determinar claramente la penalización de tiempo por la indexación.


/*Creando LLAVE*/
paso1 = DateTime.Now;
DataColumn[] pk = new DataColumn[1];
pk[0] = dc1;
dt.PrimaryKey = pk;
paso2 = DateTime.Now;
span = paso2.Subtract(paso1);
Console.WriteLine("Creacion de llave en milisegundos: " + span.TotalMilliseconds);


Esta prueba arrojó el siguiente resultado


total de Registros: 2.000.000,00
Tiempo que dura la carga en milisegundos: 6789,7632
Creacion de llave en milisegundos: 5868,4384
Fin SELECT Total encontrados: 1,00
Tiempo que duró el SELECT en milisegundos: 10,0144
Fin FOREACH Total encontrados: 1,00
Tiempo que duró el FOREACH en milisegundos: 450,648


Después de varios ciclos de ejecución: el tiempo de carga se mantiene alrededor de los 7 segundos, la creación de la llave y por ende la indexación tarda entre 5.5 y 6 segundos. La ejecución del SELECT roza la instantaneidad y el FOREACH se mantiene en menos de medio segundo. Conclusión: el tiempo que tardaba la ejecución del SELECT se ha traslado a la indexación. Si hubiese más operaciones de búsqueda más adelante en el código sobre la misma datatable, puede que valiese la pena la creación de la llave pero de lo contrario prevalece la ejecución del FOREACH.

Finalmente hicimos unas pruebas utilizando un DataTableReader a partir del DataTable y recorriéndolo para obtener el mismo resultado, pero fue unas tres veces más lento que el FOREACH.

Muchas Gracias Jason y Pigo sama,

12 feb 2010

Track Item in Solution

Hay algunas cosas, detalles, que nos hacen la vida mas fácil. Esta es una de ellas. Visual Studio trae la característica que en el Solution Explorer resalta el archivo en el que estamos trabajando. Cuando las soluciones son grandes es especialmente útil.

Si alguna vez esta opción no parece habilitada, se puede rehabilitar en Tools -> Options Estando en Options, en el árbol de la izquierda escogemos Projects and Solutions -> General y ahí marcamos el check Track Active Item in Solution Explorer.


Muy útil!

26 ene 2010

Uso de palabra INTERFAZ

Hace algun tiempo un administrador de proyectos que tenia le conuslto a Don Fernando Diez, Filólogo del periodico LA NACION sobre el uso de la palabra interfaz. He aqui su respuesta:

INTERFAZ.

La voz inglesa interface, que significa, en informática, ‘conexión física y funcional entre dos aparatos o sistemas independientes’, se ha adaptado al español en la forma INTERFAZ: «Su interfaz gráfica y capacidades de acceso a Internet facilitarán aún más el uso del PC» (Vanguardia [Esp.] 30.8.95).

Su plural es INTERFACES (→ plural, 1g). Aunque no es infrecuente su uso en masculino, debe emplearse en FEMENINO, ya que esta palabra incluye en su forma el sustantivo femenino FAZ.

Con este sentido, NO debe usarse la forma interfase, que no responde ni a la pronunciación ni a la estructura semántica del étimo inglés, que se ha formado con el sustantivo face, cuyo equivalente español es FAZ, no fase. Tampoco se aconseja usar con este significado el término interficie.


Aparte les dejo un par de definiciones de la RAE con respecto a este tema:

interfaz:

(Del ingl. interface, superficie de contacto).

1. f. Inform. Conexión física y funcional entre dos aparatos o sistemas independientes.


interfase:


1. f. Biol. Período del ciclo celular en el que tiene lugar la síntesis de proteínas y la replicación del material genético.

2. f. Fís. y Quím. Superficie de separación entre dos fases.


Así que, como ven, esta claro: únicamente debe usarse interfaz e interfaces en nuestros quehaceres informáticos.

7 ene 2010

Uno de viejitos

Hace unos días, me asignaron a un proyecto de un cliente que tiene su base de Datos en SQL Server 2000 la cual tenia bastante tiempo de no travesear. Una de las cosas que tenia que atender era habilitar el control de errores de un store procedure que ya existía y bitacorearlos en otra tabla a modo de monitor.

Como alguien dijo alguna vez: "¡No se haga la barba en seco!" refiriéndose a lo doloroso que eso podía llegar a ser. El manejo de errores (asumiendo que se le pueda llamar asi) es increíblemente engorroso, problemático e inflexible (fui generoso en los adjetivos).

Empecemos mi queja: resulta que la variable @@ERROR que es lo único que tenemos para determinar errores en SQL Server 2000 (Ni siquiera piensen en estructuras tipo Try Cacth), solo se puede capturar inmediatamente despues de la sentencia que produce el error. Osea si tenemos algo como esto:

...
WHILE algunacondicion
...
BEGIN
BEGIN TRANSACTION

INSERT INTO miTabla1 (campo1, campo2) VALUES (@parametro1, @parametro2)

UPDATE miTabla2 SET campo3 = @parametro3 WHERE campo4 = @parametro4

@variable1 = @varaible2 / @variable3

COMMIT TRANSACTION
...
END
...

y quisiéramos guardar en una tabla de bitácora, si se produce un error, cualquiera que fuese: por ejemplo que en el INSERT no se pudo ejecutar por problemas de llaves primarias, o que el UPDATE no se pudo ejecutar por problema de llave foráneas o que se dio una división por cero en la operación matemática; tendríamos que hacer algo mas o menos asi:

...
WHILE algunacondicion
BEGIN
....
BEGIN TRANSACTION

INSERT INTO miTabla1 (campo1, campo2) VALUES (@parametro1, @parametro2)
IF @@ERROR <> 0
BEGIN
GOTO logError
END

UPDATE miTabla2 SET campo3 = @parametro3 WHERE campo4 =@parametro4
IF @@ERROR <> 0
BEGIN
GOTO logError
END

@variable1 = @varaible2 / @variable3
IF @@ERROR <> 0
BEGIN
GOTO logError
END

COMMIT TRANSACTION
...
END
...
logError:
...

Luego del tag logError deberíamos tener las sentencias para el tratamiento de errores así como otros "GOTO" para seguir con la ejecución del store procedure.

Increible ¿cierto?

Foy: Ahora, y el mensaje del error ¿como lo capturamos para guardarlo?
SQLServer 2000: ¿Mensaje de error? ¿Que es eso?

Así es, no hay manera de capturar el mensaje de error o como decía un articulo al respecto: "There is no supported way to retrieve the full text of an error message in SQL 2000". Bueno en realidad se supone que si hay una manera, en este articulo se explica que haciendo uso de un store procedure de sistema (xp_readerrorlog) se puede, sin embargo se requiere tener acceso al mismo siendo owner de la base de datos, a mi particularmente no me sirve esta solución (ademas no tuve oportunidad de probar esta solución por eso señalo "se supone que funciona").

En vista de esto no quedo más remedio que usar algo como esto:

SELECT @Mensaje= MSG.description from master.dbo.sysmessages MSG
INNER JOIN sys.syslanguages LANG ON MSG.msglangID=LANG.msglangid
WHERE MSG.error=@Error

Donde @Error es el numero de error que capturamos y @Mensaje es donde dejaremos la descripcion del error. El problema es que en @Mensaje se guardan mensajes como

Cannot insert the value NULL into column '%.*ls', table '%.*ls'; column does not allow nulls. %ls fails.

Por lo que se puede apreciar no aparecen reemplazados los tags por lo que la informacion podría y de hecho es escasa.

La otra opción es escribir mensajes de error personalizados dependiendo del numero de error pero llegado ha este punto ya estaba cansado de las peripecias que habia tenido que hacer. por lo que me quede con el mensaje sin reemplazar tags. Si alguien sabe alguna forma de hacerlo muy bienvenida será, siempre que la recomiendacion no se sea ¿Por que no migras a 2005?...

5 ene 2010

Feliz 2010

Ya pasaron las fiestas, las celebraciones y ya estamos de vuelta en las faenas diarias de todos los días comunes y corrientes. Espero que este 2010 sea mejor que el 2009 pero no tan bueno como el 2011.

Que soplen vientos favorables para todos mis amigos y conocidos. Y que, aunque es mucho pedir, el mundo mejore aunque sea un poquito para bienestar de todos.

Próspero 2010!