domingo, 28 de agosto de 2011

Combinando AbsoluteExpiration y SlidingExpiration en el Application Cache

Al insertar un elemento en el Cache de Aplicación, se pueden especificar elementos que determinarán cuándo el objeto es removido del cache. Entre estos elementos tenemos las dependencias, implementadas a través de la clase CacheDependency y que permiten especificar que el objeto cacheado es dependiente de un archivo o de otro elemento en el Cache de Aplicaciones. También tenemos la expiración basada en el tiempo (time-based expiration) definida mediante dos atributos:

La Expiración Absoluta (AbsoluteExpiration) , que define un punto en el tiempo a partir del cual el objeto cacheado es quitado del Cache. En general, se especifica sumando una cantidad de minutos o segundos a DateTime.Now. El siguiente ejemplo muestra cómo se agrega un objeto al cache, que será removido luego de 60 segundos.

Cache.Insert("testkey", "testvalue", null, DateTime.Now.AddSeconds(60), System.Web.Caching.Cache.NoSlidingExpiration);

La Expiración Deslizante (SlidingExpiration) define un período de tiempo máximo entre accesos al objeto cacheado, luego del cual el mismo es removido del cache. Por ejemplo, si definimos un intervalo de 10 segundos, si el objeto es agregado al Cache y luego leído antes de que transcurran los  10 segundos, el mismo se mantendrá en cache por 10 segundos más a partir de ese momento, y así sucesivamente. Si en algún momento transcurren más de 10 segundos desde que se accedió por última vez al objeto, el mismo es removido del Cache. La expiración deslizante se define como un TimeSpan. El siguiente ejemplo muestra cómo se agrega un objeto al cache con una expiración deslizante de 10 segundos.

Cache.Insert("test1", "testvalue", null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(0, 0, 10));


ASP.NET no permite especificar ambos tipos de expiración basada en el tiempo simultaneamente. Sólo se puede definir una expiración absoluta o una deslizante.

Sin embargo, la utilización de una expiración deslizante para un objeto que sea accedido muy frecuentemente, podría hacer que se acceda permanentemente al objeto cacheado y que nunca se refresque el cache si el objeto se modifica. Para evitar esto es aconsejable utilizar adicionalmente a la expiración deslizante, una absoluta.


¿Bien, cómo hacemos esto si ASP.NET no permite definir ambos elementos a la vez? Insertando un segundo objeto en el cache, y una dependencia hacia el mismo:

Cache.Insert("trigger", "anything", null, DateTime.Now.AddSeconds(60),
                             System.Web.Caching.Cache.NoSlidingExpiration);
string[] dependencyKeys = {"trigger"};
var dependency = new CacheDependency(null, dependencyKeys);
Cache.Insert("test1", "testvalue", dependency, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(0, 0, 10));

Analicemos cada línea:
La primera línea, inserta en el cache un nuevo objeto con la clave “trigger”. Este es un objeto “auxiliar”, el valor de este objeto no nos importa, sólo nos importa que tenga definida una expiración absoluta de 60 segundos.

La segunda línea, crea un array de strings para ser pasado al constructor del CacheDependency y que contiene sólo un elemento  que es la clave del objeto definido en la línea 1.

La tercera línea, crea un objeto CacheDependency, que especifica una dependencia con el objeto del Cache de clave “trigger”, utilizando el array definido en el paso 2.

La cuarta línea, inserta nuestro objeto en el cache con una expiración deslizante de 10 segundos, y además, le define la dependencia del paso anterior.

De esta forma este objeto se mantendrá en el cache mientras sea leído con una frecuencia menor a 10 segundos, pero será removido a los 60 segundos independientemente de esto, ya que en ese momento será removido el objeto “trigger” y al depender del mismo, nuestro objeto también será removido.

Espero les sirva.

Hasta la próxima!



miércoles, 15 de junio de 2011

jQuery - Guía de referencia rápida

Una de las principales habilidades para el desarrollador web actual es saber utilizar javascript para poder crear interfaces de usuario interactivas e "inteligentes". Sin duda jQuery facilita mucho esta tarea. A pesar de esto, no utilizo jQuery todo el tiempo y cada vez que lo tengo que usar, tengo que refrescar la sintaxis para poder realizar alguna que otra cosa. Es por eso que creé esta guía de referencia rápida, o más bien "super" rápida que tiene lo mínimo indispensable para poder trabajar con jQuery. La idea no es que sea una guía que explique cómo se usa jQuery, sino una guía para que una persona que ya lo sabe utilizar pueda recordar la sintaxis rápidamente. Espero les sirva tanto como a mí.
Enjoy!

Link de descarga: jQuery - Referencia Super Rápida

domingo, 15 de mayo de 2011

Accediendo a los eventos registrados a través de jQuery para debugging

Cuando queremos debuggear javascript e inspeccionar elementos resulta muy útil utilizar las herramientas de desarrollador que traen incorporadas tanto Firefox (Firebug), IE y Chrome.
Con estas herramientas podemos ver claramente -entre otras cosas- los eventos que los diferentes elementos HTML tienen asociados.
En la siguiente imagen por ejemplo, podemos ver como el input de id “Text1” tiene asociada una función anónima al evento blur() que muestra una alerta.


Sin embargo si registramos el manejador del evento utilizando jQuery, la información que se muestra es la propia de jQuery, que no nos brinda ninguna pista sobre la función que realmente se ejecuta al dispararse el evento.


Para poder ver la información que buscamos, debemos ejecutar la siguiente sentencia en la consola:

$('#Text1').data("events");



Como vemos, esto nos muestra un objeto con la colección de eventos registrados por medio de jQuery para el elemento. Desplegando el evento blur, vemos la función que se llama al  dispararse.
Esto mismo se puede realizar en la consola de Firebug. Para Firefox, existe un plugin adicional llamado Firequery que incorpora esta información directamente en la ventana de inspección al pararnos en el elemento, evitándonos tener que hacer este paso adicional de ingresar a la colección de eventos desde la consola.
Espero les sirva!
Saludos!

viernes, 22 de abril de 2011

Creando un control de fechas con calendario desplegable usando CalendarExtender del AjaxControlToolkit


En esta publicación, vamos a ver cómo podemos crear un control de usuario para el ingreso de fechas, con un calendario desplegable, utilizando los componentes del AJAX control toolkit.
El objetivo, es tener un control donde se pueda ingresar una fecha, tanto por teclado en un campo de texto con máscara, como seleccionando la misma en un calendario desplegable, y utilizando validadores del lado del cliente y del servidor para validar la fecha y mostrar los mensajes de error. 



Empecemos por crear una nueva solución en Visual Studio y agregar un proyecto de tipo ASP.NET Web Application llamado “CalendarControlExample”. Luego, agregamos una referencia a la dll del Ajax control toolkit. Y agregamos un nuevo control de usuario llamado “DateTimePicker”.
También creamos una carpeta llamada “css” y dentro de la misma copiamos el archivo de imagen para la imagen del calendario del control y una hoja de estilo llamada “DateTimePicker.css”.

Continuemos armando el control. En el ascx, creamos un DIV y dentro agregamos:
Un control textbox con las siguientes propiedades:
        ID="txtDateTime"
            Width=”130px”
;
Un control ImageButton con las siguientes propiedades:
ID="ImgBntCalc"
ImageUrl="/css/calendar_icon.jpg"

Un control MaskedEditExtender con las siguientes propiedades:
ID="dateMaskedEditExtender"
TargetControlID="txtDateTime"
Mask="99/99/9999"
MessageValidatorTip="true"
MaskType="Date"
ErrorTooltipEnabled="True"


Un control MaskedEditValidator con las siguientes propiedades:
            ID="dateMaskedEditValidator"
            ControlExtender="dateMaskedEditExtender"
            ControlToValidate="txtDateTime"
            EmptyValueMessage="Date is required"
            InvalidValueMessage="Date is invalid"
            Display="Dynamic"
            TooltipMessage=""

Para hacer más llamativa la interfaz de usuario, usaremos un ValidatorCalloutExtender asociado al MaskedEditValidator para mostrar los mensajes de error en un popup:
            ID="dateMaskedEditValidator_vce"
            TargetControlID="dateMaskedEditValidator"
            Enabled="True"

Por último agregamos el  CalendarExtender:
            ID="cexCalendarExtender"
            TargetControlID="txtDateTime"
            CssClass="cal_Theme1"
            PopupButtonID="ImgBntCalc"

En este punto ya podemos correr la aplicación y probar nuestro control. Para eso agregamos el control a la página Default.aspx que usaremos como página de prueba.

Lo primero que pueden notar, si tienen la configuración regional de Windows en español, es que el calendario se muestra en inglés, y la fecha seleccionada también adopta el formato mm/dd/aaaa pero el validador, sin embargo, la detecta como inválida. 

Esto se debe a que ambos toman la configuración de cultura de diferentes lugares al no estar definidas las opciones de localización. Vamos a agregar entonces una propiedad al control para definirle la cultura que debe usar  y en el Load, asignaremos al calendar extender y al masked edit dicha cultura para evitar este error.


public CultureInfo Culture { get; set; }

protected void Page_Load(object sender, EventArgs e)
{
    if (this.Culture == null)
         this.Culture = CultureInfo.CurrentCulture;

    this.dateMaskedEditExtender.CultureName = this.Culture.Name
    this.cexCalendarExtender.Format = this.Culture.DateTimeFormat.ShortDatePattern;
}


Además, debemos asegurarnos de agregar estos dos atributos en el  ToolkitScriptManager

<ajaxtoolkit:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server" EnableScriptGlobalization="true" EnableScriptLocalization="true" 
/>

Ahora vamos a agregar algunas propiedades adicionales para completar el control:
        public string ValidationGroup
        {
            get { return this.txtDateTime.ValidationGroup; }
            set
            {
                this.txtDateTime.ValidationGroup = value;
                this.dateMaskedEditValidator.ValidationGroup = value;
            }
        }
  public bool IsRequired
        {
            set { this.dateMaskedEditValidator.IsValidEmpty = !value; }
        }

        public bool Enabled
        {
            get { return this.txtDateTime.Enabled; }
            set
            {
                this.txtDateTime.Enabled = value;
                this.cexCalendarExtender.Enabled = value;
                this.cexCalendarExtender.EnabledOnClient = value;
                this.ImgBntCalc.Enabled = value;
            }
        }
        public DateTime Value
        {
            get
            {
                if (String.IsNullOrEmpty(this.txtDateTime.Text))
                {
                    return DateTime.MinValue;
                }
                else
                {
                    DateTime date;
                    DateTime.TryParse(this.txtDateTime.Text, out date);
                    return date;
                }
            }
            set
            {
                this.txtDateTime.Text = value.ToShortDateString();
            }
        }

Ahora, vamos a agregar un botón a la página y una Label para ver cómo se recupera del lado del servidor el valor seleccionado.  En el Load de la página de prueba, simplemente agregamos lo siguiente:

        this.Label1.Text = this.DateTimePicker1.Value.ToString();

y ejecutamos la aplicación. Al seleccionar una fecha en el calendario o al ingresar a mano una fecha en el cuadro de texto, y al hacer click en el botón, luego del postback, el label mostrará la fecha ingresada.
Bien, ahora supongamos que queremos validar que el usuario no ingrese una fecha menor al 01/01/1950. Agregamos este valor como el valor mínimo en el MaskedEdit:

<ajaxtoolkit:MaskedEditValidator ID="dateMaskedEditValidator" runat="server"                     ControlExtender="dateMaskedEditExtender" ControlToValidate="txtDateTime" EmptyValueMessage="Date is required" InvalidValueMessage="Date is invalid"
        Display="Dynamic" TooltipMessage="" MinimumValue="01/01/1950" />

Como vemos si ingresamos un valor menor al 1950, digamos 12/03/1834, el validador valida correctamente:

Pero si ingresamos un año que comience con “00” como se ve en la imagen, el validador, no lo valida y permite hacer el postback, pudiendo tomar dicho valor en el lado del servidor. Luego del postback el control recupera el valor en la interfaz de usuario, pero cambia el siglo por el siglo 1900.




Este es un error del control y para evitarlo, utilizaremos un simple script de javascript para que cuando el usuario ingrese un año que comience con “00”, este sea reemplazado por la menor fecha aceptada por el control


    function DateText_EditClientValidation(sender, args) {
        var year = args.Value.split("/")[2];
        if (year.substr(0, 2) == "00") {
            $get("<%=this.txtDateTime.ClientID %>").value = "<%= this.dateMaskedEditValidator.MinimumValue %>";
        }
    }

Agregaremos este script por código. Para esto, en el Load agregamos las siguientes lineas de código:
        protected void Page_Load(object sender, EventArgs e)
        {
            if (this.Culture == null)
                this.Culture = CultureInfo.CurrentCulture;

            this.dateMaskedEditExtender.CultureName = this.Culture.Name;
            this.cexCalendarExtender.Format = this.Culture.DateTimeFormat.ShortDatePattern;

            var csType = this.GetType();
            var csName = this.ClientID + "_ClientValidator";

            var sb = new StringBuilder();

            sb.AppendLine("<script type=\"text/javascript\">");
            sb.AppendLine("function " + csName + "(sender, args) {");
            sb.AppendLine("var year = args.Value.split('/')[2];");
            sb.AppendLine("if (year.substr(0, 2) == '00')");
            sb.AppendLine(" $get('" + this.txtDateTime.ClientID + "').value = '" + this.dateMaskedEditValidator.MinimumValue + "';");
            sb.AppendLine("}");
            sb.AppendLine("</script>");

            ScriptManager.RegisterClientScriptBlock(this, csType, csName, sb.ToString(), false);

            this.dateMaskedEditValidator.ClientValidationFunction = csName;
        }

Esto añadirá el script a la página y hará que se ejecute cada vez que se ingrese una fecha.
Si al ejecutar la aplicación y al cambiar la fecha, les da este error:

Microsoft JScript runtime error: 'value' is undefined

Deberán agregar este atributo al ToolkitScriptManager

<ajaxToolkit:ToolkitScriptManager runat="server" ID="ScriptManager1" CombineScripts="false"
/>

Esto también se debe a un error del control y el workaround es no utilizar la versión minimizada de los scripts.
Con esto terminamos el control, con las funcionalidades básicas.

Referencias a los bugs mencionados: