Mostrando entradas con la etiqueta desarrollo. Mostrar todas las entradas
Mostrando entradas con la etiqueta desarrollo. Mostrar todas las entradas

martes, 26 de agosto de 2014

Embeber una librería dentro de un ejecutable en .NET

Buenas a to@s

Hoy voy a contaros un recurso que he utilizado alguna vez para evitarme problemas derivados de redistribuir librerías. En momentos es necesario que un programa sea manejable y a poder ser indivisible, para que no se produzcan problemas derivados de la falta de algunas librerías que este utilice. Algunos leguajes de programación como Delphi solucionaban estos problemas muy bien con su característica RunOnce, por eso los programas de Delphi pesaban 2 megas cuando el mismo código en .Net ocupa poco más de 20 Kb. Dejando a Delphi de un lado, el tener la fiabilidad de que tu programa va a funcionar siempre de manera correcta y que nunca le va a faltar una librería (y ademas estas siempre van a ser la versión correcta), es algo que nos da mucha tranquilidad a la hora de redistribuirlo. 

En .Net existe una caracteriza por la que podemos integrar cualquier fichero en nuestro software. Simplemente seleccionamos el fichero, ensamblado, documento… de nuestro proyecto que queramos, vamos a propiedades y en la propiedad “Acción de compilación” seleccionamos “Recurso Incrustado”. Una vez hecho esto ya podemos llamar al cualquier fichero que hayamos incrustado en nuestra aplicación desde dentro sin miedo a que nos dé un error. Para acceder a estos podremos encontrarlos dentro del espacio de nombres de nuestro programa por ejemplo MiPrograma1.imagen.png.

Hasta aquí todo perfecto, podemos incrustar cualquier cosa, pero si probamos a hacerlo con una librería obtendremos un error en tiempo de ejecución de que no encuentra la librería. Para solucionar este problema debemos insertar una línea en el Main de nuestra aplicación y crear un nuevo método en el fichero program de nuestra aplicación para resolver las librerías gracias a los métodos de reflexión.

Dentro de nuestro main debemos escribir una línea similar a esta.
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ResolverComponentes);

Nos suscribimos al método que se encarga de resolver las referencias y creamos un nuevo manejado que invoque la función que vamos a crear para resolver nuestras referencias. De esta forma lo que el programa va hacer es lo siguiente, primero intenta resolver las librerías por sí mismo y sino la encuentra ira a la función ResolverComponentes que hemos creado.

La función ResolverComponentes devolverá un parámetro tipo System.Reflection.Assembly debe tener dos parámetros de entrada, un object y un ResolveEventArgs que será del que obtendremos el nombre de la librería que debemos devolver.

Esta sería una posible definición:

static System.Reflection.Assembly ResolverComponentes(object sender, ResolveEventArgs args)

Para saber cuál es la librería que necesitamos, podemos hacerlo mediante la propiedad name del segundo parámetro, en este caso args.name. Esto es muy útil si tenemos que cargar varias librerías.

Por ultimo debemos devolver la librería cargada, eso lo hacemos mediante el método load del constructor de System.Reflection.Assembly es decir System.Reflection.Assembly.Load(). Posiblemente este método no os aparezca, dado que solo existe en el constructor.

Al revisarlo os daréis cuenta de que no existe ninguna definición en la que podamos pasarle la ruta donde está la librería. Tras darle unas vueltas lo que me pareció más sencillo fue enviar un array de bytes con la librería contenida en él. Para eso primero nos definimos un System.IO.Stream que contendrá el Stream de nuestra librería. Podemos hacerlo de la siguiente forma:

System.IO.Stream _streamDeLibreria;
_streamDeLibreria = _libreriaResolver.GetManifestResourceStream("MiPrograma.Librerias.LibreriaEmbebida.dll");

Una vez definido el Stream ya solo tenemos que leer todos los bytes y devolverla en el objeto System.IO.Stream:

byte[] _arrayDeLibreria = new byte[_streamDeLibreria.Length];
_streamDeLibreria.Read(_arrayDeLibreria, 0, _arrayDeLibreria.Length);
System.Reflection.Assembly _libreria_resuelta = System.Reflection.Assembly.Load(_arrayDeLibreria);

Por último solo quedaría devolver la biblioteca:

return _libreria_resuelta;


Como podéis ver el método no es muy complicado, si un poco enrevesado pero muy útil.

Si por ejemplo necesitáis hacer esto con varias bibliotecas podéis hacer un if, swich o un select case en el que condicionáis con el nombre, por ejemplo algo así:

if (args.Name == "MiLibreria.Libreria, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1b06a8ecf2258a87")
            
Si vais a realizar esto con muchas librerías, podéis definiros una variable System.Reflection.Assembly por cada librería, cargarlas en las diferentes variables con un método como el anterior y el al función ResolverComponentes solamente debemos hacer un return de la variable previamente cargada con la librería.


Aquí os dejo el método completo para que podáis modificarlo y probarlo:

static System.Reflection.Assembly ResolverComponentes(object sender, ResolveEventArgs args)
{

System.Reflection.Assembly _libreriaResolver = System.Reflection.Assembly.GetExecutingAssembly();
System.IO.Stream _streamDeLibreria;

if (args.Name == "MiLibreria.Libreria, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1b06a8ecf2258a87")
{
_streamDeLibreria = _libreriaResolver.GetManifestResourceStream("MiPrograma.Librerias.LibreriaEmbebida.dll");
}
else
{
_streamDeLibreria = __libreriaResolver.GetManifestResourceStream("MiPrograma.Librerias.LibreriaEmbebida2.dll");           
}

           
byte[] _arrayDeLibreria = new byte[_streamDeLibreria.Length];
_streamDeLibreria.Read(_arrayDeLibreria, 0, _arrayDeLibreria.Length);
System.Reflection.Assembly _libreria_resuelta = System.Reflection.Assembly.Load(_arrayDeLibreria);

return _libreria_resuelta;

}


Si alguien lo necesita y lo pide lo puedo “traducir” a VB si fuera necesario, aunque creo que se entiende bastante bien. 

De la misma manera si lo "traducís" y queréis que lo publique, no hay ningún problema, poneros en contacto conmigo y lo vemos. Si alguien realiza el mismo código, lo mejora o lo modifica y lo publica en su blog, puedo poneros un enlace desde el articulo a vuestra versión.


Espero la lectura haya sido amena e interesante y sobre todo que sirva para algo.
Muy importante, si decides comentar o republicar parte de este articulo porque te ha sido útil, por favor cita la fuente y el autor del mismo (vamos cítame) y pon un enlace al artículo de mi blog

Muchas gracias por leerme.
Saludetes a todos

P.D. Podéis seguirme en @Jberron, Google+ y LinkedIn





jueves, 24 de octubre de 2013

Problemas con Inserción desde Procedimientos almacenados al recuperar las claves o cuando usar @@identity Scope_Identity() o Ident_Current()

Buenas a tod@s

Hoy voy a contaros un par de cosillas de los procedimiento almacenados que os puede ser de gran utilidad. Muchas veces, nosotros manejamos la base de datos a través de procedimientos almacenados, ya que es más seguro, ofrece buenas herramientas de análisis, separamos el grueso de acceso a datos del resto de aplicación, permite que modifiquemos sentencias sin tener que generar una nueva publicación… y cualquier otra razón que a vosotros os guste más.

Uno de los problemas que nos enfrentamos a la hora de insertar un nuevo registro, es al devolver la clave del registro insertado, ya que este puede ser que asigne de forma automática. Muchos de nosotros utilizamos el parámetro @@identity para este fin, pero puede que no sea buena idea, ya que es posible que no devuelva en índice correcto.

Existe tres formas de obtener la clave y cada una tiene sus particularidades:
Scope_Identity(): Devuelve  la clave de la última inserción dentro del procedimiento almacenado en el que lo estamos ejecutando.
@@Identity: Devuelve la clave de la última inserción, a nivel de sesión. Esto quiere decir que si después de la inserción se ejecuta un trigger, al recuperar el valor de la clave, devolverá la clave del trigger y no la del procedimiento almacenado donde lo ejecutamos.
Ident_Current(): Devuelve la última clave introducida sin tener en cuenta sesiones. Es decir, si por ejemplo estamos introduciendo datos con dos usuarios a la vez devolverá la clave de la última inserción independientemente del usuario que lo haya insertado. Es probable que ambos reciban el mismo valor si la inserción ha sido simultánea.

Como podéis comprobar, cada una de las funciones está delimitada de una forma muy concreta. Es posible que para ciertos casos nos interese una u otra, eso tendremos que verlo en su contexto. Lo que está claro es que en muchos casos utilizamos @@identity cuando no es correcto y puede producir un gran problema de coherencia en los datos.

La información a sigo extraída de http://technet.microsoft.com/es-es/library/ms187342.aspx

Espero que este tema haya resultado interesante, no muy aburrido, que ahorre un susto a más de uno y sobre todo que sirva para algo.

Muy importante, si decides comentar o republicar parte de este articulo porque te ha sido útil, por favor cita la fuente y el autor del mismo (vamos cítame) y pon un enlace al artículo de mi blog

Muchas gracias por leerme.
Saludetes a todos

P.D. Podéis seguirme en Twitter @jberron y LinkedIn



miércoles, 8 de mayo de 2013

Problemas con Internet Explorer 10 y paginas web ASP.NET

Buen@s a todos

Ultimamente me he puesto un poco técnico, jeje.... ¡¡Que le vamos es el defecto profesional!!

Bueno sus cuento la batalla poque no tiene desperdicio. Hace un tiempo relativamente corto, Microsoft actualizo el Internet Explorer a la versión 10 (usuario de Xp, lo siento que os quedasteis en la 8 pero la 10 esta bastante chula).

Hasta aqui todo bien, el problema es cuando juntas .NET Framework 4 y el Internet Explorer 10. Normalmente para dotar de cierto dinamismo nuestra pagina, utilizamos contenedores UpdatePanel, ListView.... Bueno pues, si metes una imagen dentro de un UpdatePanel y programas el evento "click" te darás cuenta de que hace caso omiso a lo que estes haciendo. Si depuras la pagina desde la consola de depuración veras el siguiente error "Sys.WebForms.PageRequestManagerServerErrorException: cadena de entrada no estaba en un formato correcto".

La pregunta es, ¿si funciona en Firefox, Chrome, porque no funciona en Internet Explorer 10?

Bueno como respuesta simplmenete comentar que el error a sido reportado y que existen varios FixIt de .Net Framework que se supone que solucionan dicho problema. Con sinceridad puedo deciros que al menos a mi no me han funcionado. Es más, uno de ellos me desconfiguro totalmente el Internet Informatión Server de mi Windows Server 2008 R2. Vamos horrible.

Bueno pues lo dicho, tras darle muchas vueltas al tema, encontre que el propio Internet Explorer 10. tiene una "Consola de depuración", que podemos utilizar para depurar nuestra web.

En esta consola arriba a la derecha podemos escoger el tipo de "Standart"  (lo llaman así...) que estamos utilizando. Si seleccionamos por ejemplo "Internet Explorer 9", veremos que nuestra página se ejecuta correctamente...

Por lo tanto el problema esta claro que es del propio navegador y del "Standart" que utiliza, por lo tanto si le forzamos para que utilice un Standart diferente solucionamos el problema.

Con esta linea dentro de la cabecera de nuestra página web (<HEAD></HEAD>)  solucionamos el problema:

Forzamos a Internet Explorer 8:
<meta http-equiv="x-ua-compatible" content="IE=8">
Forzamos a Internet Explorer 9:
<meta http-equiv="x-ua-compatible" content="IE=9">

Si utilizáis una página con MasterPage debeis meterlo en la MasterPage, ya que las demás no tienen etiqueta Head.

Espero que esta entrada os sea de utilidad.

Saludetes a tod@s

Muy importante, si decides comentar o republicar parte de este articulo porque te ha sido útil, por favor cita la fuente y el autor del mismo (vamos cítame) y pon un enlace al artículo de mi blog

Muchas gracias por leerme.

P.D. Podéis seguirme en Twitter @jberron y linkedin

miércoles, 24 de abril de 2013

Problema al mostrar muchas filas en un GridView (más de 1.000)

Buenas a tod@s

La semana pasada me encontraba desarrollando una aplicación en ASP.NET para mi empresa. En esta aplicación, tiene una serie de filtros que una vez aplicados muestran una tabla con los registros, que además posee una casilla para seleccionarlo. Evidentemente hablamos de ASP.NET por lo que para mostrar todos los elementos de la tabla utilice un control GridView con la primera columna con CheckBox para poder marcar o desmarcar los datos. Tras realizar un montón de pruebas dimos por válida la aplicación.

Cuando se lo mostramos a nuestro director el comenzó a hacer pruebas y encontró un error en el control GridView. Realizando un filtrado que devolvía más de 1.000 líneas la página queda congelada y si pulsas cualquier otro control no funciona. El problema es que no da un error como tal hasta que intentas recargar con F5 y entonces aparece este error "Operation is not valid due to the current state of the object".

Como indico anteriormente el problema viene dado por que son más de 1.000 líneas las que se muestran en nuestro GridView. Para solucionar este problema evidentemente podemos paginarlo a un máximo de 1.000 líneas por ejemplo o podemos ampliar el número de líneas que el GridView puede mostrar.

Esto último es muy sencillo simplemente hay que introducir un parámetro en el appconfig de la aplicación web, donde le indicaremos el máximo de líneas que mostrarán las colecciones.

Esta es la línea en cuestión:

<add key="aspnet:MaxHttpCollectionKeys" value="Número de Líneas" />

Como podéis observar algo sencillo a la vez que útil.

Este problema lo solucione gracias a Google (por mostrarme la información que buscaba) y al blog de  Max Martin Rojas (Martin Cox Rojas Blog) que escribio el siguiente Artículo (el cual te agradezco mucho)

Muy importante, si decides comentar o republicar parte de este articulo porque te ha sido útil, por favor cita la fuente y el autor del mismo (vamos cítame) y pon un enlace al artículo de mi blog

Muchas gracias por leerme.

Saludetes a tod@s

P.D. Podéis seguirme en Twitter @jberron y linkedin