Codepoint

by Trentia Consulting

C# 6 : Nuevas Características, Exception Filtering & String Interpolation

La ultima versión de C# que ya se encuentra en funcionamiento es C# 6, con esta versión vinieron varias características de lo más útiles pero poco conocidas por algunos, hoy os traemos un par de ellas, las que creemos que mas uso “cotidiano” se les puede dar!

Exception Filtering

La primera que trataremos son los filtros para excepciones.

El filtro para excepciones nos permite añadir una condición al bloque catch de un try, así evitando la ejecución del bloque catch si la condición no se cumple. Esto es diferente a anidar una bloque if dentro del bloque catch y relanzar la excepción ya que en ese proceso se pierde información relacionada con la excepción original. Además es mucho más limpio a nivel de código.

Veamos un primer ejemplo :

Implementación en C# 6:

//Exception filters :
try
{
    throw new NotImplementedException();
}
catch(NotImplementedException ex) when ((DateTime.Now.Day % 2) == 0)
{
    Console.WriteLine("Hoy es par");
}
catch(NotImplementedException ex) when ((DateTime.Now.Day % 2) != 0)
{
    Console.WriteLine("Hoy es impar"); 
}
         	

Implementación C# 5:

try
{
    throw new NotImplementedException();
}
catch(NotImplementedException ex)
{
    if((DateTime.Now.Day % 2) == 0)
    {
        Console.WriteLine("Hoy es par");
    }

    throw;
}
catch(Exception ex)
{
    if((DateTime.Now.Day % 2) != 0)
    {
        Console.WriteLine("Hoy es impar");
    }

    throw;
}

 

Se nota a simple vista la diferencia entre la escritura de un código equivalente. A mi juicio esta característica era una evolución lógica del lenguaje, una sintaxis completamente inteligible visualmente y una palabra clave con la que la mayoría de los .NET developers ya estamos familiarizados (when).

Por plantear un caso de la vida real en el que podríamos utilizar esta característica, los logs de errores y excepciones.

// Example 2
try
{
    throw new NotImplementedException();
}
catch (NotImplementedException ex) when (Log("Error"))
{
    //Caching
}
	

Donde, claro esta,“Log” devuelve un valor booleano. y este llevaría el mensaje o la excepción a nuestro sistema de logueo preferido ( BBDD, fichero, servicio web , etc... )

String Interpolation

La interpolación de cadenas de texto nos permite la inserción de expresiones directamente dentro de los “string” que estén precedidos por el carácter “$” así pues representa una alternativa simplificada al clásico “string.Format”.

Incorporando esta característica de C# 6 podemos escribir cosas así :

int Number1 = 25;
int Number2 = 67;

Guid id1 = Guid.NewGuid();

string CadenaResultante = $@"
    El numero 1 es {Number1} 
    El numero 2 es {Number2}
    El Guid1 es {id1}
";

}

///Output
    El numero 1 es 25
    El numero 2 es 67
    El Guid1 es f6f9f4cf-9261-4d90-8f53-636d920d5d28

Como vemos podemos intercalar los valores de variables locales en los strings pero podemos ir algo más allá:

string CadenaResult = $@"
    Hoy es día : {DateTime.Now},
    El resultado de 2 + 5 es : {2 + 5},
    Creacion de un guid : {Guid.NewGuid()}
";

También valido, donde vemos que podemos usar propiedades de objetos, métodos y expresiones.

Y queriendo rizar un poco mas el rizo una de las características que aporta el “string interpolation” es que se les puede dar formato a ciertos tipos de datos con una sintaxis muy sencilla.

int Number1 = 25000;
string CadenaResult = $@"
    Numero Formateado : {Number1:c4},
    Fecha de hoy formateada: {DateTime.Now:dddd, MMMM dd - yyyy}
";

///Output
    Numero Formateado : 25,0000 ?,
    Fecha de hoy formateada: miércoles, julio 27 - 2016

Todos estos ejemplos pueden escribirse con string.Format pero la limpieza del código, las conversiones de tipos y los formatos tal y como los permite el string interpolation en C# 6 son un aporte muy significativo .

 

Esta claro que estas características de C# 6 solo están disponibles con las ultimas versiones de las herramientas de Microsoft.  (Visual Studio 2015).

Ya veis, mirar hacia adelante siempre nos facilita las cosas, innovar o morir!

JavaScript : Carga dinámica de ficheros e inyección de dependencias.

Hoy en día las aplicaciones web cada vez gozan de más dinamismo al momento de interactuar con el usuario lo que implica más programación de Front-End.

Cada vez más se traslada parte de la lógica de negocio de las aplicaciones web para ejecutarse en la parte cliente de la aplicación. Esto es una carga para el ordenador del usuario cada vez mayor. Y cada vez es más necesario el considerar cargar de forma dinámica archivos JavaScript justo en el momento en que sea necesario para reservar los mínimos recursos que podamos y todo sea fluido y óptimo.

Si además combinamos este comportamiento añadiéndole un orden de carga y permitimos que se realicen ejecuciones concretas justo cuando se haya cargado el código, obtenemos una simple inyección de dependencias.

(function (window) {
    window.ModuleLoader = {
        _ModulesLength: 0,
        _ModulesLoaded: 0,
        AfterLoading: undefined,

        Load: function (data) {
            var _modules = data.Modules;
            this._ModulesLength = _modules.length;
            this.AfterLoading = data.AfterLoading;

            if (typeof data.BeforeLoading === 'function')
                data.BeforeLoading();

            for (var i = 0; i < this._ModulesLength; i++) {
                var script = document.createElement("script");
                script.type = "text/javascript";

                if (script.readyState) {
                    if (_modules[i].callback !== undefined)
                        script.onreadystatechange = this.Execution(_modules[i].callback);
                } else {
                    if (_modules[i].callback !== undefined)
                        script.onload = this.Execution(_modules[i].callback);
                }

                script.src = (_modules[i].host || "") + _modules[i].url;
                document.getElementsByTagName("head")[0].appendChild(script);
            }
        },
        Execution: function (callback) {
            return function () {
                ModuleLoader._ModulesLoaded++;
                callback();

                if (ModuleLoader._ModulesLoaded >= ModuleLoader._ModulesLength)
                    ModuleLoader.AfterLoading();
            };
        }
    };
})(window);

El código anterior añade al objeto Window un objeto en su miembro “ModuleLoader” el cual tendrá una función llamada Load que espera un solo parámetro que será un “object” con esta forma:

 

{
   Modules : [
        	          {
           	    Host: string,
           	    url: string,
           	    callback: function
       		} 
   	   ],
            AfterLoading : function ,
            BeforeLoading : function 
}

Esto definirá los módulos que queremos cargar, que se traducen en ficheros y las acciones que queremos realizar después de la carga de los mismos. Estos Callbacks son la clave para resolver las dependencias necesarias para los módulos que pretendamos cargar.

También incorpora 2 Callbacks adicionales para ejecutar antes y después de la carga completa de todos los módulos especificados.

Todo esto nos permite cargar los ficheros JavaScript que necesitemos, así como las librerías JavaScript que fueran necesarias para el funcionamiento de los mismos.

Ahora expondremos un ejemplo claro cargando dos ejemplos de modulo que dependen uno de otro y a la vez dependen de Jquery :

ModuleLoader.Load({
    Modules : [
            // Cargamos Jquery
            {
                host: "https://ajax.googleapis.com/",
                url: "ajax/libs/jquery/2.2.2/jquery.min.js",
                callback: function () {
                    //Acciones de inicialización cuando Jquery quede cargado.

                }
            },
            {
                host: "",//Dejar en blanco host es como usar el host actual
                url: "OtroFichero.js",//Este modulo tiene una dependencia, en su interior utiliza otro modulo que esta en otro fichero. 
                //Cargamos el modulo y cuando se haya acabado de cargar, cargamos su dependencia
                callback: function () {
                    //  Hemos cargado los modulos en orden y realizaremos aquí las operaciones necesarias.
                }
            }
        ],
  AfterLoading: function () {
            alert("foo");
        },
        BeforeLoading: function () {
            alert("bar");
        }
});


//Fichero OtroFichero.js
(function (namespace) {

    ModuleLoader.Load({
                //En este fichero se encuentra el modulo del que depende OtroFichero.js
                Modules: [ 
                    {
                        host: "",//Cargamos el modulo dependiente. Cuando hayamos cargamos todas las dependencias
                        url: "FicheroModulo2.js",
                        callback: function () {
                            namespace.ModuleInstance = new Module(window.Module2);
                            alert(namespace.ModuleInstance.Result);
                        }
                    }
                ]
            });

    function Module(dependency) {
        this.Result = dependency.Calculation();
    }

})(window);



//Fichero FicheroModulo2.js
(function (parentObject) {
    parentObject.Module2 = { 
        Calculation : function () {
            return 2 + 2;
        }
    };
})(window);

 

Es muy importante comprender el orden en el que se ejecutan las cargas y cómo reacciona el código escrito en los módulos cargados. Por ejemplo no sería correcto intentar utilizar un modulo que no este cargado previamente, eso conduciría a errores en el código.

Así logramos cargar de forma dinámica todos los módulos necesarios para el buen funcionamiento de nuestra aplicación web. Esta implementación no utiliza ningún tipo de librería.

Es importante observar que los módulos que se cargan están implementados de una manera en concreto para que se comuniquen entre ellos correctamente.

Esta manera de implementar módulos permite agrupar los objetos en algo similar a los espacios de nombres conocidos en otros lenguajes.

SharePoint 2013 : Evitando el error ‘The Maximum allowed value is 4096’

Recientemente trabajando en el desarrollo de una aplicación web con base de SharePoint 2013, se me ordenó el desarrollo de un centro de búsqueda y de descarga de contenidos en SharePoint, así que me puse manos a la obra.

Esta parte de la aplicación permite al usuario recibir gran cantidad de contenidos por lo que decidimos atacar al servicio de búsqueda de SharePoint 2013 utilizando la librería CSOM 15, dado que las búsquedas sería mucho más optimas y mejoraría la experiencia de usuario. Así mismo existen gran cantidad de filtros que permite al usuario realizar una criba muy especifica de los contenidos que desea recibir. En este proceso se construye una consulta en Keyword Query Languaje (KQL en adelante) dependiendo de los datos que el usuario haya seleccionado en la sección de filtros.

Así pues el código que genera la consulta tiene un aspecto similar a este :

/// <summary>
/// Searches al documents that maches the specified metadata and retrieve all specified properties
/// </summary>
/// <param name="Filters">Filters to add</param>
/// <param name="PropertiesName">Properties to retrieve</param>
/// <returns></returns>
public IEnumerable<IDictionary<string, object>> SearchDocuments(IDictionary<string, object> Filters, IEnumerable<string> PropertiesName)
{
    using (var context = new ClientContext(_host))
    {

        context.AuthenticationMode = ClientAuthenticationMode.FormsAuthentication;
        context.FormsAuthenticationLoginInfo = new FormsAuthenticationLoginInfo(_user, _password);

        KeywordQuery query = new KeywordQuery(context);
        if(PropertiesName != null)
        {
            foreach (var PropertyName in PropertiesName)
            {
                query.SelectProperties.Add(PropertyName);
            }
        }

        query.QueryText = "IsDocument=true";

        if (Filters != null && Filters.Any())
            query.QueryText = query.QueryText + " AND ";

        foreach (var filter in Filters)
        {
            if (filter.Value is Array)
            {
                query.QueryText = query.QueryText + "( ";
                foreach (var valueItem in filter.Value as IEnumerable<object>)
                {
                    query.QueryText = query.QueryText + filter.Key + ":*" + valueItem.ToString() + "*";
                    if (((IEnumerable<object>)filter.Value).Last() != valueItem)
                        query.QueryText = query.QueryText + " OR ";
                    else
                        query.QueryText = query.QueryText + " ) ";
                }
            }
            else
            {
                if (filter.Value is bool)
                {
                    query.QueryText = query.QueryText + filter.Key + "=" + filter.Value.ToString().ToLower();
                }
                else
                {
                    query.QueryText = query.QueryText + filter.Key + ":*" + filter.Value.ToString() + "*";
                }
            }
            if (Filters.Last().Key != filter.Key)
                query.QueryText = query.QueryText + " AND ";
        }

        SearchExecutor executor = new SearchExecutor(context);
        ClientResult<ResultTableCollection> results = executor.ExecuteQuery(query);

        context.ExecuteQuery();
        return results.Value[0].ResultRows;
    }
}

 

Habiendo visto el código que genera las consultas, es fácil imaginar que es posible que la consulta KQL alcance grandes tamaños sobretodo si añadimos que muchos de los valores que se usan en la búsqueda son identificadores globales únicos (GUID por sus siglas en inglés).

Si construimos pues, una consulta excesivamente larga y la lanzamos contra el servicio de búsqueda de SharePoint 2013, recibiremos esta excepción : ( y ningún resultado )

System.ArgumentOutOfRangeException : The Maximum allowed value is 4096, Parameter Name ‘QueryText’

   ¿Por que es lanzada esta excepción ? SharePoint 2013 tiene un limite de caracteres en la búsqueda cuando se usa KQL, este por defecto está establecido en 4096 bytes lo que no permite realizar consultas muy largas o muy complejas. Pero esto tiene solución o por lo menos para la mayoría de los casos, pues este límite se puede incrementar. lo que en la mayoría de casos nos permitirá realizar las consultas que antes no nos permitía el límite de caracteres.

¿ Por que digo que se puede solucionar para la mayoría de los casos ? Pues por que el límite “sólo” se puede incrementar hasta 20480 bytes, lo que en la mayoría de casos nos permitirá realizar las consultas que antes no nos permitía el límite de caracteres. Esta tarea es sencilla pero no se puede realizar desde la administración central de SharePoint 2013 si no que tendremos que establecer el nuevo valor desde la “SharePoint 2013 Management Shell”. El valor que tenemos que modificar es una propiedad llamada MaxKeywordQueryTextLength del servicio de búsqueda. Para realizar eso abriremos como administradores una “SharePoint 2013 Management Shell” e introduciremos los siguientes comandos en la consola :

PS C:\Users\trentia> $search = Get-SPServiceApplication | where {$_.displayname -contains "search"}
PS C:\Users\trentia> $search.MaxKeywordQueryTextLength
4096
PS C:\Users\trentia> $search.MaxKeywordQueryTextLength = 20480
PS C:\Users\trentia> $search.Update()
PS C:\Users\trentia> $search.MaxKeywordQueryTextLength
20480
## Quizás no sea necesario pero cerciorémonos que todo quede intacto
 
PS C:\Users\trentia> Restart-Service spsearchhostcontroller
PS C:\Users\trentia> IISRESET

Y con este sencillo proceso el límite queda incrementado hasta 20 kb lo que nos permitirá realizar consultas muchísimo más complejas.

Espero que sea de ayuda !