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 !

Projection y Query en Orchard CMS

El módulo Projector nos permite crear instancias de Query y Projection, consultas de datos y páginas que muestran el resultado de estas.

En este artículo crearemos una consulta que devuelva todos nuestros productos y una página que muestre el listado de estos. Suponemos que inicialmente en nuestro proyecto ya existen diferentes Content Types que tienen asociado un ProductPart.
public class ProductPart : ContentPart<ProductPartRecord>
{
  public string SerialNumber
  {
    get { return Retrieve(r => r.SerialNumber); }
    set { Store(r => r.SerialNumber, value); }
  }

  public decimal Price
  {
    get { return Retrieve(r => r.Price); }
    set { Store(r => r.Price, value); }
  }
}

public class ProductPartRecord : ContentPartRecord
{
  public virtual string SerialNumber { get; set; }
  public virtual decimal Price { get; set; }
}
Necesitamos una Query que devuelva todos los elementos cuyo tipo de contenido contenga un ProductPart adjunto. En el framework no tenemos un filtro que permita esta funcionalidad, así que creamos una nueva clase que herede de IFilterProvider:
public class ProductPartFilter : IFilterProvider
{
  public void Describe(DescribeFilterContext describe)
  {
    describe.For("Content","Content","Content")
        .Element("ProductPartFilter", "ProductPartFilter", "ProductPartFilter", ApplyFilter, DisplayFilter);
  }

  private void ApplyFilter(FilterContext context)
  {
    context.Query = context.Query.Join(x => x.ContentPartRecord(typeof(ProductPartRecord)));
  }
}
Ahora ya podemos añadir la nueva Query desde la interfaz web de administración asignándole el título "Productos". La editamos y hacemos clic en "Add a new Filter" para seleccionar el filtro que hemos creado anteriormente.

Es posible que por alguna razón específica de nuestro proyecto necesitemos realizar lo mismo programando directamente, en este caso podemos utilizar el siguiente código:
string name = "Productos";
string type = "ProductPartFilter";

QueryPart query = _queryService.CreateQuery(name);

var form = new Form { ContentTypes = type };
var serializer = new XmlSerializer(form.GetType());
StringWriter sw = new StringWriter();
XmlWriter writer = XmlWriter.Create(sw);
serializer.Serialize(writer, form);
var state = sw.ToString();

query.FilterGroups[0].Filters.Add(new FilterRecord
{
  Category = "Content",
  Description = "ProductPartFilter",
  Position = 0,
  State = state,
  Type = type
});

_contentManager.Publish(query.ContentItem);

[Serializable]
public class Form
{
  public string Description { get; set; }
  public string ContentTypes { get; set; }
}
Por último, para crear la página que muestra el listado de productos, hacemos clic en New Projection. Asignamos el título "Listado Productos", la url "productos" y seleccionamos en el desplegable la Query "Productos".

Una vez más, si necesitamos crear la instancia de Projection programando, tenemos el código equivalente:
var projection = _contentManager.New("ProjectionPage");
projection.As<TitlePart>().Title = "Listado Productos";
projection.As<AutoroutePart>().DisplayAlias = "productos";
projection.As<ProjectionPart>().Record.QueryPartRecord = query.Record;

string ownerName = _siteService.GetSiteSettings().SuperUser;
var owner = _membershipService.GetUser(ownerName);
projection.As<ICommonPart>().Owner = owner;

_contentManager.Create(projection, VersionOptions.Published);
Projection - Orchard Documentation

Actualización de la URL de alertas en SharePoint

Desde hace varios días estoy realizando una migración cautelosa y paso a paso de un portal complejo en SharePoint al cual se estaba accediendo via protocolo HTTP y al cual se deberá acceder a partir de ahora por HTTPS, o sea, con encriptación SSL. Uno de los usuarios ha detectado un curioso efecto y es que los correos de alerta que le están llegando incluye enlaces a HTTP, cuando en realidad este acceso ya no existe. Por lo visto es un problema bastante común y tiene fácil solución, no solo para este sino para otros escenarios.

He dado con un script de Powershell y algunos articulos muy útiles para reparar estas URLs de alerta de SharePoint. El script en cuestión se llama Invoke-AlertFixup, que como veis tiene un nombre suficientemente explícito. Nos permite indicar la URL base de alertas de las que nos queremos deshacer y sustituirla por la nueva URL o la URL en vigor del portal. En mi caso seria:

> Invoke-AlertFixup –site https://miportal –url http://miportal

Cómo podéis ver estoy realizando el cambio de URLs. El proceso es rápido y efectivo. Os dejo los materiales:

Script de PoweShell en Script Center de MS: Update alerts by using Windows PowerShell

Artículos de Technet sobre el tema:

Solución a la hora de filtrar Foreign Keys con identificadores Guid en la grid de la suite de controles Telerik MVC Extensions

Seguramente los que hayáis trabajado con la suite de controles de Telerik para MVC os hayáis dado cuenta que determinados operaciones con los controles Grid no funcionan como deberían. Entre ellas, la más destacada y a la que no hemos encontrado solución hasta ahora, se trata del error que se producía a la hora de filtrar una grid con Foreign Keys donde la Primary Key eran identificadores únicos (Guid o Uniqueidentifier).

Parece ser que Telerik no ha querido darle la solución a esta problemática ya que este error se arrastra desde versiones muy iniciales de esta suite de controles y estoy seguro que no hemos sido los únicos que nos hemos encontrado con este problema.

Pues bien, después de darle vueltas al asunto,en Trentia Consulting no hemos tenido más remedio que darle solución y para ello hemos descargado el último código fuente y lo hemos modificado para dar solución al problema.

Aunque al final de esta entrada acompañamos el código fuente modificado para que los podáis compilar y actualizar a vuestra solución, hemos querido resaltar algunos pasos que creemos que son los más importantes.

Vamos a ello, principalmente cuando filtrábamos por una FK, la llamada AJAX que se encargaba de dibujar el GRID nos daba el siguiente error en la pila del Stack Trace

Invalid cast from "System.String" to "System.Guid" 
System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider) +10578274 
	
System.String.System.IConvertible.ToType(Type type, IFormatProvider provider) +8 
	
System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider) +10625268 

Con este error, deducimos que la la conversión de String a Guid no estaba implementada. Así que después de investigar, lo primero que tuvimos que tocar fue la clase FilterLexer ubicada dentro del namespace Telerik.Web.Mvc.Infrastructure.Implementation.

Revisando el método Tokenize vimos que el parse de parámetros Guid no estaba implementado, así que tuvimos que añadir lo siguiente:

 public IList Tokenize()
        {
            List tokens = new List();

            while (currentCharacterIndex < input.Length)
            {
                string result;

                if (TryParseGuid(out result))
                {
                    tokens.Add(UniqueIdentifier(result));
                }
                else if (TryParseIdentifier(out result))
                {
                    tokens.Add(Identifier(result));
                }
	...
}


Aquí hemos implementado el método TryParseGuid, que se encargará de detectar y formatear el string del identificador del filtro a Guid.

El código es el siguiente

private bool TryParseGuid(out string guid)
{
            SkipSeparators();

            int longitud=36;

            StringBuilder result = new StringBuilder();
            for (int i = 0; i < longitud; i++)
            {
                if((i + currentCharacterIndex)<input.Length)
                    result.Append(input[i + currentCharacterIndex]);
            }

          
            Guid outGuid = Guid.Empty;

            if (Guid.TryParse(result.ToString(), out outGuid))
            {
                guid = result.ToString();
                currentCharacterIndex+=longitud;
                return true;
            }
            else
            {
                guid = null;
                return false;
            }
}

Una vez implementada la detección del Guid, ya sólo nos queda ir al resto de clases que integran las funcionalidades de filtrado de la Grid de Telerik como la clase FilterParser.cs, GuidNode.cs y FilterTokenType.cs del mismo namespace.

Aquí os dejo el enlace de descarga del artículo que hemos redactado en CodeProject con el fin de compartir este problema con el mayor número de personas.

Descarga de la solución

Espero que os sea de ayuda.

Proyecto Orchard: CMS en ASP.NET MVC

Orchard es un gestor de contenidos web o CMS que parece contar con la bendición de Microsoft. Recientemente hemos estado desarrollando con este CMS, como alternativa a Umbraco, otro conocido CMS de .NET.


Orchard nos ha gustado especialmente por sus características funcionales y técnicas.

Orchard cubre bastante bien aspectos que otros CMS quizás no tengan demasiado presente, como el soporte multiidioma y multicultural, que acostumbra a ser un problema. También dispone de una buena extensibilidad a muy diversos niveles, que nos permite trabajar con vistas de MVC para definir hasta el más mínimo aspecto de la web. Tenemos control absoluto del HTML resultante y eso es muy positivo. También contempla bien aspectos de SEO, tan necesarios hoy en día. Y finalmente, ofrece un buen rendimiento, aspecto que también puede resultar complejo en CMS basados en Microsoft .NET.

Nuestro último proyecto en Orchard es el desarrollo de una web con CMS, haciendo uso deFoundation Zurb como framework de diseño responsable. Estamos realmente satisfechos con el resultado y con Orchard. Os recomiendo que lo tengáis presente en vuestro portfolio de soluciones CMS.

Web essentials, indispensable para nuestro Visual Studio

Si trabajáis en entornos web y diseñáis con el entorno de Visual Studio, os habréis dado cuenta que el entorno no nos ayuda demasiado a la hora de realizar nuestras tareas de diseño. Y si nos empeñamos hacer compatible nuestro desarrollo con múltiples plataformas se nos complica mucho más el trabajo ya que hemos de estar comprobando por cada propiedad CSS la compatibilidad con los diferentes motores de navegación.

Para estas problemáticas, tenemos un extensión muy interesante para Visual Studio 2012 (también las hay disponibles para VS 2010) y que casi se va a convertir en imprescindible cuando vayamos a diseñar con VS. Se trata de Web Essentials para Visual Studio.

Esta extensión nos proporciona las siguientes funcionalidades:

TypeScript
Para quien no lo conozcáis, TypeScript es un superset tipado de JavaScript (typed superset) y es muy útil para implementar aplicaciones escalables en desarrollos JavaScript. Pues bien, con la extensión de VS en el momento de guardar el fichero X.ts, web essentials compila el fichero y genera el código JavaScript correspondiente.

Edición CSS

Con la aparición de CSS 3 tenemos multitud de propiedades específicas por cada navegador. Con la extensión instalada nos añade los tags –moz, –webkit, –s y –o automáticamente. Con el siguiente ejemplo veremos el resultado equivalente de border-radius para cada navegador. Cuando escribimos border-radius se generán automáticamente los equivalentes para cada navegador y los valores de las propiedades se modificarán automáticamente al resto cuando modifiquemos una de ellas.

image_thumb_1

Otra ayuda visual que disponemos es la de poder ver con un sólo golpe de vista la compatibilidad de todas las propiedades o la posibilidad de poder ver el color correspondiente de una color hexadecimal por ejemplo

image_thumb_2

image_thumb_3

Intellisense
Tenemos Intellisense para añadir regiones dentro del CSS

image_thumb_4

Intellisense para añadir fuentes personalizadas

image_thumb_5

Estas son sólo algunas de las funcionalidades que aporta Web essentials, os recomiendo seguir el siguiente enlace para que descubráis toda las posibilidades que os aporta esta extensión.

http://visualstudiogallery.msdn.microsoft.com/07d54d12-7133-4e15-becb-6f451ea3bea6

Wizard para generar expresiones de Set Analysis en QlikView

Este asistente ayuda a generar rápidamente expresiones de “Set Analysis” para Qlikview.

Se puede aplicar a conjuntos estándares, selecciones anteriores y siguientes, marcadores usando las funciones de agregación básicas, sobre cadenas de caracteres, contadores y agregaciones estadísticas.

También dispone de una lista de ejemplos de expresiones más utilizadas.

clip_image001_77e476f3-7f11-40d1-a49d-972be60316db

Autenticación Windows en MVC 4 redirige a página de login

En mi caso ha sido a Login.aspx, pero podría haber sido /Account/Login. Por alguna razón las llamadas a controladores que requieren autorización me llevaban a la página de inicio de sesión, cuando en realidad en web.config tengo configurada la autenticación Windows.

Después de investigar algo he visto un artículo en Stackoverflow que me ha ayudado a resolver el problema. Se trata de deshabilitar SimpleMembership de la aplicación. Esto se consigue añadiendo esta entrada en la clave <appSettings> de web.config.


Por cierto, un artículo sobre cómo configurar SimpleMembership: http://www.mono-software.com/blog/post/Mono/226/Adding-ASP-NET-SimpleMembership-to-an-existing-MVC-4-application/