Los descubrimientos descritos a continuación, surgieron de mi necesidad de cargar CSS dinámicamente sin estar creando numerosos temas dentro de una aplicación WEB ASP.NET. Esto es, separar la administración de CSS de los temas. Así un mismo tema podrá tener varios CSS a petición. En primera instancia: Toda página que referencie un tema, SIEMPRE cargará todos los CSS incluidos en el mismo, independientemente de que en realidad los necesite, y del nivel de anidamiento en el que se encuentren ubicados. Poco eficiente no es así? Como se sabe, los CSS finalmente son Links referenciados en el HEAD de la página que está referenciándolos. Entonces es cuestión de escribir dinámicamente allí la ruta del CSS que se desea cargar y listo. Lo anterior se logra agregando un ASP.NET literal control dentro del HEAD, y en el load de la página ajustarlo con el valor que se desea. Y listo!!!
A pesar de que existan varios CSS en la misma ruta de aquel que está siendo referenciado, solo el referenciado se descarga. Ventajas?
Conclusión: Si se desea optimizar el tiempo de descarga, entonces es mejor no usar Themes para cargar los estilos, sino que estos se carguen independientemente usando el modelo de HEAD descrito anteriormente.
Ejemplo:
Dado que me han pedido mucho un ejemplo en código aquí va:
Aspx
<html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <asp:Literal ID="Literal1" runat="server"></asp:Literal> <title></title> </head> <body> <form id="form1" runat="server"> <div> <asp:Label ID="Label1" runat="server" Text="Este es un ejemplo de texto"></asp:Label> <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox> </div> </form> </body> </html>
Codebehind
using System; namespace WebApplication1 { public partial class WebForm1 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Literal1.Text = "<link href='Styles/Site.css' rel='stylesheet' type='text/css' />"; } } }
Un Generic Handler (GH) es una clase de objeto .NET que puede procesar http requests, sin necesidad de estar dentro del scope de una página aspx (que está dirigida a presentar salidas de tipo HTML clásico). Un ejemplo de GH es el HTTP Handler. Como es bien sabido un http Handler se puede asociar a cualquier extensión de archivo (de acuerdo a lo permitido por el IIS). Los GH sin embargo, solo se pueden asociar a la extensión ASHX que está directamente soportada por los proyectos web en visual Studio 2005 y posteriores. Así que los GH en .NET se asocian a archivos con extensión ASHX.
Todos los Handlers implementan System.Web.IHttpHandler. Además, en IIS 7 se puede alojar cualquier Handler directamente.
El hecho que el GH pueda correr fuera del entorno de una página y aparte procesar HTTPRequests, lo hace una herramienta perfecta para ofrecer servicios similares a los que ofrecería un Web Service. De esta manera, es un muy buen candidato para reemplazarlos, cuando no se pueden implementar ya sea por requerimientos del negocio o requerimientos no funcionales.Por qué puede no ser deseado un WebService?
Según la arquitectura y requerimientos de algunos clientes, es mejor recibir resultados a través de HTTPRequest "simple", debido a que por ejemplo el llamado se hace desde una plataforma distinta a .NET desde la cual se hace bastante complejo crear un llamado a un WebService, tal vez porque la herramienta no puede manejar todos los tipos estándar en los WebServices (como sucede por ejemplo con PHP antiguo).
Entonces por qué no hacer una página Web Normal que responda a los llamados?
Si se crea una página aspx que responde a las peticiones se incurre en adicionales encabezados y todo el overhead adicional que genera la creación de una página completa (ciclo de vida, viewstate, etc), cuando en realidad sólo se desea obtener una simple respuesta (una imágen un xml, etc).Entonces para ganar performance y facilidad de administración en este tipo de funciones, es posible crear un Generic Handler sin dejar de atender al requerimiento de que la comunicación permanezca sencilla.
Entre los métodos obligatorios al estarse implementando IHttpHandler está ProcessRequest que es el que ejecuta el proceso requerido y por ende al fnal de su alcance habrá de tener un llamado a context.Response.Write o similar, que devuelva una respuesta http. Esta respuesta puede ser html, txt plano, imágenes, xml, etc.
Uso Avanzado
Suponga le siguiente escenario: Existe un objeto en memoria que se desea presentar al usuario como una página html.
Opciones:
Cómo se Logra?
Al crear un GH, éste puede ser llamado como cualquier otra página aspx. (Por ejemplo desde un browser) Además también pueden aceptar parámetros: http://warnov.com/miGH.ashx?param=valor;otroparam=otrovalor a través de su URL. Para incluir un GH en su aplicación Web, solo agreguelo como un nuevo item en el proyecto en Visual Studio.
En el caso especial de querer mostrar un objeto serializado en XML y transformado a través de XSLT es bastante sencillo: Solo se tiene que incluir en el XML la directiva de transformación XSLT y el archivo XSLT se ha de encontrar en la ruta que allí se especifica.
Conclusión:Como se observa, son muchas la aplicaciones que tienen estos objetos y es considerable la ganancia que se obtiene con su uso, aunque en general pasan muy desapercibidos.
Voy a ponerlo lo más simple que pueda:
Un Closure es una entidad de código que encapsula un comportamiento dado teniendo acceso al contexto en que fue definido; en cristiano, es como una clase, pero no con tanta flexibilidad (solo admite una acción dada) y su estado no puede ser cambiado luego de ser inicializado. En síntesis, una seudoclase más especializada, que consume menos recursos. Y que es más felxible que una estructura, dado que se constuye con delegados, que pueden apuntar a distintos métodos.
Cómo se construye?
Tomar el framework 2.0 en adelante, usar sus delegados y métodos anónimos, generar un productor de delegados con un parámetro de configuración y se obtendrá un closure.Ha tenido por ejemplo que calcular un impuesto como el IVA que puede variar según el artículo?Por ejemplo para una crema dental es de 16% pero para un auto es 25%.Entonces el programador bien juicioso se hace el siguiente método:double IvaCalc(double tax, double amount);Así pues siempre que se vaya a llamar al método se han de pasar ambos parámetros:double imp1 = IvaCalc(25, 25800);double imp2 = IvaCalc(16, 4);dpuble imp3 = IvaCalc(16, 5800);etcéteraEntonces se puede optar por hacer dos métodos; uno por cada tipo de IVA.Pero si son 10 tipos de IVA habrán 10 métodos?Así que se opta por hacer una clase calculadora de IVA:De esta manera basta con instanciar un objeto por cada tipo de IVA y ponerlo a trabajar; esto minimiza la cantidad de código escrita, y es bastante claro:IvaCalculator autoCalc=new IvaCalculator(25);IvaCalculator prodCalc=new IvaCalculator(16);double imp1 = autoCalc(25800d);double imp2 = prodCalc(4d);double imp3 = prodCalc(5800d);Pero de nuevo... si son diez tipos distintos de IVA, creará diez instancias de objeto? Es justo tanto empleo de memoria al tener un objeto completo solo para hacer una operación?No lo creo... precísamente para solventar esta situación son útiles los closures.(Entre otras)Un closure permite reflejar la simple funcionalidad de la pequeña clase que diseñamos anteriormente, sin incurrir en el overhead del objeto como tal. Para lograrlo, se requiere una forma de mantener un estado (en este caso, la tasa del impuesto) para no tener que estar pasando el parámetro de configuración en cada llamado. Además se requiere una operación sobre ese estado. Cómo lograrlo sin tener que hacer un objeto?La operación como tal, se logra con un simple método. Pero este método debe ser configurable en su creación de manera que tenga un estado.Para que se pueda configurar es necesario que exista una variable en un ambiente léxico (scope) superior al del método, de manera que esta variable pueda ser inicializada sin necesidad de llamar al método. Esta, será pues la variable que indica el "estado" del método. Pero igual, eso está dentro de una clase. De hecho en C# todo lo que ejecute, ha de estar dentro de una clase. La ventaja ahora, es que vamos a trabajar dentro de la misma clase de ejecución de nuestro flujo. No tendremos que crear más instancias. Tanto el parámetro como la operación deben quedar encapsulados dentro de un mismo ambiente léxico dentro de la clase, para que tengamos la unidad requerida para poder generar instancias (pero no de una clase que es lo que queremos instanciar, sino de algo más liviano que llamaremos "enclosure"). Para esto es necesario poder hacer referencia al método dado.En .NET qué nos permite hacer referencia a métodos? Si... correcto: los delegados. Entonces generamos un ambiente léxico que tenga al delegado referenciando al método y al parámetro de configuración. Obviamente si estamos dentro de una clase y no queremos generar otra, ese ambiente léxico es un nuevo método. Este método deberá retornar el delegado apuntando a la operación ya configurada como es debido, pero además el método al que apunte el delegado deberá estar declarado inmediatamente, de manera que el parámetro de configuración del mismo sea accesible a éste, desde el método que lo contiene.En teoría suena bien. Pero cómo lograr que un delegado apunte a un método que se declara "en línea" junto con éste?Ahí es donde entran los métodos anónimos. Son precisamente eso: Métodos declarados en línea donde son requeridos por los delegados. Generalmente, los delegados se han inicializado con el nombre de un método que cumpla el contrato de su firma. Ahora (Framework 2.0 en adelante), no es necesario declarar el método aparte para poderlo referenciar luego por un delegado (lo que impediría acceder al dichoso parámetro de configuración) sino en línea junto con la declaración del delegado:delegate int MyDelegate(int a, int b);MyDelegate myDel = delegate (int a, int b){ ..... return ....};No olvide el ";".Ya con este conocimiento, podemos generar el método que queremos://Este delegado que permitiradelegate double IvaCalculator_(double tax);IvaCalculator_ ivaCalcProducer(double tax){ return delegate(double amount) { return amount*tax; };}Exótico no?Pero a pesar de ello, logramos lo deseado: El método toma una variable de configuración. Así tenemos estado y manipulación de estado. Lo que haría una clase. Entonces ahora cómo se operaría es muy parecido al uso de clases anterior; solo que ahora no hay overhead por creación de nuevos objetos. Lo que se crean son nuevos delegados. Uno por cada tipo de tasa en este caso. Ellos "cuestan" mucho menos que un objeto.
IvaCalc_ ivaCalc16=ivaCalcProducer(16);IvaCalc_ ivaCalc25=ivaCalcProducer(20);double imp1 = ivaCalc25(25800); double imp2 = ivaCalc16(4); double imp3 = ivaCalc16(5800);
Descargar Ejemplo Completo (Aplicación de consola en C# mostrando el uso de Closures)Y listo. Así se construyen y usan los closures. Ahora ya puede ir y alardear un poco con su equipo de desarrollo acerca del misterioso "Closure"!!
Open mind 4 a different Coding!!!
using (SqlConnection conn = new SqlConnection(myConnectionString)){ conn.Open(); doSomething(conn);}
Para mí esto significa:
Hoy en día los modelos de producto abierto están a la orden. El mismo IE8 permite usar cualquier buscador (Kumo, Google, tú mismo podrías crear el buscador de tu predilección en minutos). Los aceleradores permiten escoger el operador de tu predilección para las acciones disponibles, los WebSlices se basan en estándares completamente abiertos…Y no solo pasa con el Browser. Silverlight es el ejemplo cumbre de la apertura que están teniendo tecnologías que otrora estaban encerradas dentro de una sola corporación. Un usuario con Firefox desde Unix podría bien hacer un request a un servidor Apache que interpreta un PHP que escribe XAML (la aplicación Silverlight como tal) con inyección de datos desde MySQL, para ver en una interfaz tridimensional con audio y video los mejores momentos de la última válida Nascar (el ejemplo va en concordancia con el amigo que me inspiró este post) más las estadísticas de la carrera, en una espectacular aplicación Silverlight con tecnología Microsoft.Un pequeño apunte aquí: Han notado por ejemplo que los sitios principales de las nuevas tecnologías Microsoft ya no son .com sino .net?Ya es muy conocido hoy en día que la diferencia que puede marcar un producto contra su competencia, es la capacidad de ofrecer valor agregado a sus usuarios.Y en tecnología, con la gran cantidad de opciones que hay, este valor agregado se ofrece mediante:
IE8 para mí, cumple perfectamente con estas dos premisas sin las cuales, un producto bien puede estar despidiéndose de su market share. Hoy en día me siento seguro hablando de él sin temor a que saquen la innumerable lista negra de otros tiempos.Más información en inglés y descarga, desde aquí