Performancing Metrics

Return(GiS);

Return(GiS);

Aplicaciones imprescindibles en Windows Phone 7

Después de probar varias aplicaciones disponibles actualmente en el marketplace de Windows Phone 7, me gustaría compartir aquellas que creo que son imprescindibles en nuestros dispositivos :)  

Adobe Reader: Una de las aplicaciones que no necesita presentación. Gracias a ella podremos disfrutar de nuestros archivos en formato PDF.
Facebook: Aplicación oficial desarrollada por Microsoft Corporation. En una versión anterior presentaba bastantes errores de refresco y en ocasiones se quedaba colgada. Recientemente ha mejorado de forma considerable. Si bien una de las cosas que admiro de Windows Phone 7 es su gran integración con Facebook (permite escribir en muros, imágenes, perfil etcétera desde el propio sistema operativo), si queremos una aplicación exclusiva para esta red social, la versión oficial es altamente recomendable.
Foursquare: Aplicación oficial de foursquare. Para todos aquellos amantes del check in :D
4th & Mayor: Otro cliente para Foursquare, del cual dicen que será el sucesor de la versión oficial. Si bien a día de hoy la versión oficial sólo soporta check-in, con este cliente podremos además la crear y visualizar fotos de los lugares, disfrutar de nuevas features como “explore”, mayor rápidez en la localización de sitios próximos, entre otras.
Flory: Se trata de una aplicación dedicada a la mensajería instantánea. Actualmente nos da soporte para Google Talk y Facebook, aprovechando las notificaciones Push y Tile. Todos los mensajes son securizados y encriptados a 256 bits.
Messenger by Miyowa: Si bien la versión Mango (Windows Phone 7.5)  incorporará Windows Live Messenger, mientras tanto podemos ir abriendo boca con este cliente con notificaciones Tile.
Beez: Cliente para la famosa red social Twitter con notificaciones Tile. Una de las características que más me motivan al utilizar un cliente para esta red social es poder recibir notificaciones de menciones y mensajes privados que recibo a través de la misma. Esta es la aplicación que más se acerca a mis necesidades, aunque las notificaciones llegan en ocasiones con un retardo considerable :(
Twitter: Aplicación oficial para Twitter. Encantada con su interfaz gráfica donde podemos ver claramente toda la información de la plataforma. A diferencia de la anterior no aprovecha las notificaciones Tile… Por el momento espero :)
Birdsong: Otro cliente de Twitter con gran acogida que nos ofrece nuevas características: configuración del timeline con listas, búsquedas guardadas, etcétera, integración de las imágenes twitteadas, tweets mostrados en modo conversación, thread de mensajes privados, integración con Places, etcétera.
Rowi: Cliente de pago para Twitter que nos ofrece notificaciones push y una interfaz limpia. Si bien dispone de una versión free con el inconveniente publicitario :) por $2,99 podemos disfrutar la misma libre de ads.
Shazam: Aplicación conocida por todos aquellos clientes de iPhone, iPad, Android, BlackBerry y Nokia que nos permite detectar información sobre la canción que está sonando.
TextMe: Si bien soy una gran fan de WhatsApp, por el momento no tenemos una versión disponible para Windows Phone 7. Para seguir ahorrando en nuestros SMS, MMS, etcétera podemos hacer uso de TextMe (también disponible para iPhone) el cual nos aporta una función similar al mencionado anteriormente.
Microsoft Tag: Se trata de un lector de tags made in Microsoft :) con el que podremos descifrar y leer el contenido de los mismos.
Amazon Kindle: Otra de las aplicaciones que no podía faltar era el cliente de Amazon para la lectura de nuestros libros comprados en la gran tienda virtual de esta compañía.
WordPress: Gestor de nuestros blogs gestionados con WordPress. Nos permite tanto postear, administrar páginas, comentarios, etcétera.
Ebay: Excelente cliente para la conocida página de subastas donde podremos buscar, seguir, pujar, pagar e incluso enviar mensajes a los vendedores.
AppFinder: Aplicación complementaria para localizar contenido dentro del marketplace de Windows Phone 7. Actualmente el buscador por defecto del sistema operativo no está lo suficientemente optimizado y a veces se complica la localización de algunas aplicaciones.
NextGen Reader: Lector de RSS que nos permite sincronizar con nuestros feeds de Google Reader. Además nos ofrece modo offline, compartir artículos en Twitter, enviar feeds a Instapaper, entre otros.
Fruit Ninja: Juego ya conocido por los usuarios de apple donde debemos cortar todas aquellas frutas que aparezcan en pantalla como un autentico ninja :D ¡Cuidado! ¡Super adictivo! ;)
 
¿Alguien da más? :D 
 
¡Saludos!  
Posted: 7/6/2011 19:35 por Gisela | con 3 comment(s)
Archivado en:
Subir un proyecto existente a Windows Azure

Una de las preguntas que me llegan a través del formulario de contacto es qué es lo necesario para subir una aplicación que ya tenemos implementada, en este caso con Web Forms, a la plataforma Windows Azure. En realidad, un proyecto para la nube contiene todos aquellos proyectos que componen nuestra aplicación y además un proyecto relacionado con la plataforma en el cual se define qué rol tiene el resto de proyectos de nuestra solución.

Cuando creamos una solución de tipo Cloud una de las ventanas que nos aparece es la siguiente:
 

En ella podemos seleccionar qué tipo de aplicaciones queremos implementar, desde aplicaciones web a aplicaciones en segundo plano. Sin embargo, uno de los escenarios más comunes es cuando no tenemos nada que implementar sino que únicamente queremos acondicionar su subida a la nube. En ese caso, no seleccionamos ninguno de los roles ofrecidos y pulsamos directamente en el botón OK. De esta forma, estamos pidiendo a Visual Studio 2010 crear una solución con un proyecto del tipo cloud sin ningún rol asociado.
 

Si en este momento tratáramos de ejecutar el proyecto, pulsando F5, Visual Studio nos devolvería el siguiente error:
 

En él se nos está avisando de que actualmente no hay ningún rol asociado al proyecto y que al menos es requerido uno del tipo web, worker o virtual machine. Llegado a este punto, debemos añadir  aquel proyecto(s) a la solución que queramos subir a Windows Azure Platform desempeñando uno de los roles.
Al igual que en cualquier solución convencional, copiamos/cortamos el proyecto que queramos añadir a la solución
 

y lo pegamos dentro de la carpeta del proyecto cloud.
 

Para que este aparezca dentro de la solución, hacemos clic con el botón derecho a nivel de solución y seleccionamos Add => Existing project…
 

y localizamos el archivo del proyecto que acabamos de mover dentro de la solución cloud.
 

Si bien este ya aparece dentro de la ventana Solution Explorer, todavía no forma parte del servicio que será desplegado en la nube. Para finalizar la configuración, debemos hacer clic con el botón derecho sobre la carpeta Roles y seleccionar Add => Web Role Project in solution…
 

Acto seguido nos aparecerá un cuadro de diálogo donde podremos seleccionar aquellos proyectos que encajen dentro de cualquiera de los roles existentes a día de hoy en la plataforma de Windows Azure.
 

Por otro lado habrá proyectos, por ejemplo de tipo Class Library, que no serán necesarios subirlos a la nube como tal sino únicamente las dlls resultantes del mismo.
Como nota adicional, es importante que nos aseguremos de que todas las librerías utilizadas en los proyectos ya existentes fuera de la plataforma y que queramos desplegar estén disponibles en la nube. Como solución podemos modificar las propiedades de las mismas como Copy Local = true o bien utilizar Startups.
 
Espero que sea de utilidad :D
 
¡Saludos! 
Posted: 7/6/2011 19:25 por Gisela | con no comments
Archivado en:
Windows Azure Startups

​Una de las cosas que más me gustan de la plataforma Windows Azure es que nuestras soluciones se convierten en un paquete capaz de acondicionar su entorno de vida :) Todos aquellos pasos previos, como la instalación de librerías, configuración de puertos en el firewall, etcétera, podemos definirlos gracias a la sección Startup del archivo ServiceDefinition.csdef

Esta sección nos permite ejecutar cualquier tipo de acción desatendida que sea necesaria para cada una de las instancias de nuestro rol. Para verlo con un ejemplo, voy a mostrar cómo sería posible la instalación de las librerías de ASP.NET MVC 3, ya que, a día de hoy, estas no están disponibles por defecto en las imágenes que utilizan las instancias de Windows Azure.
 
En primer lugar, creamos un projecto de tipo cloud sin seleccionar ningún rol. Para poder añadir un proyecto del tipo ASP.NET MVC 3, seleccionamos con el botón derecho sobre la solución y hacemos clic sobre Add -> New Project…
 

 

por último seleccionamos la plantilla ASP.NET MVC 3 Web Application (En mi caso la plantilla que he utilizado fue Internet Application) y esperamos a que la misma se genere.

 

 

Si llegados a este punto pulsamos F5 para ejecutar la aplicación, Visual Studio nos informará del siguiente error: 

 

 

El motivo es que, si bien hemos añadido una aplicación web a nuestro proyecto, la misma no está asociada con el proyecto cloud. Para ello, nos posicionamos en la carpeta Roles y, haciendo clic con el botón derecho sobre ella, seleccionamos Add -> Web Role Project in solution… 
 

Pulsamos F5 para comprobar que en local todo funciona correctamente :)
Una vez que tenemos nuestro proyecto listo, descargamos el ejecutable para la instalación llamado AspNetMVC3ToolsUpdateSetup.exe desde el siguiente enlace.

 

Este archivo debemos incluirlo dentro del proyecto y modificar las propiedades del mismo para que sea copiado en el paquete que posteriormente subiremos a la nube.
 

 
¡Perfecto! Ya tenemos nuestra solución con una aplicación ASP.NET MVC 3 y el ejecutable que instalará las librerías necesarias para que el sitio web funcione en Windows Azure. Como último paso, debemos definir el apartado Startup y definir una tarea donde indiquemos que es necesario instalar ASP.NET MVC 3 antes de que la aplicación esté disponible para los usuarios. Para ello, abrimos el archivo ServiceDefinition.csdef donde añadimos la sección Startup con los siguientes valores: 

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="Startups" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WebRole name="MyASPNETMVC3">
    <Startup >
      <Task commandLine ="installaspnetmvc3.cmd" executionContext="elevated" taskType="simple"></Task>
    </Startup>
    <Sites>
      <Site name="Web">
        <Bindings>
          <Binding name="Endpoint1" endpointName="Endpoint1" />
        </Bindings>
      </Site>
    </Sites>
    <Endpoints>
      <InputEndpoint name="Endpoint1" protocol="http" port="80" />
    </Endpoints>
    <Imports>
      <Import moduleName="Diagnostics" />
    </Imports>
  </WebRole>
</ServiceDefinition>
Dentro del apartado Startup podemos tener tantas tareas como sean necesarias con los siguiente parámetros: 
  • commandLine: Recibe como valor el nombre del programa o script que queremos ejecutar. En este caso vamos a invocar a un archivo llamado installaspnetmvc3.cmd, el cual situaremos en la raiz del proyecto web.


    En él vamos a incluir la llamada al archivo AspNetMVC3ToolsUpdateSetup.exe con la opción /q para indicar que debe ser una instalación “silenciosa” o desatendida ;)
  • E:\approot\files\AspNetMVC3ToolsUpdateSetup.exe /q
    Nota: Existe un problema a la hora de generar scripts desde Visual Studio, el cual añade un byte para marcar todos los ficheros por defecto. Para evitar incidentes, recomiendo crear estos archivos con Notepad o modificar las opciones de guardado en File -> Advanced Save Options… donde deberemos seleccionar Unicode (UTF-8 without signature) – Codepage 650001 encoding.
  • executionContext: Nos permite indicar el nivel de permisos que queremos que tenga la tarea (limited o elevated)
  • taskType: Dependiendo de la implicación que tenga nuestra tarea en relación con el rol, podemos elegir entre:
    • simple: Se trata del tipo de tarea síncrona, la cual paraliza cualquier proceso hasta que la misma termina. Si no especificamos ningún tipo, esta es la opción por defecto.
    • background: Nuestra tarea se ejecutará a la par que otras (la ejecución de nuestro rol por ejemplo).
    • foreground: A diferencia del tipo anterior, cuando especificamos una tarea foreground esta se ejecutará de manera asíncrona como la anterior. La peculiaridad consiste en que el rol no podrá detenerse hasta que dicha tarea haya finalizado. 

 Si desplegamos nuestra aplicación en Windows Azure, podremos disfrutar de ASP.NET MVC 3 gracias a Startup section :D

Espero que haya sido de utilidad :)
¡Saludos!  
Posted: 7/6/2011 18:56 por Gisela | con no comments
Archivado en:
Zune: No installation media. Can’t find the media for installation package Windows Media Format SDK
 

 

Ahora que he vuelto de nuevo a mi Windows Phone 7, necesitaba instalar Zune para poder sincronizar todo mi stuff. Una vez descargado el paquete de instalación de la página oficial de Zune, me encuentro con el siguiente error:

 

Despues de pasarme un buen rato navegando, descubro que el problema es mi versión del sistema operativo: Windows 7 Ultimate N
A día de hoy existen distintos tipos de ediciones:
  • Windows 7: Esta edición sería la “estándar” donde incluye todo el repertorio típico de los sistemas operativos Windows para aquellos clientes que residen en países pertenecientes al Espacio Económico Europeo, Croacia y Suiza.
  • Windows 7 K: Sería la edición estándar, al igual que la anterior, pero para Corea.
  • Windows 7 N: Con esta edición podemos elegir nuestro propio reproductor multimedia y el software que queramos utilizar para administrar y reproducir CDs, DVDs, etcétera, es decir, no incluye Windows Media Player 12 por defecto como las dos versiones anteriores. Esta edición está pensada para los clientes del Espacio Económico Europeo, Croacia y Suiza al igual que la edición Windows 7.
  • Windows 7 KN: Exactamente igual que la versión anterior pero para Corea.
El problema reside en que es necesario tener instalado Windows Media Player 12 para poder instalar Zune en el sistema :( . Tanto si tenéis la versión N como la KN y aún así quereis tener Zune en vuestro equipo, es necesario descargar e instalar Media Feature Pack for Windows 7 N with Service Pack 1 and Windows 7KN with Service Pack 1 (KB968211)
Para más información sobre las ediciones N puedes consultar esta página.
Espero que sea de utilidad :D

¡Saludos!
Posted: 17/5/2011 13:17 por Gisela | con no comments
Archivado en: ,
Windows Azure Accelerator for Umbraco

 

 

Meses atrás explicaba en un artículo que escribí para MSDN España cómo era posible subir el CMS de Umbraco a Windows Azure. Si bien vimos que era posible, existían diversas dificultades que hacían de este gestor un funcionamiento algo desorientado a las aplicaciones escalables. El mayor problema era que el propio CMS almacenaba parte del contenido del sitio web en local, por lo que hacía imposible aumentar el número de frontales/instancias sin tocar el código fuente de Umbraco :( .

 

Después de la presentación en el MIX 2011 ya disponemos de un nuevo acelerador, con el que conseguimos desplegar la última versión del CMS adaptada de la siguiente manera:

 

 
Utilizando este esquema lo que se consigue es disponer de múltiples instancias de nuestro sitio web sincronizadas en todo momento (cada segundo) a través de un método llamado SyncForever dentro de la clase WebRole, típica  de los proyectos de Windows Azure como punto de entrada.

 

public override void Run()
{
    this.SyncForever(TimeSpan.FromSeconds(1));
}
Para descargar esta herramienta se puede hacer desde su sitio en codeplex.

 

 

¡Saludos!

 

Posted: 17/5/2011 13:05 por Gisela | con no comments
Archivado en: ,
Windows Azure Diagnostics (SDK 1.4)
En este post vamos a pararnos un poco más en profundidad en cada una de las fuentes de diagnóstico disponible a través de Microsoft.WindowsAzure.Diagnostics.dll.
Un punto importante a la hora de utilizar el sistema de diagnósticos de Windows Azure es que este trabaja conjuntamente con Windows Azure Storage, donde vamos a almacenar toda la información que cada una de las fuentes disponibles nos facilite.
Antes de centrarnos en la configuración, veamos qué podemos recuperar de nuestras instancias y dónde se almacena:
 
Fuente Descripción Destino
Windows Azure Logs En estos logs se guardan todas aquellas trazas registradas a través de la clase System.Diagnostics.Trace. Tabla
Performance Counters Se almacenan los contadores de rendimiento configurados previamente. Tabla
Windows Azure Diagnostic Infrastructure Logs Son todos aquellos registros relacionados con el servicio de Windows Azure. Tabla
Windows Event Logs Podemos añadir tanto fuentes predeterminadas de Windows, como System o Application, como aquellas dadas de alta programáticamente. Tabla
Crash Dumps Volcados de memoria de nuestra aplicación. Blob
Custom Error Logs Esta opción nos permite dar de alta nuestros propios logs dentro del proceso de transferencia de Windows Azure Diagnostics para poder recuperar los mismos a través del storage. Blob
IIS 7.0 Logs Podemos recuperar toda la información generada de forma automática relacionada con IIS Blob
Failed Request Logs Almacena información sobre aquellas peticiones fallidas de IIS Blob

Si bien cada una de las fuentes tiene su propia configuración, lo primero que debemos recuperar es la configuración inicial por defecto de los diagnósticos de la siguiente manera:
//We need to get default initial configuration
var config = DiagnosticMonitor.GetDefaultInitialConfiguration();
Generalmente esta configuración se recupera y se modifica en el archivo WebRole.cs o WorkerRole.cs en el método OnStart ya que es el punto de entrada de nuestra aplicación y se ejecuta cuando nuestras instancias se están iniciando.
En los siguientes apartados utilizaremos el objeto config para configurar cada una de las fuentes.

Windows Azure Logs

Esta primera fuente lo que nos va a permitir es almacenar en una tabla, generada de forma automática por el sistema de diagnósticos llamada WADLogsTable, todas aquellas líneas de registro que realicemos en nuestro código a través de System.Diagnostics.Trace, como por ejemplo:
Trace.WriteLine("WorkerRole_Diag entry point called", "Information");
Para poder recuperar estas trazas, debemos añadir el siguiente código:
//Windows Azure Logs. Table: WADLogsTable
config.Logs.ScheduledTransferLogLevelFilter = LogLevel.Verbose;
config.Logs.ScheduledTransferPeriod = TimeSpan.FromMinutes(5);

Performance counters

Una de las fuentes más importantes para poder conocer el rendimiento de nuestros sistemas trata de los contadores de rendimiento. Para poder conocer cuál sería el nombre del contador que queremos añadir, podemos localizarlos en nuestro propio sistema:
 
 
Una vez elegidos los que queramos auditar, los agregamos al sistema de diagnóstico de la siguiente manera:
//Performance counters. Table: WADPerformanceCountersTable
 config.PerformanceCounters.DataSources.Add(
    new PerformanceCounterConfiguration
    {
        CounterSpecifier = @"\Processor(*)\*",
        SampleRate = TimeSpan.FromSeconds(1)
    }
 );

 config.PerformanceCounters.DataSources.Add(
    new PerformanceCounterConfiguration
    {
        CounterSpecifier = @"\Memory\*",
        SampleRate = TimeSpan.FromSeconds(1)
    }
 );

config.PerformanceCounters.ScheduledTransferPeriod = TimeSpan.FromMinutes(5);

Windows Azure Diagnostic Infrastructure Log

Cuando trabajamos en local con development fabric podemos visualizar una consola por cada instancia que tengamos de nuestros roles donde nos facilita información de su estado.
 
 
Para poder recuperar esta información utilizaremos DiagnosticInfrastructureLogs para recuperar esta información en forma de tabla, la cual recibirá el nombre WADDiagnosticInfrastructureLogsTable:
//Windows Azure Diagnostic Infrastructure Logs. Table: WADDiagnosticInfrastructureLogsTable
config.DiagnosticInfrastructureLogs.ScheduledTransferLogLevelFilter = LogLevel.Verbose;
config.DiagnosticInfrastructureLogs.ScheduledTransferPeriod = TimeSpan.FromMinutes(5);

Windows Event Logs

El visor de eventos de Windows creo que es algo que no necesita presentación :D . También podemos recuperar esta información a excepción de la fuente Security. Estos datos se nos presentarán a través de la tabla WADWindowsEventLogsTable.
//Windows Event Logs. Table: WADWindowsEventLogsTable
config.WindowsEventLog.ScheduledTransferLogLevelFilter = LogLevel.Verbose;
config.WindowsEventLog.DataSources.Add("Application!*");
config.WindowsEventLog.DataSources.Add("System!*");
config.WindowsEventLog.ScheduledTransferPeriod = TimeSpan.FromMinutes(5);

Crash Dumps

Uno de los clásicos no podía faltar :) Todo lo relacionado con los volcados de memoria se puede conseguir utilizando estas líneas:
//Crash Dumps. Blob. Container: wad-crash-dumps
CrashDumps.EnableCollection(true);
config.Directories.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);
En este caso debemos tener en cuenta dos pasos: CrashDumps.EnableCollection nos permite habilitar la recolección tanto de mini dumps (parámetro a false) o full dumps (parámetro a true). Si sólo indicamos esta línea lo que vamos a conseguir es el almacenamiento local de estos volcados de memoria, pero no serán transferidos a nuestro storage. Para conseguir este segundo paso debemos especificar cada cuánto tiempo vamos a realizar la transferencia de los directorios de diagnóstico a través de config.Directories.ScheduledTransferPeriod como se indica en la segunda línea del código anterior.

Custom Error Logs

Imaginemos que ya tenemos un sistema de diagnóstico de terceros que no deseamos modificar, pero aun así queremos recuperar los ficheros generados de todas las instancias para poder procesarlos. En este caso podemos crear nuevos directorios de diagnóstico, lo cual nos va a permitir que los mismos sean transferidos cada cierto tiempo a los blobs de nuestra cuenta de storage:
//Custom Error Logs
var localResource = RoleEnvironment.GetLocalResource("CustomErrorStorage");
var directoryConfiguration = new DirectoryConfiguration
{
    Container = "wad-mycustom-log-container",
    DirectoryQuotaInMB = localResource.MaximumSizeInMegabytes,
    Path = localResource.RootPath
};

config.Directories.DataSources.Add(directoryConfiguration);
config.Directories.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);

IIS Logs

En el caso de los logs de IIS basta con configurar el tiempo de transferencia del apartado Directories, ya que se genera una carpeta donde almacena de forma automática la información:
config.Directories.ScheduledTransferPeriod = TimeSpan.FromMinutes(5);

Failed Request Logs

Esta fuente está relacionada también con Internet Information Services tras la cual podemos recuperar todas aquellas peticiones fallidas. A excepción del resto, para poder configurar la misma debemos modificar el archivo web.config de nuestro web role para añadir la sección tracing dentro de system.webServer:
<system.webServer>
   <!-- Blob. Container: wad-iis-failedreqlogfiles -->
     <tracing>
       <traceFailedRequests>
          <add path="*">
             <traceAreas>
                <add provider="ASP" verbosity="Verbose" />
                <add provider="ASPNET"
                     areas="Infrastructure,Module,Page,AppServices"
                     verbosity="Verbose" />
                <add provider="ISAPI Extension" verbosity="Verbose" />
                <add provider="WWW Server"
                areas="Authentication,Security,Filter,StaticFile,CGI,Compression,Cache,RequestNotifications,Module"
                verbosity="Verbose" />
            </traceAreas>
            <failureDefinitions timeTaken="00:00:15" statusCodes="400-599" />
          </add>
        </traceFailedRequests>
      </tracing>
 <modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
Al generar esta información dentro de un archivo, necesitamos indicar el tiempo de transferencia de los directorios de diagnóstico como en los casos anteriores:
config.Directories.ScheduledTransferPeriod = TimeSpan.FromMinutes(5);
Nota: Todas aquellas fuentes que se almacenan en blobs generan una carpeta dentro del directorio DiagnosticStore para almacenar los archivos con la información. Para poder ser transferidos necesitamos indicar cada cuánto tiempo queremos que se envíe la información a Windows Azure Storage. Este tiempo de transferencia basta con definirlo una vez para todas las fuentes que usen este sistema.
Para que todos estos cambios tengan efecto, debemos iniciar la nueva configuración tomando como parámetros el nombre de la cadena de conexión que utilizará Windows Azure Diagnostics (por defecto Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString) y el objeto config que hemos personalizado:
//Start with new configuration
DiagnosticMonitor.Start("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString", config);
Por último, como recomendación, existe una aplicación de Cerebrata llamada Azure Diagnostics Manager la cual nos va a facilitar mucho la interpretación de toda esta información de una forma muy visual y organizada.
Adjunto un proyecto de ejemplo donde podemos probar cada uno de los casos ;)
 
Espero que sea de utilidad :D
 
¡Saludos!

 

Posted: 24/4/2011 17:32 por Gisela | con 2 comment(s)
Archivado en:
Cambiar la edición y el tamaño de nuestras bases de datos en SQL Azure

 

 
Cuando decidimos contratar SQL Azure como servidor de nuestras bases de datos relacionales lo primero que debemos estimar es el tamaño de nuestra base de datos. En la actualidad existen diferentes tamaños disponibles, agrupadas en dos ediciones:
  • Web edition
    • 1 GB
    • 5 GB
  • Business edition
    • 10 GB
    • 20 GB
    • 30 GB
    • 40 GB
    • 50 GB
Cuando queremos dar de alta una base de datos de cualquiera de estos tamaños podemos hacerlo bien a través del portal de Windows Azure:

 

a través de SQL Server 2008 R2 Management Studio:

 
 o bien utilizando directamente la siguiente sentencia SQL:

 

Create DATABASE mybbdd (EDITION='web',MAXSIZE=1GB)

 

Lo más lógico,  cuando utilizamos un modelo Pay as you go, es que vayamos adaptando el tamaño de nuestras base de datos cuando nuestro negocio lo requiera. A día de hoy el portal no nos ofrece una forma visual para modificar las características de las bases de datos existentes, por lo que es necesario modificar las mismas utilizando sentencias SQL. Cuando realizamos una modificación esta puede ser a nivel de edición y/o de tamaño:
ALTER DATABASE mybbdd MODIFY (EDITION='business', MAXSIZE=50GB)
 
ALTER DATABASE mybbdd MODIFY (EDITION='web', MAXSIZE=5GB)
Nota: En realidad, indicando sólo el tamaño es suficiente, ya que la edición será modificada automáticamente de web a business o viceversa si la situación lo requiere.
 
ALTER DATABASE mybbdd MODIFY (MAXSIZE=20GB)

 

Más información

 

Espero que sea de utilidad :D
¡Saludos!
Posted: 3/4/2011 17:55 por Gisela | con no comments
Archivado en:
Instalar Windows Azure Service Management CmdLets en Windows 7 SP 1 con Windows Azure SDK 1.4
Después de la nueva versión del SDK de Windows Azure y el Service Pack 1 de Windows 7, podemos encontrarnos ciertas trabas si intentamos instalar Windows Azure Service Management CmdLets:

 

 
Esto puede ocurrir principalmente por varios motivos:
  • Tenemos instalado el service pack 1 para Windows 7.
  • Hemos actualizado a Windows Azure SDK 1.4
  • Ambos :D
 Al no tener las nuevas versiones contempladas tanto en uno de los archivos de configuración como en uno de los script de powershell no puede reconocer las nuevas versiones tanto de compilación del sistema operativo como la del SDK. Para solucionarlo, podemos realizar los siguientes pasos:

Añadir el builder number de Windows 7 con sp 1

 
Dentro del directorio de WASM nos ubicamos en WASMCmdlets\setup. Dentro de la misma tenemos un archivo llamado Dependencies.dep el cual lo abrimos con el Notepad para añadir el nuevo build number.
 
 
En este archivo podemos ver y modificar todas las dependencias necesarias para la instalación e incluso modificar la descripción y la url donde de donde podemos descargar la dependencia requerida. Centrándonos en el sistema operativo, como vemos en la imagen anterior, vemos que aparece un atributo llamado buildNumber el cual contiene todas las numeraciones de las compilaciones válidas. 7600 se corresponde con Windows 7 sin service pack. ¿Cómo sabemos cuál es la versión de compilación del sistema operativo? La forma más sencilla de llegar a este valor es abriendo una aplicación propietaria de Microsoft integrada en el sistema operativo que tenemos instalado. Por ejemplo, abrimos la aplicación Paint y pulsamos sobre About Paint/Sobre Paint.
 
 
Voilà! En este caso podemos confirmar que 7601 se corresponde con Windows 7 SP 1:D
El siguiente paso se trata de modificar uno de los powershell donde se verifica la versión del SDK de Windows Azure dentro del registro. En la versión anterior, el número anterior se correspondía con la 1.3.11122.0038.
 
 
Para que la comprobación se realice con éxito, necesitamos cambiar el valor de SearchVersion a 1.4.20227.1419 como se muestra en la imagen.
 
 
¡Listo! ya tenemos adaptadas todas las comprobaciones para lanzar de nuevo la instalación.
 
 
Espero que sea de utilidad :D
¡Saludos!
Posted: 3/4/2011 17:38 por Gisela | con 2 comment(s)
Archivado en:
Refrescar un WebGrid cada X tiempo de forma asíncrona
Con la nueva versión ASP.NET MVC 3, disponemos de una nueva extensión para la creación de grids. Para conocer más detalles sobre este nuevo helper podéis consultar el artículo de Jose M. Aguilar :)
En este post me gustaría mostraros cómo es posible actualizar de forma asíncrona un grid cada un número determinado de segundos.

Del lado del servidor he adjuntado una base de datos con una única tabla para el ejemplo que os quiero mostrar:
 


Un repositorio para recuperar los valores:

using System.Linq;

namespace WebGridAsync.Models
{
    public class StuffRepository
    {
        private readonly StuffEntities _model;
        public StuffRepository()
        {
            _model = new StuffEntities();
        }

        public FileViewModel GetFiles(int page = 1)
        {
            var fileViewModel = new FileViewModel();
            var files = _model.Files.ToList();
            fileViewModel.FilesPerPages = 5;
            fileViewModel.NumberOfFiles = _model.Files.Count();
            fileViewModel.Files = files.Skip((page - 1) * fileViewModel.FilesPerPages)
                                       .Take(fileViewModel.FilesPerPages);
            return fileViewModel;
        }

        public void SaveChanges()
        {
            _model.SaveChanges();
        }
    }
}
Y por último un controlador donde lo que vamos a actualizar es el estado de los archivos en cada llamada:
using System.Web.Mvc;
using WebGridAsync.Models;

namespace WebGridAsync.Controllers
{
    public class HomeController : Controller
    {
        private readonly StuffRepository _stuffRepository;
        public HomeController()
        {
            _stuffRepository = new StuffRepository();
        }

        public ActionResult Index(int page = 1)
        {
            return View(GetFilesWithUpdatedStatus());
        }

        public FileViewModel GetFilesWithUpdatedStatus()
        {
            var fileViewModel = _stuffRepository.GetFiles();

            foreach (var myFile in fileViewModel.Files)
            {
                switch (myFile.Status)
                {
                    case "Pending":
                        myFile.Status = "Processing";
                        break;
                    case "Processing":
                        myFile.Status = "Waiting";
                        break;
                    case "Waiting":
                        myFile.Status = "More waiting...";
                        break;
                    case "More waiting...":
                        myFile.Status = "Completed";
                        break;
                    default:
                        myFile.Status = "Pending";
                        break;
                }
            }

            _stuffRepository.SaveChanges();
            return fileViewModel;
        }
    }
}
Por último, necesitamos crear una vista donde hagamos uso del helper WebGrid. Es importante que el mismo esté incluido en un div con un id asociado.
@model WebGridAsync.Models.FileViewModel
@{
    ViewBag.Title = "Async WebGrid";

    WebGrid grid = new WebGrid(rowsPerPage: Model.FilesPerPages, canSort: false);
    grid.Bind(Model.Files, rowCount: Model.NumberOfFiles);
}
<h2>@ViewBag.Message</h2>
<div id="myWebGrid">
    @grid.GetHtml(columns: new[]{
    grid.Column("Name"),
    grid.Column("Date"),
    grid.Column("Status")
})
</div>
Para hacer la llamada asíncrona, necesitamos utilizar algo de código javascript, con la ayuda de JQuery. En la misma página que el grid anterior incluimos lo siguiente:
<script language="javascript" type="text/javascript">

    $(document).ready(function () {
        $.ajaxSetup({
            cache: false
        });

        setTimeout(reloadAsync, 5000);
    });

    function reloadAsync() {
        $("#myWebGrid").load("/Home/Index #myWebGrid", function (response, status, xhr) {
            if (status == "error") {
                var msg = "Sorry but there was an error: ";
                $("#error").html(msg + xhr.status + " " + xhr.statusText);
            }
            else {
                setTimeout(reloadAsync, 5000);
            }

        });

    }
</script>
Lo que estamos haciendo en realidad es inicializar un timer para que cada 5 segundos solicite los datos al servidor. El método load de JQuery va a realizar una llamada a la acción Index del controlador Home para que este le devuelva la página renderizada. Una vez obtiene el resultado va a localizar el elemento div myWebGrid para reemplazar el que se está visualizando actualmente por el nuevo resultado.
Adjunto el proyecto por si fuera de utilidad :D
 
¡Saludos!
Posted: 3/4/2011 0:37 por Gisela | con 2 comment(s)
Archivado en:
Configurar el proxy en Visual Studio 2010

Cuando trabajamos detrás de un proxy podemos experimentar algunos problemas con ciertas aplicaciones. Incluso Visual Studio puede verse perjudicado por ello :(

Para solucionarlo, podemos modificar el archivo de configuración de Visual Studio para especificar el uso del proxy por defecto.

En primer lugar cerramos todas las instancias de Visual Studio. Hacemos clic con el botón derecho sobre el acceso directo Microsoft Visual Studio 2010 para localizar la ubicación del ejecutable devenv.exe.


De todos los archivos que encontramos en el directorio, el que realmente nos interesa es devenv.exe.config que corresponde con el archivo de configuración del IDE.
Abrimos el mismo con Visual Studio y añadimos las siguientes líneas dentro de la sección <system.net>.
<defaultProxy useDefaultCredentials="true" enabled="true">
 <proxy usesystemdefault="True" />
</defaultProxy>

De tal manera que el archivo de configuración quede con el siguiente aspecto:

 

 

 
Si arrancamos de nuevo Visual Studio, ya podemos ejecutar aquellas acciones que requieran de autenticación en el proxy :D
Espero que sea de utilidad :)

¡Saludos!

 

Posted: 26/3/2011 15:25 por Gisela | con no comments
Archivado en:
Fast CGI Web Role y Windows Azure SDK 1.3
En esta última versión del SDK hemos podido encontrar ciertos problemas con las aplicaciones de tipo Fast CGI, como por ejemplo esto:
 

 

Este error es debido a que, al crear una nueva aplicación con la nueva plantilla proporcionada por el SDK e intentar configurarla para Full IIS, no es capaz de encontrar el runtime de php y por lo tanto lanza una excepción, lo cual significa que por el momento las aplicaciones de tipo Fast CGI no soportan Full IIS :(

Para solventar este escenario únicamente debemos comentar el elemento Sites del archivo ServiceDefinition.csdef

 

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="PHPWindowsAzure" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WebRole name="WebCgiRole_PHP" enableNativeCodeExecution="true">
    <!--<Sites>
      <Site name="Web">
        <Bindings>
          <Binding name="Endpoint1" endpointName="Endpoint1" />
        </Bindings>
      </Site>
    </Sites>-->
    <Endpoints>
      <InputEndpoint name="Endpoint1" protocol="http" port="80" />
    </Endpoints>
    <Imports>
      <Import moduleName="Diagnostics" />
    </Imports>
  </WebRole>
</ServiceDefinition>

Si ejecutamos de nuevo la aplicación, la misma arranca sin problemas :)

¡Saludos!
Posted: 26/3/2011 15:19 por Gisela | con no comments
Archivado en: ,
Bing Maps & Geolocation APIs

 

 
Para continuar con el tema de la georreferenciación, esta vez voy a mostraros cómo es posible posicionarnos en un mapa de Bing Maps, además de recuperar nuestra localización aproximada a través del API Geolocation (No disponible en todos los navegadores, aunque si en las últimas versiones de los más conocidos).

API Geolocation

Antes de posicionarnos en un mapa lo primero que debemos hacer es comprobar que el navegador soporta dicha API.
$(document).ready(function () {

   //Check if geolocation is available
   if (navigator.geolocation) {
       navigator.geolocation.getCurrentPosition(
           function (position) //success
           {
               GetMap(position);
           },
           function (error) //error
           {
               alert(error);
           }
       );
   }
   else
      GetMap(null);

});
Si navigator.geolocation no es undefined, podemos hacer la llamada a getCurrentPosition la cual acepta como parámetros dos funciones de callback: la primera si la llamada se ha realizado con éxito y la segunda en caso de error.

Bing Maps API

En el caso de Bing Maps, para poder utilizar la API es necesario registrarnos en Bing Maps Account Center con un Windows Live ID. Una vez logados, podremos acceder al menú donde seleccionaremos Create or view keys.


Damos de alta una nueva aplicación con el fin de generar una clave que posteriormente utilizaremos en la llamada.
var credentials = "App9aeMkRCpeXATFyZ8....cR9dqc6T4rIMG7XTnd";
var bingMap = null;
Por otro lado, necesitamos añadir un enlace al siguiente script para acceder a las funciones de la API.
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&mkt=es-ES"></script>
Si nos fijamos en la referencia, vemos que al final aparece un parámetro llamado mkt que nos permite indicar la cultura del mapa. A día de hoy existen varias culturas disponibles.
Una vez obtenida la key y añadida la referencia al script de Bings Maps, ya podemos implementar la función GetMap, llamada desde el evento ready del documento.
function GetMap(position) {

    var lat = null;
    var long = null;

    if (position != null) {
        lat = position.coords.latitude;
        long = position.coords.longitude;
    }
    else{
        lat = 47.616023; //sample values (Seattle)
        long = -122.333565;
    }

    bingMap = new Microsoft.Maps.Map($("#mapDiv")[0],
                  { credentials: credentials,
                    center: new Microsoft.Maps.Location(lat, long),
                    mapTypeId: Microsoft.Maps.MapTypeId.road,
                    zoom: 16
                  });

   var center = bingMap.getCenter();
   var pin = new Microsoft.Maps.Pushpin(center, { text: '1' });
   Microsoft.Maps.Pushpin.prototype.title = null;
   pin.title = "Infobox Position";
   Microsoft.Maps.Pushpin.prototype.description = null;
   pin.description = "This is my position lat:" + lat + " long: " + long;

   //Add handler for the pushpin click event.
   Microsoft.Maps.Events.addHandler(pin, 'click', displayEventInfo);
   bingMap.entities.push(pin);
}
Si os dais cuenta, los pasos a seguir son básicamente los mismos que utilizábamos en la  API de Google Maps, a excepción del parámetro para las credenciales. En primer lugar, generamos el mapa, asignando como lienzo un div llamado mapDiv, pasamos la key como credencial, asignamos las coordenadas obtenidas como el centro del mapa, un tipo para el mismo y por último el valor del zoom.
Después creamos un objeto del tipo PushPin para añadir un punto de interés en el mapa con las coordenadas que utilizamos para establecer el centro y añadimos algunos valores como el titulo y la descripción, este último correspondiente al contenido del pop up.
Por último añadimos un handler al evento click, el cual llamará a la función displayEventInfo y adjuntamos el PushPin al mapa.
Para completar el ejemplo, creamos la función displayEventInfo que mostrará el pop up al hacer clic sobre el marker y agregamos los elementos HTML correspondientes al lienzo del mapa y el conjunto de divs que forman la ventana de información.
    function displayEventInfo(e) {
        if (e.targetType == "pushpin") {
            var pix = bingMap.tryLocationToPixel(e.target.getLocation(), Microsoft.Maps.PixelReference.control);
            $("#infoboxTitle").html(e.target.title);
            $("#infoboxDescription").html(e.target.description);

            var infobox = $("#infoBox");
            infobox.css({
                "top": (pix.y - 60) + "px",
                "left": (pix.x + 5) + "px",
                "visibility": "visible"
            });

            $("#mapDiv").append(infobox);
        }
    }   

</script>
<div id='mapDiv'>
</div>
<div id='infoBox'>
    <div id='infoboxText'>
        <b id='infoboxTitle'></b>
        <img id="imgClose" src="/Content/images/close_icon.gif" alt="close" />
        <a id='infoboxDescription'></a>
    </div>
</div>
Si accedemos a la aplicación desde Firefox vemos que aparece el siguiente mensaje de confirmación (No siempre deseamos que una aplicación sepa nuestras coordenadas):


Desde el momento en el compartimos nuestra ubicación, unos segundos después (a mi parecer creo que tarda bastante) aparece nuestro mapa con nuestra ubicación aproximada :D
 

Una sitio muy interesante para probar las funcionalidades que nos ofrece la API es Maps Interative SDK donde nos permite visualizar el resultado y recuperar el código asociado.
Adjunto el proyecto por si fuera de utilidad :)
¡Saludos!

 

Posted: 26/3/2011 15:12 por Gisela | con 2 comment(s)
Archivado en: ,,
Google Maps API v.3.0 y ASP.NET MVC 3 con Razor
 

 

Gracias a todas las APIs que nos facilita Google, podemos hacer uso prácticamente de todos los servicios que ofrece la compañía a día de hoy. En este post en concreto me gustaría hablaros de cómo utilizar el API de Google Maps en su versión 3.0 para generar mapas dinámicos con una enorme cantidad de funciones. Además, para rizar más el rizo utilizaré ASP.NET MVC 3 con Razor para crear una llamada síncrona y otra asíncrona con la ayuda de JQuery.

Referencias

Para utilizar el API de Google Maps son necesarias las siguientes referencias:
<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
Nota: Cuando utilizas Google Maps siempre debe especificarse si vas a utilizar un sensor, por ejemplo un GPS, para determinar la ubicación del usuario (importante para los dispositivos móviles). En este caso estableceremos el valor del parámetro sensor a false ya que no entra este escenario dentro del post ;)

¿Por dónde empiezo?

Lo primero que debemos tener en cuenta es que podemos ubicar marcas de dos formas distintas: Indicando las coordenadas del sitio o recuperando las coordenadas a través de una dirección postal. Sin embargo, en el segundo caso tenemos una limitación por parte de la API, la cual sólo nos permite solicitar unas 10 direcciones por consulta, lo cual limita nuestro campo de acción. En este post utilizaremos coordenadas para poder agregar tantas marcas como sea necesario :D Para ello, he creado un objeto de prueba que almacenará todas las marcas y un repositorio,  también de prueba, que nos devolverá tres objetos dentro de una lista para la demo.

Objeto de prueba

namespace GoogleMapsASPNETMVC3.Models
{
    public class GoogleMarker
    {
        public string SiteName { get; set; }
        public double Latitude { get; set; }
        public double Longitude { get; set; }
        public string InfoWindow { get; set; }
    }
}

Repositorio de prueba

using System.Collections.Generic;

namespace GoogleMapsASPNETMVC3.Models
{
    public class MarkerRepository
    {

        public IList<GoogleMarker> GetMarkers()
        {
            var googleMarkers = new List<GoogleMarker>
                                    {
                                        new GoogleMarker
                                            {
                                                SiteName = "Jardines de Sabatini",
                                                Latitude = 40.421749,
                                                Longitude = -3.713994,
                                                InfoWindow = "InfoWindow de los Jardines de Sabatini"
                                            },
                                        new GoogleMarker
                                            {
                                                SiteName = "Campo del Moro",
                                                Latitude = 40.419658,
                                                Longitude = -3.718801,
                                                InfoWindow = "InfoWindow del Campo del Moro"
                                            },
                                        new GoogleMarker
                                            {
                                                SiteName = "Parque de la Cornisa",
                                                Latitude = 40.413254,
                                                Longitude = -3.716483,
                                                InfoWindow = "InfoWindow del Parque de la Cornisa"
                                            }
                                    };

            return googleMarkers;
        }
    }
}

Llamada síncrona

Para la llamada síncrona lo que vamos a hacer es solicitar la lista de marcas y pasarla a la vista para poder asociarla a la propiedad Model.
[HttpGet]
public ActionResult Sync()
{
     return View(_markerRepository.GetMarkers());
}
 Una vez en la vista agregaremos el siguiente código:
@model IEnumerable<GoogleMapsASPNETMVC3.Models.GoogleMarker>
@using System.Threading;
@using System.Globalization;
@{
    ViewBag.Title = "Sync";
    Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
}
<script language="javascript" type="text/javascript">
    $(document).ready(function () {
    var bounds = new google.maps.LatLngBounds();
    var options = {
        zoom : 14,
        mapTypeId: google.maps.MapTypeId.TERRAIN
    };
    var googleMap = new google.maps.Map($("#map")[0],options);
    var infoWindow = new google.maps.InfoWindow({ content: "Cargando..." });
    @foreach (var marker in Model)
    {
    <text>
        var point = new google.maps.LatLng(@marker.Latitude, @marker.Longitude);
        bounds.extend(point);

        var marker = new google.maps.Marker({
                position: point,
                map: googleMap,
                icon:'/Content/images/cloud_marker.png',
                html: '@marker.InfoWindow'
            });

        google.maps.event.addListener(marker, "click", function () {
                infoWindow.setContent(this.html);
                infoWindow.open(googleMap, this);
            });
    </text>
    }
    googleMap.fitBounds(bounds);
    });
</script>
<h2>
    Sync</h2>
<div id="map" style="width: 800px; height: 500px;"></div>
Lo primero que hacemos en la parte javascript es la inicialización de unas variables bases como son bounds donde vamos a insertar cada uno de los puntos de coordenadas, googleMap que asignará el espacio dedicado al mapa a un div con id “map” situado abajo del todo e infoWindow que nos servirá para asociar un pop up a cada marca del mapa.
Posteriormente utilizamos la @ para inicializar un foreach, ya que estamos utilizando el motor de vistas Razor, y recorremos cada una de las marcas ubicadas en el Model del tipo GoogleMarker. Como vamos a mezclar código javascript con variables de servidor, debemos hacer el uso de <text></text> para que no interprete todo el código dentro del foreach como código de servidor.
Los pasos que vamos a realizar en cada iteración es la creación de cada conjunto de coordenadas en el mapa a través de google.maps.LatLng para acto seguido usar el resultado como la posición de la marca en el objeto google.maps.Marker en el mapa asociado. En este último podemos asociar distintos tipos de opciones como por ejemplo el icono que queremos mostrar para dicha marca, sombreados, etcétera. En este caso he modificado la imagen por una nube :)
Además podemos asociar un evento a cada marca para mostrar el típico pop up que aparece cuando haces clic sobre una de ellas. Para ello utilizaremos google.maps.event.Listener donde crearemos la asociación con la clase InfoWindow instanciada al inicio del script.
Por otro lado, si os fijáis de nuevo en el código anterior, he cambiado la cultura a en-US. Esto es debido a que es necesario respetar el signo de separación de los decimales en las coordenadas ya que, de lo contrario, la latitud y longitud recuperadas de la propiedad Model se pintarían con comas y no se mostraría correctamente.
Como resultado obtendríamos la siguiente imagen :D

Llamada asíncrona

Para la llamada asíncrona el escenario cambia:
@{
    ViewBag.Title = "Async";
}
<script language="javascript" type="text/javascript">
    $(document).ready(function () {

        var bounds = new google.maps.LatLngBounds();
        var options = {
            zoom: 14,
            mapTypeId: google.maps.MapTypeId.TERRAIN
        };
        var googleMap = new google.maps.Map($("#map")[0], options);
        var infoWindow = new google.maps.InfoWindow({ content: "Cargando..." });

        $.ajax({
            type: "POST",
            url: "GetMarkersAsync",
            datatype: "json",
            success: function (data) {
                for (var i = 0; i < data.length; i++) {

                    var point = new google.maps.LatLng(data[i].Latitude, data[i].Longitude);

                    bounds.extend(point);

                    var marker = new google.maps.Marker({
                        position: point,
                        map: googleMap,
                        html: data[i].InfoWindow
                    });

                    google.maps.event.addListener(marker, "click", function () {
                        infoWindow.setContent(this.html);
                        infoWindow.open(googleMap, this);
                    });
                }
            }
        });

        googleMap.fitBounds(bounds);
    });
</script>
<h2>
    Async</h2>
<div id="map" style="width: 800px; height: 500px;">
</div>
En este segundo caso estamos utilizando JQuery para la llamada Ajax a una acción llamada GetMarkersAsync, la cual nos devolverá un listado de objetos en JSON con el mismo formato que nuestro objeto de prueba. Para ello, crearemos una acción HttpPost que retorne dicho listado basado en la clase GoogleMarker.
[HttpPost]
public ActionResult GetMarkersAsync()
{
    return Json(_markerRepository.GetMarkers());
}
En este caso el resultado es el mismo que en la llamada síncrona a excepción de los iconos utilizados en las marcas, ya que en el segundo ejemplo he omitido la propiedad icon para que utilice el icono por defecto de Google.
Adjunto el proyecto por si fuera de utilidad :D
Más información sobre la Google Map API 3.0

 

¡Saludos!​
Posted: 10/2/2011 17:12 por Gisela | con 7 comment(s) |
Archivado en:
Microsoft Partner Network & Microsoft Platform Ready

 

 

 

A finales del año pasado se lanzó Microsoft Partner Network (MPN), el nuevo programa de partners con nuevos niveles y requisitos para empresas, con el objetivo de mejorar la calidad de los socios de Microsoft. Este nuevo concepto será el que releve a Microsoft Partner Program y para seguir aportando esta distinción a la empresa es necesario afrontar nuevos retos :D

Para conseguir la competencia de ISV en el nivel Silver se solicitará una aplicación certificada con uno de los siguientes tests:
  • Windows 7 Platform Ready
  • Windows Server 2008 R2 Platform Ready
  • Windows Azure Platform Ready
  • SQL Azure Platform Ready
  • Microsoft Surface Test
Para ello tenemos a nuestra disposición Microsoft Platform Ready que nos ayudará a realizar estos test en nuestros desarrollos de una manera rápida y sencilla, gracias a los recursos online que se nos ofrecen así como herramientas de testeo disponibles de forma gratuita para el partner. Por otro lado disponemos de un conjunto de utilidades que nos permitirán lanzar nuestra solución al mercado a través de los catálogos de Microsoft.
Date de alta en Microsoft Platform Ready con tu usuario y contraseña del programa de partners, da de alta todas tus aplicaciones, independientemente de la fase en la que estén (desarrollo, pruebas, producción, etc) y aprovecha los recursos de MPR (recursos de desarrollo, realiza test de certificación, material de marketing, etc).
 Si realizas algunos de los test, estos automáticamente volcaran en MPN y podrás asociarlo a la competencia para la que sean válidos.
 A parte de ISVs, hay otras competencias que permitirán la certificación de una aplicación para obtenerla en su nivel Silver. 
  • Application Integration  =  Windows Server 2008 R2 Platform Ready
  • Data Platform  =  SQL Server 2008 R2 Platform Ready
  • Business Intelligent  =  SQL Server 2008 R2 Platform Ready
  • Content Management   =  Sharepoint Server 2010 Platform Ready (proximamente disponible)
  • Unified Communication  =  Unified Communicatios Platform Ready ( aun no disponible)
Espero que esta información os sea de utilidad :)

 

¡Saludos!

 

 
Posted: 10/2/2011 17:08 por Gisela | con no comments
Archivado en:
Razor View Engine para ASP.NET MVC 3

 

 
Una de las novedades con mejor acogida de la nueva versión del frameworkd ASP.NET MVC 3 ha sido sin duda el nuevo motor de vistas llamado Razor. Si bien es cierto que con las primeras versiones y el motor de vistas aspx muchos programadores se echaban para atrás con ASP.NET MVC, con Razor podremos generar vistas de una forma más sencilla y fluida. En este post voy a comentar algunas de las diferencias y mejoras respecto al anterior view engine predeterminado.

 

En primer lugar, Razor pasa a ser el motor de vistas por defecto, aunque tenemos disponibles ambos al crear un nuevo proyecto o una nueva vista.
 

 

Accediendo a variables de servidor

Si recordáis, cuando trabajamos con el aspx view engine, para acceder al contenido de Model, ViewData, helpers, etcétera necesitábamos añadir las siguientes etiquetas: <%: %>
<p>Please enter your username and password. <%: Html.ActionLink("Register", "Register") %>
if you don't have an account.</p>
Con Razor simplemente utilizaremos @ al inicio y el view engine se encargará de determinar hasta dónde debe procesar.
<p>Please enter your username and password. @Html.ActionLink("Register", "Register") if
you don't have an account.</p>

Recorrer una lista

Para recorrer una lista es bastante similar a como lo haríamos con el motor aspx, con la diferencia del etiquetado para comenzar la sentencia foreach.
@foreach (var item in Model) {
    <tr>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
            @Html.ActionLink("Details", "Details", new { id=item.Id }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.Id })
        </td>
        <td>
            @item.FirstName
        </td>
        <td>
            @item.LastName
        </td>
        <td>
            @item.Address
        </td>
        <td>
            @item.E_Mail
        </td>
        <td>
            @item.Mobile
        </td>
    </tr>
}
Si nos fijamos en la llave de cierre del bucle, vemos que en Razor no es necesario etiquetar esa llave :D ya que se supone que pertenece a la llave de apertura abierta anteriormente, caso que no ocurría con aspx view engine:
<% foreach (var item in Model) { %>
    <tr>
        <td>
            <%: Html.ActionLink("Edit", "Edit", new { id=item.Id }) %> |
            <%: Html.ActionLink("Details", "Details", new { id=item.Id }) %> |
            <%: Html.ActionLink("Delete", "Delete", new { id=item.Id }) %>
        </td>
        <td>
            <%: item.FirstName %>
        </td>
        <td>
            <%: item.LastName %>
        </td>
        <td>
            <%: item.Address %>
        </td>
        <td>
            <%: item.E_Mail %>
        </td>
        <td>
            <%: item.Mobile %>
        </td>
    </tr>
<% } %>

Bloques de código

Por otro lado, para utilizar bloques de código bastará con utilizar la arroba y abrir llaves para delimitar el espacio abarcado por el bloque.
@{
    ViewBag.Title = "Loop";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

Tag <text>

Otro escenario que se nos puede presentar es que dentro de un bloque de código necesitemos añadir código javascript o HTML que no requiera ser procesado por el motor.
<p>
    To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">
        http://asp.net/mvc</a>.
</p>
@if (DateTime.Now.Year == 2011)
{
    <text>while year is 2011 this should be shown.</text>
}
Para que nos hagamos una idea más clara, si no añadimos el tag <text>, algunas partes del código anterior sería interpretadas como palabras clave.

Archivo _ViewStart.cshtml

Para conseguir unas vistas más limpias y concisas, aparece un nuevo archivo llamado _ViewStart.cshtml.
 
El objetivo del mismo trata de recopilar código común a todas las vistas de la aplicación, simplificando el código de las mismas. Por defecto aparece definido en él el layout utilizado en las vistas, en caso de no ser especificado en las mismas.
@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

Sections 

Otra de las novedades dentro de Razor es la posibilidad de crear secciones.
<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
</head>
<body>
    <div>
        <div id="header">
            <div id="title">
                <h1>
                    My MVC Application</h1>
            </div>
            <div id="logindisplay">
                @Html.Partial("_LogOnPartial")
            </div>
            <div id="menucontainer">
                <ul id="menu">
                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <li>@Html.ActionLink("About", "About", "Home")</li>
                </ul>
            </div>
        </div>
        <div id="main">
            @RenderBody()
            <div id="footer">
                @RenderSection("returngis");
            </div>
        </div>
    </div>
</body>
</html>
 Cuando definimos una sección en el layout, el objetivo principal es que la misma pueda ser modificada dinámicamente dependiendo de la vista que solicitemos de la siguiente manera.
@section returngis{
    <p>Este es un ejemplo para <a href="http://www.returngis.net">Returng(GiS);</a></p>
}
Si tenemos varias vistas dependiendo del anterior layout y no todas asignan un valor a la sección definida anteriormente, al acceder a alguna de las vistas sin esta definición saltaría la siguiente excepción:
 
 
Este error ocurre debido a que, por defecto, cuando se define una sección se da por hecho que la misma debe estar implementada en cualquier vista que utilice este layout. Para evitar esto, basta con modificar la definición de la sección agregando el parámetro false al final para convertir la sección de obligatorio a opcional.
@RenderSection("returngis", false)

Nuevos helpers

Por último, y no por ello menos importante :D , se añaden a nuestra lista de helpers 5 más, algunos de ellos imprescindibles:

 

  • Chart: A partir de la versión 3 no necesitaremos crear un chart from scratch como hacíamos en este antiguo post. Este helper renderiza exactamente las mismas características que en ASP.NET 4.
  • WebGrid: Un helper más que necesario el cual renderiza un grid con paginación y ordenación. Gracias a José M. Aguilar tenemos un tutorial de este helper explicando paso a paso cada una de las funcionalidades.
  • Crypto: Utilizado para crear password hash.
  • WebImage: Se utiliza para renderizar imágenes.
  • WebMail: Nos ayuda en el envío de emails.
Espero que sea de utilidad :D
¡Saludos!  
Posted: 10/2/2011 17:01 por Gisela | con 4 comment(s)
Archivado en:
Lleida dotnetclub: Descubriendo Windows Azure Platform

 

Para seguir con el hilo de los webcasts, el día 16 de Febrero tengo el placer de hablaros de la plataforma Windows Azure para todos aquellos que aún no sepáis exactamente de qué trata.
Desde Lleida dotnetclub hablaremos sobre qué es la plataforma, los servicios que ofrece y cómo podemos comenzar a utilizar la misma.
Podéis registraros a través del siguiente enlace.

¡Saludos!

 

Posted: 10/2/2011 17:00 por Gisela | con no comments
Archivado en: ,
Windows Azure Companion

 

 
Una de las ventajas más importantes de la plataforma Windows Azure es que no sólo nos permite ejecutar nuestras aplicaciones web en .NET en la nube sino que además podemos desplegar cualquier aplicación web en cualquier lenguaje :D
En este post voy a centrarme en todos aquellos CMS por excelencia implementados en PHP: Drupal, Joomla y WordPress. Si bien podemos adaptarlos de forma manual utilizando el Web role de FastCGI, me gustaría mostraros una forma mucho más sencilla y con un entorno de administración muy útil gracias a Windows Azure Companion :)

¿Qué es Windows Azure Companion?

WAC se trata en realidad de una solución cloud debidamente preparada para la instalación y ejecución de aplicaciones PHP.

Como podemos ver en la imagen, el modo de proceder es bastante simple:
  1. Descargamos el paquete y el archivo de configuración de Windows Azure Companion.
  2. Establecemos los valores de configuración.
  3. Desplegamos el paquete.
  4. Elegimos las aplicaciones que queremos instalar.

Descarga de los paquetes de Windows Azure Companion

Actualmente, con la última release del mes de diciembre, tenemos varios tipos de descargas:
Tanto el primero como el segundo se tratan de los paquetes de despliegue con la única diferencia de que el segundo de ellos tiene configurados los endpoints necesarios para HTTPS. Por otro lado, ambos están pensados para ser ejecutados en una máquina virtual small.
Si necesitáramos cambiar algo más específico o simplemente modificar el tamaño de la máquina virtual por uno mayor, disponemos de un tercer paquete que contiene el código fuente.
En este post vamos a descargar el primero de ellos.

Configuración

Para poder hacer uso de esta aplicación, es necesario tanto un Hosted Service como una Storage Account, además de una o varias bases de datos, dependiendo del número de CMS que queramos instalar. 
Abrimos el archivo ServiceConfiguration.cscfg el cual muestra el siguiente aspecto:
<?xml version="1.0" encoding="utf-8"?>
<!--
  **********************************************************************************************

  This file was generated by a tool from the project file: ServiceConfiguration.cscfg

  Changes to this file may cause incorrect behavior and will be lost if the file is regenerated.

  **********************************************************************************************
-->
<ServiceConfiguration serviceName="WindowsAzureCompanion" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily="1" osVersion="*">
  <Role name="AdminWebSite">
    <!-- Do not increase the instance count. Windows Azure Companion does not support more than 1 instances -->
    <Instances count="1" />
    <ConfigurationSettings>
      <!-- Application Details. Title and Welcome text -->
      <Setting name="ApplicationTitle" value="Windows Azure Companion" />
      <Setting name="ApplicationDescription" value="&lt;p&gt;The &lt;span class=&quot;textlink&quot;&gt;&lt;a href=&quot;http://www.microsoft.com/windowsazure/windowsazure&quot;&gt;Windows Azure platform&lt;/a&gt;&lt;/span&gt; is a cloud platform that allows applications to &#xD;&#xA;      be hosted and run at Microsoft datacenters. It provides a cloud operating system called Windows Azure that serves as the &#xD;&#xA;      runtime for the applications and provides a set of services that allow development, management and hosting of applications.&lt;/p&gt;&#xD;&#xA;      &#xD;&#xA;      &lt;p&gt;With its standards-based and interoperable approach, the Windows Azure platform supports multiple Internet protocols including &#xD;&#xA;      HTTP, XML, SOAP and REST. As an open platform, the Windows Azure platform allows developers to use multiples languages &#xD;&#xA;      (.NET, Java, and PHP &amp; other dynamic languages ) to build applications which run on Windows Azure and/or consume any of the &#xD;&#xA;      Windows Azure platform offerings.&lt;/p&gt;&#xD;&#xA;      &#xD;&#xA;      &lt;p&gt;The Windows Azure Companion aims to provide a seamless experience for deploying platform-level components as well as applications &#xD;&#xA;      on to Windows Azure. The first community preview of the Windows Azure Companion has been tested with PHP runtimes, frameworks, &#xD;&#xA;      and applications. However, the system may be extended to be used for deploying any component or application that has been tested &#xD;&#xA;      to run on Windows Azure.&lt;/p&gt;" />
      <!-- Windows Azure Storage Account Details. -->
      <Setting name="WindowsAzureStorageAccountName" value="ACCOUNT NAME" />
      <Setting name="WindowsAzureStorageAccountKey" value="PRIMARY ACCESS KEY" />
      <!-- Admin user name, password and e-mail. Please user strong username and password -->
      <Setting name="AdminUserName" value="USERNAME" />
      <Setting name="AdminPassword" value="PASSWORD" />
      <!-- Atom Feed for Product items to be installed. This URL should be internet addressable -->
      <Setting name="ProductListXmlFeed" value="http://wazstorage.blob.core.windows.net/azurecompanion/default/WindowsAzureCompanionFeed.xml" />
      <!-- Settings for Windows Azure Drive used for durability -->
      <Setting name="PHPApplicationsBackupContainerName" value="companionv2" />
      <Setting name="InstallationStatusConfigFileBlob" value="status.xml" />
      <Setting name="ProgressInformationFileBlob" value="progress.xml" />
      <Setting name="XDrivePageBlobName" value="backup.vhd" />
      <Setting name="XDriveSizeInMB" value="2000" />
      <!-- Max value is defined as 500 in ServiceDefinition.csdef file (XDriveLocalCache Local Resource) -->
      <!-- Windows Azure Drive size is defined in ServiceDefinition.csdef. Look for ApplicationsAndRuntimeResource -->
      <Setting name="XDriveCacheSizeInMB" value="500" />
      <!-- Diagnostics and Performance Counter Capture Frequency in minutes -->
      <Setting name="DiagnosticsAndPerformanceCounterCaptureFrequencyInMinutes" value="1" />
    </ConfigurationSettings>
  </Role>
</ServiceConfiguration>
Los valores obligatorios que debemos modificar son:
  • Windows Azure Storage Account Name: Podemos localizar este nombre en las propiedades de la storage account que vayamos a utilizar.

  • Windows Azure Storage Account Key: Se refiere a la clave primaria de la cuenta de storage, también en las propiedades en el portal.

  • AdminUserName y AdminPassword para el acceso a la parte de administración.
  • ProductListXMLFeed: Se trata de un XML donde están registrados todos los repositorios que tenemos disponibles para instalarlos a través de la plataforma. Por defecto he dejado indicado el propio del autor pero podemos añadir uno propio.

Despliegue

Para el despliegue, basta con acceder a través del portal de Windows Azure y crear un nuevo deployment en uno de los dos entornos del Hosted Service.
Una vez iniciado el servicio ¡Ya podemos comenzar a instalar CMS :D !
 

Instalación de aplicaciones

Gracias a la nueva característica FullIIS anunciada en el PDC 2010, esta aplicación está configurada poder generar diferentes sitios dentro del mismo role. 
Para poder instalar los CMS,  debemos acceder a nuestra URL a través del puerto 8080, el cual ha sido establecido para las labores de administración. Por ejemplo:

Nos logamos con las credenciales establecidas en el archivo ServiceConfiguration.cscfg para poder acceder a los apartados Platform y Applications.

Espero que sea de utilidad :)
¡Saludos!

 

Posted: 29/1/2011 11:10 por Gisela | con 1 comment(s)
Archivado en: ,
Creación de un vhd para arrancarlo en modo nativo

 

 

 

Ayer me encontré con la necesidad de montar un Windows Server 2008 R2 en 64 bits. Ya que virtual PC no soporta imágenes en 64 bits y Virtual Box me daba problemas al querer instalar Hyper-V he decidio una solución con un gran rendimiento a la par que elegante: Montar un disco duro virtual (vhd) para poder arrancarlo en modo nativo  :D  

¿Qué necesitamos?

Creación del VHD

Lo primero que vamos a hacer es crear un disco duro virtual donde vamos a instalar posteriormente Windows Server 2008 R2 de 64 bits. Para ello, accedemos a la consola de comandos como administrador y ejecutamos lo siguiente: 
diskpart
create vdisk file=c:\Win2K8R2x64.vhd maximum 40960
sel vdisk file=c:\Win2k8R2x64.vhd
attach vdisk
list disk
 
Con el apartado anterior estamos creando un disco duro virtual de 40GB y lo montamos en el sistema. El siguiente paso será crear una partición y formatearla en NTFS para poder trabajar con el vhd. 
sel disk 2
create part primary
sel part 1
active
format fs=ntfs quick
assing

Instalación de Windows Server 2008 R2 64 Bits en el VHD

Una vez que tenemos el vhd montado y formateado ya podemos instalar el sistema operativo. Para ello necesitamos instalar The Windows® Automated Installation Kit (AIK) for Windows® 7. Accedemos a la consola de comandos de AIK a través de Inicio => Todos los programas => Microsoft Windows AIK => Deployment Tools Command Prompt como administrador. 
ImageX /apply c:\en_windows2008_r2_x64\sources\install.wim 3 /check F:\
Si desglosamos los parámetros de este comando analizamos lo siguiente:  
  • Imagex /apply nos dice la acción que vamos realizar. Este comando se utiliza para aplicar imágenes en HDs y VHDs.
  • c:\en_windows2008_r2_x64\sources\install.wim es la ubicación del archivo .wim que es la imagen que queremos aplicar.
  • El 3 se utiliza para decir cuál de las imagenes queremos utilizar de las disponibles dentro del archivo wim. En el caso del DVD de Windows Server 2008 R2 de 64 bits disponemos de varias imágenes:
    1. Windows Server 2008 R2 Standard (Full Installation)
    2. Windows Server 2008 R2 Standard (Server Core Installation)
    3. Windows Server 2008  R2 Enterprise (Full Installation)
    4. Windows Server 2008 R2 Enterprise (Server Core Installation)
    5. Windows Server 2008 R2 Datacenter (Full Installation)
    6. Windows Server 2008 R2 Datacenter (Server Core Installation)
    7. Windows Web Server 2008 R2 (Full Installation)
    8. Windows Web Server 2008 R2 (Server Core Installation)


    En este post instalaremos la versión Enterprise, la cual ocupa el tercer lugar. 

  • /check es un parámetro opcional para comprobar la integridad del archivo install.wim.
  • F:\ se trata de la unidad asignada a nuestro VHD. 

Registrar el vhd en boot.ini

Como último paso debemos registrar el vhd en el boot para poder seleccionarlo en el menú durante el arranque.
En primer lugar, volvemos a la línea de comandos y desmontamos el vhd del sistema. 
 
diskpart
select vdisk file=c:\Win2K8R2x64.vhd
detach vdisk
exit
Para registrar el nuevo disco, utilizamos el comando bcdedit a través de la línea de comandos: 
bcdedit /copy {current} /d "Windows Server 2008 R2 64 bits – VHD Boot"
bcdedit /set {nuevo_GUID} device vhd=[C:]\Win2K8R2x64.vhd
bcdedit /set {nuevo_GUID} osdevice vhd=[C:]\Win2K8R2x64.vhd
bcdedit /set {nuevo_GUID} detecthal on
Espero que sea de utilidad :D  
¡Saludos!

 

Posted: 23/1/2011 21:06 por Gisela | con 8 comment(s)
Archivado en:
Leer archivos XML con JQuery

Desde que JQuery llegó a nuestras vidas, Javascript se ha convertido en un camino de rosas :D Tanto es así que en pocas líneas podemos conseguir el parseo completo de un archivo XML ¡Vamos a verlo!

En primer lugar hay que aclarar que podemos toparnos principalmente con tres formatos de XML diferentes:
  1. Utilizando atributos: Cada elemento guarda su información dentro de atributos. Los atributos deberían utilizarse para información predeterminada, metadatos, archivos grandes (los atributos tienden a ocupar menos bytes que los elementos).
    <?xml version="1.0" encoding="utf-8" ?>
    <Blogs>
      <blog name="WebLog Expert - Powerful log analyzer"
            description="Download and analyze Apache and IIS Web logs on your own PC."
            url=" http://www.weblogexpert.com/"/>
      <blog name="Professional drupal themes"
            description="For professional drupal themes with a unique look and feel, visit TopNotchThemes.com."
            url="http://www.topnotchthemes.com/"/>
      <blog name="Paypal Developer Community"
            description="The community for developping paypal payment processing scripts."
            url="http://www.pdncommunity.com" />"
    </Blogs>
  2. Utilizando nodos/elementos: Los elementos contienen a su vez otros elementos llamados nodos. Se suelen utilizar para almacenar datos.
    <?xml version="1.0" encoding="utf-8" ?>
    <Blogs>
      <blog>
        <name>WebLog Expert - Powerful log analyzer</name>
        <description>Download and analyze Apache and IIS Web logs on your own PC.</description>
        <url>http://www.weblogexpert.com/</url>
      </blog>
      <blog>
        <name>Professional drupal themes</name>
        <description>For professional drupal themes with a unique look and feel, visit TopNotchThemes.com.</description>
        <url>http://www.topnotchthemes.com/</url>
      </blog>
      <blog>
        <name>Paypal Developer Community</name>
        <description>The community for developping paypal payment processing scripts.</description>
        <url>http://www.pdncommunity.com</url>
      </blog>
    </Blogs>
  3. Mezcla de los dos anteriores.
Dependiendo del cómo esté almacenado utilizaremos un método u otro para recuperarlo.

Recuperación de atributos

//Read attributes

$.get("Attributes.xml", function (xml) {
    $(xml).find("blog").each(function () {

       var name = $(this).attr('name');
       var description = $(this).attr('description');
       var url = $(this).attr('url');

alert(name + " " + description + " " + url);
    });
});

Recuperación de nodos/elementos

 

//Read nodes
  $.get("Nodes.xml", function (xml) {
    $(xml).find("blog").each(function () {
       var name = $(this).find('name').text();
       var description = $(this).find('description').text();
      var url = $(this).find('url').text();

       alert(name + " " + description + " " + url);
    });
});

 

Lo que estamos haciendo en ambos casos es una iteración a través de each por cada elemento blog que tengamos en el archivo. Dependiendo del tipo que esperemos utilizaremos attr para recuperar los atributos y find para los elementos.
Espero que sea de utilidad :)

 

¡Saludos!

 

Posted: 23/1/2011 21:02 por Gisela | con no comments
Archivado en:
Log4net: trazando nuestras aplicaciones
 
 
En ocasiones es importante almacenar un registro con información sobre los procesos realizados, principalmente en el entorno de desarrollo. Otra de las necesidades comunes puede ser recibir un correo cuando una acción finaliza correcta y/o incorrectamente o simplemente mantener un registro de un determinado nivel de información. Log4net cubre todas estas necesidades por nosotros de una forma altamente configurable :D

 

Esta librería es fruto de su antecesora log4j (log for Java) la cual ha derivado para diferentes lenguajes como C++, PHP y por supuesto .NET.

Configuración

Toda la configuración de la librería puede estar incluida en el archivo de configuración o programáticamente. Personalmente pienso que la configuración debe estar contenida en los archivos que tienen ese fin :)
<configSections>
   <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
Una vez que tenemos definida la sección en configSections, personalizamos un apartado con la configuración para nuestras trazas. En este post mostraré una configuración para mostrar las mismas a través de la consola y por email.
<log4net>
    <logger name="Program">
      <level value="INFO" />
      <appender-ref ref="ConsoleAppender" />
    </logger>
    <logger name="ProductosRepository">
      <level value="WARN" />
      <appender-ref ref="SmtpAppender" />
    </logger>
    <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
      </layout>
    </appender>
    <appender name="SmtpAppender" type="log4net.Appender.SmtpAppender,log4net">
      <to value="destinatario@returngis.net" />
      <from value="remitente@returngis.net" />
      <subject value="Asunto del correo" />
      <smtpHost value="smtp.returngis.net" />
      <bufferSize value="512" />
      <lossy value="true" />
      <evaluator type="log4net.Core.LevelEvaluator,log4net">
        <threshold value="ERROR" />
      </evaluator>
      <layout type="log4net.Layout.PatternLayout,log4net">
        <conversionPattern value="%property{log4net:HostName} :: %level :: %message %newlineLogger: %logger%newlineThread: %thread%newlineDate: %date%newlineNDC: %newline%newline" />
      </layout>
    </appender>
</log4net>
Si nos fijamos en la configuración expuesta, vemos que tenemos en realidad dos apartados repetidos: logger y appender.

Elemento logger

Este elemento define la configuración para ser utilizada en un apartado específico de nuestro código. Me explico: Supongamos que queremos mostrar información por pantalla de los pasos que se van completando en una capa de nuestra aplicación, para saber qué parte de nuestro código se está ejecutando. Por otro lado, necesitamos recibir un email cada vez que en la lógica relacionada con la base de datos o los servicios se produzca un error. Gracias a este apartado, podemos definir diferentes configuraciones para poder usarlas conjuntamente en una misma aplicación. Como parámetros tenemos el nivel de información que queremos registrar y el tipo de salida (appender).
 El nivel de error es acumulativo, es decir, si nuestro nivel de error es de tipo INFO abarcará los niveles información, warnings, errores y errores fatales. Si por el contrario el nivel establecido es ERROR, este sólo cubrirá los errores y los errores fatales. Por lo tanto, el modo que habilita todos los niveles sería el modo DEBUG.

Elemento appender

Por otro lado, los appenders son los tipos de salida que podemos configurar. En este post voy a mostrar solamente la salida por consola y por SMTP para el envío de correos. Para más ejemplos sobre otros tipos podéis echar un vistazo a este enlace.
Como valores significativos podemos mencionar:

Pattern.Layout para definir información a mostrar a través de patrones. En el caso del ConsoleAppender que tenemos definido en el ejemplo la información sería mostrada de la siguiente forma:

 

Lossy se utiliza de forma conjunta con el elemento evaluator para evitar el consumo excesivo de recursos en producción cuando la ejecución de nuestro programa es normal. Sin embargo, si la aplicación registra un error registrará esta información con un pequeño batch de lo ocurrido momentos antes de la excepción :)

Los appenders que soportan esta opción son los siguientes:
  • log4net.Appender.AdoNetAppender
  • log4net.Appender.RemotingAppender
  • log4net.Appender.SmtpAppender
  • log4net.Appender.SmtpPickupDirAppender
  • log4net.Appender.BufferingForwardingAppender

Recuperar la configuración

Como comenté en el apartado anterior, dependiendo del cuál queramos que sea el nivel y el tipo de salida, debemos solicitar una configuración u otra a la hora de la creación de nuestro objeto del tipo ILog. A través del mismo podremos registrar las trazas necesarias. En este caso, he utilizado el nombre de la clase en la que me encuentro para dar nombre a los distintos apartados, pero podríamos utilizar, por ejemplo, el nombre del proyecto o cualquier otro nombre significativo.
private static readonly ILog Logger = LogManager.GetLogger(typeof(Program).Name);
El siguiente paso sería cargar la configuración desde el archivo de configuración, a través del siguiente método:
XmlConfigurator.Configure();
A partir de este momento estamos listos para comenzar a trazar nuestro código :D Para ello, basta con utilizar el objeto logger creado anteriormente de manera bastante similar a Trace.
Logger.Info("Retrieving files...");
Del mismo modo, podemos formatear strings:
Logger.ErrorFormat("{0}:{1}", ex.InnerException, ex.StackTrace);
Espero que sea de utilidad :)

¡Saludos!
Posted: 13/1/2011 12:16 por Gisela | con 5 comment(s)
Archivado en: ,
Más artículos Página siguiente >