[TIP] Mejorando Linq. Un Comparador genérico y un “DistinctBy” !!!

Intentando utilizar el “Distinct” de Linq, me he encontrado con la necesidad te tener que implementar una clase “IEqualityComparer<T>”. Concretamente tenía que obtener objetos distintos en una gran lista. Así que, después de buscar un poco, y gracias a nuestro compañero driis, me gustaría compartir la siguiente clase con vosotros. Creo que puede comenzar a formar parte de nuestros desarrollos.

La utilizaremos de cualquier de las dos formas siguientes:

  1. list.DistinctBy(item => item.Id);
    1 public static class Compare 2 { 3 public static IEnumerable<T> DistinctBy<T, TIdentity>(this IEnumerable<T> source, Func<T, TIdentity> identitySelector) 4 { 5 return source.Distinct(Compare.By(identitySelector)); 6 7 public static IEqualityComparer<TSource> By<TSource, TIdentity>(Func<TSource, TIdentity> identitySelector) 8 { 9 return new DelegateComparer<TSource, TIdentity>(identitySelector); 10 } 11 12 private class DelegateComparer<T, TIdentity> : IEqualityComparer<T> 13 { 14 private readonly Func<T, TIdentity> identitySelector; 15 16 public DelegateComparer(Func<T, TIdentity> identitySelector) 17 { 18 this.identitySelector = identitySelector; 19 } 20 21 public bool Equals(T x, T y) 22 { 23 return Equals(identitySelector(x), identitySelector(y)); 24 } 25 26 public int GetHashCode(T obj) 27 { 28 return identitySelector(obj).GetHashCode(); 29 } 30 } 31 }

  2. list.Distinct(Compare.By(item => item.Id));

O incluso, cada vez que necesitemos un “IEqualityComparer<T>” como parámetro.

 

Saludos

Juanlu

TIP: Unos pocos clics para que tus tests de Windows Phone 8.1 funcionen en Visual Studio 2015

¿Estás trabajando en una aplicación Windows Phone 8.1 y quieres que tus tests funcionen en Visual Studio 2015 en unos pocos clics? Sólo tienes que seguir estos breves pasos:

Coded UI TestT

  • Simplemente cambia el siguiente “Reference Path”:

image

Unit Test

  • Selecciona el proyecto y desde su menú contextual elige la opción: “Unload Project”
  • Vuelve a seleccionar el proyecto, y edítalo: “Edit WindowsPhone.Tests (unavailable)”.
  • Busca las siguientes líneas y asegúrate de que la versión indicada en las mismas es la “14.0”.
  • Vuelve a cargar el proyecto  (“Reload project”) y listo para compilar sin errores.

Esto ha sido todo. ¡Ahora tienes un motivo menos para no tener instalado VS2015!
Saludos
Juanlu

WinRT: Working on Local Storage: File System & SQLite. IsolatedStorageExplorer, IsoStorySpy y SQLite Toolbox

Muy buenas,

Continuando con los posts pendientes, veamos en éste algunas herramientas imprescindibles al desarrollar aplicaciones Windows 8.1 / Windows Phone 8.1 y Windows 10.

Cuando trabajamos con ficheros, es decir, con el Storage, es muy normal querer conocer que información estamos guardando en ellos  y las rutas en las que estos son almacenados. También puede ser interesante intercambiar ficheros entre el PC y el emulador o dispositivo móvil y viceversa. 

De esta misma manera, también podemos plantearemos si utilizar o no ficheros para la gestión de la información de nuestra aplicación o bien, utilizar un sistema de BD. ¿SQL Server CE o SQLite? La respuestas a estas preguntas pueden variar y no pretendemos abordarlas aquí. ¡Podemos echar un vistazo a este enlace para ver una comparativa entre estos dos y aun otro!. Lo que si veremos ,es como trabajar con SQLite desde Visual Studio y como comprobar la información que almacenamos en el. Es decir, como realizar consultas utilizando una interfaz gráfica al igual que como hacemos con “Management Studio” para SQL Server.

Veamos cuales son algunas de las herramientas que va a permitirnos hacer todo esto:

1) FILE SYSTEM

a) IsolatedStorageExplorer. Esta herramienta, como comentamos en el capítulo uno, es una de las herramientas que encontramos en el directorio de instalación del SDK (“C:Program Files (x86)Microsoft SDKsWindows Phonev8.1Tools”) y se basa en línea de comandos. Su uso es el siguiente:

1. Listado de emuladores instalados. “ISETool.exe EnumerateDevices”:

IsolatedStorageExplorer-EnumerateDevices

2. Listado de ficheros para el dispositivo conectado. “ISETool.exe dir deviceindex:2 300177dd-2599-41dd-acf4-3168c460f615”.

Dónde: deviceindex:2 hace referencia al dispositivo/emulador del que queremos listar los ficheros, según la lista anterior, es decir, “Emulator 10.0.1.0 WVGA 4 inch 1GB”. El identificador (o GUID) de la aplicación se encuentra en el fichero de manifiesto de la aplicación indicado por la propiedad “PhoneProductId”.

3. Copia de ficheros entre dispositivo/emulador y el PC. “ISETool.exe <ts | rs> deviceindex:2 300177dd-2599-41dd-acf4-3168c460f615 "C:MyAppFiles”.

Dónde: ts copia los ficheros desde el dispositivo al PC creando siempre el subdirectorio “IsolatedStore” en caso de no existir y, reemplaza todo su contenido sin previo aviso. rs, copia los ficheros desde la ruta del PC al dispositivo.

Para mayor detalle sobre esta herramienta visitar la página: https://msdn.microsoft.com/es-es/library/windows/apps/dn629254.aspx

b) IsoStorySpy:  Ésta, además de realizar las mismas acciones que “IsolatedStorageExplorer”, dispone de una interfaz gráfica que hace más fácil e intuitivo su uso. Podemos descargarla desde Codeplex (https://isostorespy.codeplex.com).

 image

Nota: Los número en la imagen anterior indican los pasos a seguir para la gestión ficheros.

Además, esta herramienta, añade más funcionalidad. Por ejemplo, incluye una zona de “Preview”, para la visualización de los ficheros de tipo imagen, vídeo o incluso tablas de “SQL Server CE” etc.

2) BD – SQLite

SQLite es un sistema gestor de base de datos relacional, Atómico, Consistente, Integro, permite el Aislamiento de sus transacciones y Durable (o persistente), es decir, es “ACID Compliant”. Se trata de una única librería con un tamaño relativamente pequeño, unos 275kb y guarda la información de BD (tablas, índices, datos, etc.), en un sólo fichero.

Es multiplataforma y de dominio público y gracias a su pequeño tamaño es recomendado y está siendo muy usado en aplicaciones móviles: Windows Phone 8/8.1, Windows 10, Android, BlackBerry, Google Chrome, iOS, Maemo, Symbian, webOS, etc.

Actualmente, SQLite es utilizado por muchas aplicaciones conocidas: Skype, Adobe Photoshop Elements, Mozilla Firefox, Opera, OpenOfficel.org, Apple Mail, etc.

El sitio web oficial es http://sqlite.org y la descarga podemos realizarla desde este otro enlace: http://sqlite.org/download.html .

INSTALACIÓN: Para poder trabajar con SQLite desde Visual Studio seguiremos los siguientes pasos:

1. Descargar el paquete “.VSIX”, desde la página de descargas antes indicada, Windows Runtime 8.1, Windows Phone 8.1 o Windows 10 / 10 Móvil. ¡Por el momento podemos optar por los paquetes de 8.1, que funcionan correctamente en todos los casos!

2. Instalar el paquete “.VSIX” y reiniciar Visual Studio si estaba abierto. Para comprobar la instalación, desde el menú “Tools – Extensions and Updates…” de Visual Studio, acceder a las extensiones y actualizaciones instaladas:

ExtensionAndUpdates-SQLite

3. Añadir al proyecto Windows / Windows Phone la referencia “SQLite for Windows 8.1” / “SQLite for Windows Phone 8.1” respectivamente tal y como puede verse en la siguiente imagen para un proyecto de Windows Phone 8.1:

AddSQLiteReferenceToProject

Nota: Únicamente es posible añadir la extensión anterior a los proyectos “Windows” y/o “Windows Phone”.

4. Para el proyecto “Windows” / “Windows Phone” y para nuestro proyecto de acceso a datos,  por ejemplo, “Data” , instalar desde NuGet, el paquete: “SQLite.Net.Async-PCL”, que a su vez instalará la dependencia “SQLite.Net-PCL” y junto a esta, se añadirá también, automáticamente la dependencia “SQLite.Net.Platform.WinRT”.

AddSQLiteNugetToProject

A partir de este momento, nuestro proyecto “Data” está listo para gestionar la información de la aplicación con SQLite.

USO: Aunque no entraremos en el detalle de como usar SQLite, no al menos en este post, me gustaría incluir como hacer la conexión y la creación de tablas (mediante código), para comenzar a trabajar.

1 public SQLiteProvider(ISQLitePlatform sqlitePlatform) 2 { 3 this.sqlitePlatform = sqlitePlatform; 4 5 if (null == db) 6 { 7 // Obtener ruta donde generar el fichero ".sqlite" que contendrá la estructura de la BD y su contenido. 8 string databaseFile = StorageHelper.GetFullPath(DATABASE_FILE_NAME); 9 if (!string.IsNullOrWhiteSpace(databaseFile)) 10 { 11 var connection = new SQLiteConnectionWithLock(sqlitePlatform, new SQLiteConnectionString(databaseFile, false)); 12 db = new SQLiteAsyncConnection(() => connection); 13 14 InitializeAsync(); 15 } 16 else 17 { 18 throw new System.IO.IOException("No access to database file"); 19 } 20 } 21 } 22 23 private async System.Threading.Tasks.Task InitializeAsync() 24 { 25 await db.CreateTableAsync<ProjectTable>(); 26 await db.CreateTableAsync<TaskTable>(); 27 await db.CreateTableAsync<ImageTable>(); 28 }

Una vez generada la conexión de la BBDD y la creación de las tres tablas anteriores, encontraremos un fichero “MyProjects.sqlite” en la carpeta “Local”. Copiaremos éste al PC usando las herramientas (“IsolatedStorageExplorer” o “IsoStoreSpy”).

Herramienta gráfica (SQL Server Compact / SQLite Toolbox):

De la misma forma que al trabajar con ficheros necesitábamos conocer la información almacenada en ellos, querremos conocer también la información con la que trabajamos en SQLite, que consultas estamos ejecutando y que información estamos obteniendo, insertando, actualizando o eliminando.

Los pasos a seguir para trabajar con ella son:

1. Ejecutar el instalable “SqlCeToolbox.vsix” y reiniciar Visual Studio.

2. Ejecutar la opción de menú: “Tools – SQL Server Compact/SQLite Toolbox”, para mostrar el explorador o caja de herramientas.

Menu-SQLite Toolbox

3. Una vez que aparezca el explorador, crear una nueva conexión indicando un nombre de fichero para la misma, o bien seleccionar un fichero existente con extensión “.sqlite”. En nuestro caso, seleccionaremos el fichero “MyProjects.sqlite”, generado a partir del código anterior y que hemos descargado al PC.

AddSQLite_Connection

4. Una vez creada la conexión, el explorador de SQLite, estaremos en disposición de comenzar a trabajar con la BD al igual que lo hacemos para SQL Server con Visual Studio o con “Management Studio”.

SQLToolbox in action

A partir de aquí, a seguir explorando y trabajando con estas herramientas, ¡que no se diga que las aplicaciones WinRT o Windows 10 suponen un contratiempo! Guiño

Espero que sea de utilidad.

Saludos

Juanlu

Mejorando la “responsividad”: asyc y await, AsyncLazy<T> y MVVM asíncrono (NotifyTaskCompletion<T>)

Muy buenas,

Hoy me gustaría comentar algunos “Tips“ que creo, deberíamos conocer cuando desarrollamos aplicaciones WinRT, o incluso cuando desarrollamos casi cualquier aplicación .NET, al menos 4.0 o superior.

En primer lugar, simplemente recordar / repasar el patrón async y await, que cada día cobra más y más importancia debido a los dispositivos móviles y a las aplicaciones responsivas.

  • Se trata de un patrón que hace más ágil la interacción con  nuestras aplicaciones evitando incluso alguna larga espera (“congelación”) de la misma durante su ejecución.
  • Permite el lanzamiento de operaciones asíncronas sin bloqueos en la Interfaz de Usuario
  • Su implementación es muy sencilla y mantiene un código legible.
  • Se introdujo a partir de NetFX 4.5 / Windows Phone 8 y WinRT.
  • Podemos utilizarlo en NETFX 4.0 / Windows Phone 7.1 / Silverlight 4 / MonoTouch / MonoDroid  y  Librerías de clases Portables, con Visual Studio 2012 o posterior  y el paguete NuGet, Microsoft.Bcl.Async.

Nota: Asyn no implica necesariamente el uso de hilos (threads), esto es opcional.

Los métodos de pruebas unitarias (Tests), tendrán que indicarse como “Public async Task TestMethod1() { … }”. ¡No sé porqué, pero siempre se me olvida. Y hasta que no pretendo lanzarlos y no los veo en la consola, no lo recuerdo! grrr…

 

En segundo lugar. Para retrasar la carga de un costoso consumo de recursos, hasta el momento en el que sea realmente necesario su uso. Utilizaremos el patrón de instanciación perezosa, o, mas comúnmente conocido como ” Lazy<T>. Sin embargo, si para dicha carga se requiere no bloquear la UI, necesitaremos por tanto, un método asíncrono, con lo que perderemos de vista el objetivo principal del patrón Lazy. Éste, explicitamente no lo permite.

Por ejemplo, si en WinRT queremos ejecutar la instrucción

this.folder = new Lazy<StorageFolder>(() => this.CreateFolderIfNotExistsAsync(folderName));

Podemos pensar  en cambiarla para que sea asíncrona:

this.folder = new Lazy<StorageFolder>(async () => this.CreateFolderIfNotExistsAsync(folderName));”

No obstante, obtendremos un error en tiempo de diseño: “Cannot convert lambda expression to type ‘System.Threading.LazyThreadSafeMode’ because it is not a delegate type.

Podemos seguir intentándolo, pero en lugar complicar el código, echemos un vistazo a la siguiente clase:

1 public class AsyncLazy<T> : Lazy<Task<T>> 2 { 3 public AsyncLazy(Func<T> valueFactory) : 4 base(() => Task.Factory.StartNew(valueFactory)) { } 5 6 public AsyncLazy(Func<Task<T>> taskFactory) : 7 base(() => Task.Factory.StartNew(() => taskFactory()).Unwrap()) { } 8 9 public TaskAwaiter<T> GetAwaiter() { return Value.GetAwaiter(); } 10 }

Gracias a ella y de manera muy sencilla, nuestra instanciación perezosa se ejecutará de manera asíncrona y no bloqueará el UI.

this.folder = new AsyncLazy<StorageFolder>(() => this.CreateFolderIfNotExistsAsync(folderName));

 

Por último. Cuando trabajamos con WinRT siguiendo el patrón MVVM y queremos abrir una nueva ventana/pantalla, los enlaces a datos (o Bindings) se realizan de manera síncrona y automática en el momento de la carga de esa nueva ventana. Puede ocurrir por tanto, que el tiempo de espera se vea incrementado dando la sensación de una aplicación poco responsiva. Si además, la ventana es nuestro “Home”, el impacto en el usuario puede ser mayor. Para evitarlo, entre otras opciones, la clase “NotifyTaskCompletion<T>”, como la siguiente, puede ayudarnos:

1 public sealed class NotifyTaskCompletion<TResult> : INotifyPropertyChanged 2 { 3 public NotifyTaskCompletion(Task<TResult> task) 4 { 5 Task = task; 6 if (!task.IsCompleted) 7 { 8 var watcher = WatchTaskAsync(task); 9 } 10 } 11 private async Task WatchTaskAsync(Task task) 12 { 13 try 14 { 15 await task; 16 } 17 catch 18 { 19 } 20 21 var propertyChanged = PropertyChanged; 22 if (propertyChanged == null) return; 23 24 propertyChanged(this, new PropertyChangedEventArgs("Status")); 25 propertyChanged(this, new PropertyChangedEventArgs("IsCompleted")); 26 propertyChanged(this, new PropertyChangedEventArgs("IsNotCompleted")); 27 if (task.IsCanceled) 28 { 29 propertyChanged(this, new PropertyChangedEventArgs("IsCanceled")); 30 } 31 else if (task.IsFaulted) 32 { 33 propertyChanged(this, new PropertyChangedEventArgs("IsFaulted")); 34 propertyChanged(this, new PropertyChangedEventArgs("Exception")); 35 propertyChanged(this, new PropertyChangedEventArgs("InnerException")); 36 propertyChanged(this, new PropertyChangedEventArgs("ErrorMessage")); 37 } 38 else 39 { 40 propertyChanged(this, new PropertyChangedEventArgs("IsSuccessfullyCompleted")); 41 propertyChanged(this, new PropertyChangedEventArgs("Result")); 42 } 43 } 44 45 public Task<TResult> Task { get; private set; } 46 public TResult Result 47 { 48 get 49 { 50 return (Task.Status == TaskStatus.RanToCompletion) ? Task.Result : default(TResult); 51 } 52 } 53 public TaskStatus Status { get { return Task.Status; } } 54 public bool IsCompleted { get { return Task.IsCompleted; } } 55 public bool IsNotCompleted { get { return !Task.IsCompleted; } } 56 57 public bool IsSuccessfullyCompleted 58 { 59 get 60 { 61 return Task.Status == TaskStatus.RanToCompletion; 62 } 63 } 64 public bool IsCanceled { get { return Task.IsCanceled; } } 65 public bool IsFaulted { get { return Task.IsFaulted; } } 66 public AggregateException Exception { get { return Task.Exception; } } 67 public Exception InnerException 68 { 69 get 70 { 71 return (Exception == null) ? null : Exception.InnerException; 72 } 73 } 74 public string ErrorMessage 75 { 76 get 77 { 78 return (InnerException == null) ? null : InnerException.Message; 79 } 80 } 81 public event PropertyChangedEventHandler PropertyChanged; 82 }

La usaremos en uno de los métodos de inicio (o constructor),  de nuestra pantalla, de la siguiente manera:

this.MyListAsync = new NotifyTaskCompletion<Project>(this.service.GetProject(projectId));

En el XAML realizaremos el enlace con la instrucción: “MyListAsync.Result”:

1 <ListView Grid.Row="1" x:Name="listViewTasks" 2 Margin="10,0,5,0" 3 ItemsSource="{Binding MyListAsync.Result, Mode=TwoWay}" 4 ItemTemplate="{StaticResource SmallImageDetailTemplate}" >

Y, por ejemplo, habilitaremos o no un botón de edición sólo si la carga se ha realizado con éxito.

1 <Button Content="&#xE104;" Margin="10,0,0,0" 2 FontSize="18" FontFamily="Segoe Ui Symbol" 3 Visibility="{Binding ProjectAsync.IsSuccessfullyCompleted, Converter={StaticResource BooleanToVisibilityConverter}}" 4 Command="{Binding EditProjectCommand}" />

Así mismo y aunque algo menos elegante, podríamos interactuar en el ViewModel de la siguiente manera, o incluso exponiendo eventos tales como “NotifySuccessfullyCompleted, “NotifyFaulted” y “”NotifyCanceled” dentro de la clase NotifyTaskCompletion<T>.

1 this.ProjectAsync.PropertyChanged += (sender, e) => 2 { 3 if (e.PropertyName == "IsSuccessfullyCompleted") 4 { 5 // Iniciar, cargar o ejecutar métodos una 6 // vez obtenido un proyecto y toda su información 7 } 8 else if (e.PropertyName == "IsFaulted") 9 { 10 // Tratar casos de error 11 } 12 };

Referencias:

Espero que estos cuantos Tips hayan sido de utilidad.

Saludos desde lo que ha sido un gran puente del Corpus: Cervezas, buen pescadito, descanso y playa. Risa

Juanlu,ElGuerre

Comenzando con Xamarin y Visual Studio 2015

Muy buenas,

Hace ya un tiempo que no me dejo ver por aquí, el motivo no es otro que el de estar altamente centrado en otros menesteres. Espero sin embargo estar de nuevo al pie el cañón y dejarme ver, al menos, un más.

Tengo algunos posts sobre WPF, Windows Phone 8.1 y WinRT e incluso Windows 10 en el tintero, no me olvido de ellos, pero por el momento, dedicaré este post a mis primeros pasos con Xamarin.

Antes de Comenzar debemos saber que:

Existen algunos aspectos importantes en cuanto a las licencias:

Starter, tenemos algunas limitaciones, principalmente en cuanto al tamaño de cada ensamblado, que no puede superar los 64kb / 128kb. Aunque parece suficiente para probar,  he de decir que todo lo contrario, una solución creada directamente desde una plantilla de Visual Studio (“Native Shared”) ya supera este límite. En este caso, al compilar, obtendremos errores como los siguientes:

“User code size, 2945919 bytes, is larger than 131072 and requires a Business (or higher) License. XamarinApp1.Droid”

– “Using type `Android.Runtime.JNIEnv` requires Business (or higher) License.    XamarinApp1.Droid”

En estos enlaces: http://xamarin.com/faq#q18 y http://forums.xamarin.com/discussion/2912/xamarin-starter-edition-build-limits, podemos leer un poco más acerca de estas limitaciones.

Trial, tenemos 30 días para probar sin las restricciones anteriores. 30 días me parecen pocos, y  más cuando en esos días el tiempo dedicado a a las pruebas no es el deseado. Como solución, podemos crear una cuenta nueva cada 30 días !!! Pero…, antes de esto, nos aseguraremos de que la compañía en la que trabajamos no tienen acuerdos a este respecto. Es normal disponer de hasta 90 días e incluso renovaciones, y esto ya si comienza a ser un tiempo razonable para dar nuestros primeros pasos.

En cuanto al emulador Android de Xamarin, podemos echar un vistazo a este enlace: Introducing Visual Studio’s Emulator for Android.

Y por supuesto, no nos podemos olvidar de MVVM y Xamarin. Laurent Bugnion (GalaSoft), nos ayuda con esto: http://blog.galasoft.ch/posts/2014/10/my-xamarinevolve-talk-is-online-for-your-viewing-pleasure/

Conociendo estos aspectos entre otros, y, una vez instalado Visual Studio 2015 RC, lo primero es crear nuestro proyecto Xamarin, y para ello:

Elegimos la plantillas “Blank App (Native Shared)”:

image

Establecemos desde el Explorador de Soluciones, el proyecto “XamarinApp1.Droid” como proyecto de inicio (“Set as StartUp Project”).

Pulsamos “F5”, seleccionando previamente el emulador que con el que queramos trabajar.

image

En este punto nuestra aplicación se habría ejecutado sin más preámbulos en el emulador de Android, sin embargo, y como todo no puede ser fácil a la primera, nos encontramos con que el emulador no se inicia produciendo el mensaje: “Starting emulator: VS Emulator 5" KitKat (4.4) XXHDPI Phone”. Después de esperar durante un buen rato, el estado sigue siendo el mismo.

Tras varios intentos y el mismo resultado, decido probar los siguientes pasos:

-Borrar la carpeta correspondiente al emulador que no se inicia, en este caso: “C:Usersjuan.luis.guerreroAppDataLocalMicrosoftVisualStudioEmulatorAndroidContainersLocalDevicesvhd5_KitKat_(4.4)_XXHDPI_Phone”. Como hemos podido ver en el enlace, Introducing Visual Studio’s Emulator for Android, los emuladores de Android, son imágenes “.vhd” que funciona bajo Hyper-V y que se encuentra localizadas en estas carpetas.

– Desinstalar e instalar imágenes desde “Visual Studio Emulator for Android”

step 3

– Crear una nueva máquina virtual a partir de las imágenes .vhd anteriores de manera manual con “Hyper-V Manager”.

– Desinstalar “Microsoft Visual Studio Emulator for Android”

– Reparar la instalación de ,  “Microsoft Visual Studio 2015 RC”.

– Tras cada una de estas acciones, el problema continua, por lo que opto por desinstalar “Microsoft Visual Studio 2015 RC” y volver a instalarlo.

– Finalmente y tras un poco de paciencia, el emulador se inicia correctamente.

Cuando finalmente todo parece que funcionar, al iniciarse la aplicación, …. “Unfortunately, App1.Dro1 has stopped”.

screen1

Nos queda un paso más, desmarcar la casilla “Use Share Runtime”. Ahora sí,  la aplicación Xamarin se inicia por primera vez en el emulador de Android.

image step ok

Si alguien ha encontrado una mejor solución y quiere compartirla, Bienvenida será, así que, gracias de antemano por ello.

He de decir, que con Visual Studio 2015, los emuladores de Android han mejorado mucho. ¡Una recomendación, sin duda, para comenzar a crear aplicaciones Cross Platform con Xamarin!.

Por el momento, es todo, espero que este pequeño post aclare algunos pasos y evite alguna desesperación a algún principiante como yo.  Seguiremos profundizando en algunos de los aspectos comentados e iremos viendo otros.

Enjoy your cross Apps.
Juanlu, ElGuerre

Windows Phone 8.1. Primeras Best Practices con MVVM y GalaSoft.MvvmLight

image_thumb12Muy buenas,

En breve recibiremos la actualización oficial de este nuevo SO. No obstante, hace ya una semana que Windows Phone 8.1 está entre nosotros gracias a la aplicación “Preview for Developers”. Nuevas características y opciones nos tienen entretenidos. Noticias cada día sobre cómo configurarlo o cómo utilizar cada una de sus nuevas opciones. Josue Yeray y otros compañeros ya han posteado a este respecto como podemos ver aquí.

En cuanto al desarrollo, también nos encontramos con novedades:

  • Nuevos Emuladores y otras herramientas. Principalmente “Windows Phone Power Tools.”
  • Tareas de background y triggers: Notificaciones push, geofencing, etc.
  • Convergencia de APIs con Windows Store (aproximadamente un 90%).
  • Aplicaciones Universales para C# y WinJS (HTML 5 y JS).
  • Nuevas APIs para para la edición de vídeo y audio.
  • Etc.

Para no escribir más sobre estos temas, podemos echar un vistazo a este enlace.

Aunque existen compatibilidad total con proyectos Windows Phone 8.0, así como la posibilidad de migrar los proyectos de Windows Phone 8.0 a 8.1 (Silverlight), lo recomendable es comenzar a desarrollar proyectos WinRT basados en los nuevos modelos de aplicación para Windows Phone 8.1 y Windows. Modelos que podemos ver en la siguiente imagen:

image_thumb2

En cuanto al patrón MVVM, como siempre, Microsoft presenta su manera de implementarlo. Sin embargo, si al igual que yo, usas “GalaSoft.MvvmLight”, entonces tendrás que seguir algunas pautas, teniendo en cuenta que por el momento, no tenemos las plantillas que nos automatizan el trabajo.

Los siguientes pasos son los siguientes:

  • Crear un nuevo Proyecto Universal o Windows Phone 8.1 de tipo Hub App desde Visual Studio 2013 Update 2.
  • Añadir las referencias o instalar desde nuget el paquete MVVM Light Libraries only (PCL) 4.3.31.2.
  • Añadir la referencia a la DLL “Behaviors SDK (XAML)”. Es importante incluir ésta para poder hacer uso de triggers (“<i:Interaction.Behaviors>”)  desde el XAML y vincular así cada View con su ViewModel.

image_thumb4

  • Dentro del proyecto Shared, crear la carpeta ViewModel y añadir en su interior las clases ViewModel tal y como ya estábamos acostumbrados. De ésta manera tendremos algo como lo siguiente:

image_thumb10

Nota: Los interfaces son opcionales y, dependerán de hasta que punto queramos testear nuestra aplicación. Sin embargo, la interfaz IViewModelBase si que es necesaria. De la misma manera, como puede verse, la carpeta Common sólo contiene lo que vamos a necesitar además de una nueva clase, PageBase. Ambas, Interfaz y clase se muestran a continuación:

PageBase.cs

1 public class PageBase : Page 2 { 3 private readonly NavigationHelper navigationHelper; 4 private IViewModelBase viewModel; 5 6 public PageBase() : base() 7 { 8 this.NavigationCacheMode = NavigationCacheMode.Required; 9 10 this.navigationHelper = new NavigationHelper(this); 11 this.navigationHelper.LoadState += this.NavigationHelper_LoadState; 12 this.navigationHelper.SaveState += this.NavigationHelper_SaveState; 13 } 14 15 16 /// <summary> 17 /// Gets the <see cref="NavigationHelper"/> associated with this <see cref="Page"/>. 18 /// </summary> 19 public NavigationHelper NavigationHelper 20 { 21 get { return this.navigationHelper; } 22 } 23 24 ///// <summary> 25 ///// Populates the page with content passed during navigation. Any saved state is also 26 ///// provided when recreating a page from a prior session. 27 ///// </summary> 28 ///// <param name="sender"> 29 ///// The source of the event; typically <see cref="NavigationHelper"/> 30 ///// </param> 31 ///// <param name="e">Event data that provides both the navigation parameter passed to 32 ///// <see cref="Frame.Navigate(Type, object)"/> when this page was initially requested and 33 ///// a dictionary of state preserved by this page during an earlier 34 ///// session. The state will be null the first time a page is visited.</param> 35 private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e) 36 { 37 this.viewModel.Load(); 38 39 //BackgroundAccessStatus status = BackgroundExecutionManager.GetAccessStatus(); 40 //if (status == BackgroundAccessStatus.Denied || status == BackgroundAccessStatus.Unspecified) 41 //{ 42 // await BackgroundExecutionManager.RequestAccessAsync(); 43 //} 44 } 45 46 ///// <summary> 47 ///// Preserves state associated with this page in case the application is suspended or the 48 ///// page is discarded from the navigation cache. Values must conform to the serialization 49 ///// requirements of <see cref="SuspensionManager.SessionState"/>. 50 ///// </summary> 51 ///// <param name="sender">The source of the event; typically <see cref="NavigationHelper"/></param> 52 ///// <param name="e">Event data that provides an empty dictionary to be populated with 53 ///// serializable state.</param> 54 private void NavigationHelper_SaveState(object sender, SaveStateEventArgs e) 55 { 56 // TODO: Save the unique state of the page here. 57 } 58 59 60 #region NavigationHelper registration 61 62 ///// <summary> 63 ///// The methods provided in this section are simply used to allow 64 ///// NavigationHelper to respond to the page's navigation methods. 65 ///// <para> 66 ///// Page specific logic should be placed in event handlers for the 67 ///// <see cref="NavigationHelper.LoadState"/> 68 ///// and <see cref="NavigationHelper.SaveState"/>. 69 ///// The navigation parameter is available in the LoadState method 70 ///// in addition to page state preserved during an earlier session. 71 ///// </para> 72 ///// </summary> 73 ///// <param name="e">Event data that describes how this page was reached.</param> 74 protected override void OnNavigatedTo(NavigationEventArgs e) 75 { 76 base.OnNavigatedTo(e); 77 78 this.viewModel = (IViewModelBase)this.DataContext; 79 80 this.navigationHelper.OnNavigatedTo(e); 81 } 82 83 protected override void OnNavigatedFrom(NavigationEventArgs e) 84 { 85 base.OnNavigatedFrom(e); 86 this.navigationHelper.OnNavigatedFrom(e); 87 } 88 89 #endregion 90 }

IViewModelBase.cs

1 public interface IViewModelBase 2 { 3 void Load(); 4 }

  • La clase PageBase permite la navegación entre páginas (NavigationHelper) que a su vez hace uso de la clase (SuspensionManager) y que van a permitir a nuestra aplicación:
    1. Mayor rapidez en la carga de cada página. Evitaremos incluir en el constructor de cada ViewModel la carga de datos. Ésta pasará a implementarse en el método “Load” indicado por el interface IViewModelBase. Por ello, cada ViewModel que creemos deberá implementar esta interfaz. Puede verse como la clase PageBase, en las líneas 37 y 78 se encargan de llevar a cabo todo esta labor.
    2. Mantener el estado de cada página para que en caso de que la aplicación quede suspendida pueda volver a ser activada en su estado anterior. Recordemos que ahora Windows Phone 8.1 no cierra las aplicaciones al volver hacia atrás (con el botón Back).
  • Modificar la página MainPage.xaml como se muestra a continuación para que pase a hacer uso de la clase PageBase:
1 <common:PageBase 2 x:Class="elGuerre.MainPage" 3 xmlns:common="using:elGuerre.Common" 4 ... 5 > 6 </common:PageBase>

  • Con todos estos cambios, la clase MainPage.xaml.cs quedará totalmente limpia. Simplemente heredando de PageBase.
1 public sealed partial class MainPage : PageBase 2 { 3 public MainPage() 4 { 5 this.InitializeComponent(); 6 } 7 }

 

  • Modificar la página MainPage.xaml nuevamente para hacer uso de los behaviors y vincular los eventos con los eventos RelayCommand de nuestro ViewModel:
1 ... 2 xmlns:i="using:Microsoft.Xaml.Interactivity" 3 xmlns:core="using:Microsoft.Xaml.Interactions.Core" 4 ... 5 <HubSection x:Uid="HubSection1" Header="SECTION 1" > 6 <DataTemplate> 7 <ListView 8 ItemsSource="{Binding Groups}" 9 IsItemClickEnabled="True" 10 ContinuumNavigationTransitionInfo.ExitElementContainer="True" 11 > 12 <ListView.ItemTemplate> 13 ... 14 </ListView.ItemTemplate> 15 16 <i:Interaction.Behaviors> 17 <i:BehaviorCollection> 18 <core:EventTriggerBehavior EventName="ItemClick"> 19 <core:InvokeCommandAction Command="{Binding ClickSectionCommand}" /> 20 <!--<core:CallMethodAction MethodName="Method1" TargetObject="{Binding Mode=OneWay}" />--> 21 </core:EventTriggerBehavior> 22 </i:BehaviorCollection> 23 </i:Interaction.Behaviors> 24 25 </ListView> 26 </DataTemplate> 27 </HubSection> 28 29 ... 30 31 <HubSection x:Uid="HubSection2" Header="SECTION 2" Width="Auto" > 32 <DataTemplate> 33 <GridView 34 ItemsSource="{Binding Groups[0].Items}" 35 AutomationProperties.AutomationId="ItemGridView" 36 AutomationProperties.Name="Items In Group" 37 ItemTemplate="{StaticResource Standard200x180TileItemTemplate}" 38 SelectionMode="None" 39 IsItemClickEnabled="True" 40 ContinuumNavigationTransitionInfo.ExitElementContainer="True"> 41 <GridView.ItemsPanel> 42 <ItemsPanelTemplate> 43 <ItemsWrapGrid /> 44 </ItemsPanelTemplate> 45 </GridView.ItemsPanel> 46 47 <i:Interaction.Behaviors> 48 <i:BehaviorCollection> 49 <core:EventTriggerBehavior EventName="ItemClick"> 50 <core:InvokeCommandAction Command="{Binding ClickItemCommand}" /> 51 </core:EventTriggerBehavior> 52 </i:BehaviorCollection> 53 </i:Interaction.Behaviors> 54 55 </GridView> 56 </DataTemplate> 57 </HubSection>

  • Nota: Se ha eliminado de los controles HubSection el DataContex con objeto de que los behabiors encuentren los RelayCommand en su ViewModel. Por lo que el DataContext es el siguiente teniendo como Source al contenedor de dependencias (SimpleIoC). De igual forma los ItemSource han sido modificados por el mismo motivo.
DataContext="{Binding Main, Mode=OneWay, Source={StaticResource Locator}}"

 

  • Como punto final. La implementación de MainViewModel es la siguiente:
1 public class MainViewModel : ViewModelBase, IMainViewModel 2 { 3 public const string TitlePropertyName = "Title"; 4 5 private string title = string.Empty; 6 private readonly IDataService dataService; 7 private NavigationHelper navHelper; 8 private ObservableCollection<SampleDataGroup> groups; 9 10 /// <summary> 11 /// Initializes a new instance of the MainViewModel class. 12 /// </summary> 13 public MainViewModel(IDataService dataService) 14 { 15 16 this.dataService = dataService; 17 18 // Tiempo de diseño 19 if (this.IsInDesignMode) 20 this.Load(); 21 } 22 23 public async void Load() 24 { 25 var data = await this.dataService.GetData(); 26 this.Groups = new ObservableCollection<SampleDataGroup>(data); 27 } 28 29 public ObservableCollection<SampleDataGroup> Groups 30 { 31 get { return this.groups; } 32 set { Set(() => Groups, ref this.groups, value); } 33 } 34 35 public string Title 36 { 37 get{ return title; } 38 set { Set(() => Title, ref this.title, value); } 39 } 40 41 public void Method1() 42 { 43 44 } 45 46 private RelayCommand<ItemClickEventArgs> clickSectionCommand; 47 public RelayCommand<ItemClickEventArgs> ClickSectionCommand 48 { 49 get 50 { 51 return clickSectionCommand 52 ?? (clickSectionCommand = new RelayCommand<ItemClickEventArgs>( 53 (e) => 54 { 55 var groupId = ((SampleDataGroup)e.ClickedItem).UniqueId; 56 this.navHelper = new NavigationHelper(); 57 this.navHelper.NavigateTo<SectionPage>(groupId); 58 })); 59 } 60 } 61 62 63 private RelayCommand<ItemClickEventArgs> clickItemCommand; 64 public RelayCommand<ItemClickEventArgs> ClickItemCommand 65 { 66 get 67 { 68 return clickItemCommand 69 ?? (clickItemCommand = new RelayCommand<ItemClickEventArgs>( 70 (e) => 71 { 72 var itemId = ((SampleDataItem)e.ClickedItem).UniqueId; 73 this.navHelper = new NavigationHelper(); 74 this.navHelper.NavigateTo<ItemPage>(itemId); 75 })); 76 } 77 } 78 }

  • Modificar las páginas SectionPage e ItemPage siguiendo estos mismo pasos creando igualmente los ViewModel correspondientes.
  • Si hemos optado por una aplicación Universal, sería necesario repetir los pasos únicamente para las páginas .XAML.

Tras estos pasos y algunos comentarios que encontraremos al analizar el código, no nos será nada complicado tener nuestro primer conjunto de buenas prácticas para la implementación de aplicaciones Universales y/o aplicaciones Windows Phone 8.1.  ¡Espero haber conseguido ese objetivo!

Saludos @Home

@JuanluElGuerre

TIP: Windows Phone 8 Update 3/GDR3 ( 8.0.10512.142). Con Preview for Developers sin esperas !!!

imageMuy buenas,

 

Ahora que tengo móvil nuevo y liberado, hay que actualizarlo y ponerlo al día.

Por si aún no lo sabéis, aquí está el update 3 (GDR3) para Windows Phone 8. Aún tardará en llegar hasta nuestros dispositivos, pero si eres desarrollador puedes optar por disponer de el mucho antes, sin necesidad de esperar a que llegue ese momento tan deseado.  Si es así, sigue estos pasos y tendrás el update listo en tu móvil en unos pocos minutos.

Aquí tenéis los pantallazos de  “mi delito”. Yo no salgo muy favorecido, pero bueno, tampoco soy el objetivo de este pequeño post.

Updates_1 Updates_2

Algunas de las mejoras que incorpora son estas:

  • App switcher: ”El aspa” (X) para cerrar las aplicaciones que se están ejecutando en “background” sin necesidad de estar volviendo hacia atrás continuamente.
  • Bloqueo de rotación de pantalla.
  • Driving Mode: Para evitar que te interrumpan mientras conduces. Configurado a partir del Bluetooth de tu coche.
  • Posibilidad de eliminar ficheros temporales del estorage.
  • Asignar tonos a distintos mensajes/notificaciones
  • Backups (en Cloud):
    • Lista de aplicaciones instaladas
    • Links favoritos de internet Explorer
    • Mensajes de texto
    • Fotos
  • Extras+info. Información Extra
  • Network+: Configuración avanzada para red
  • Otras mejoras.

Nota: Creo que también es un buen update para el ahorro de batería, Guiño

 

Y, para más detalle no dejeiss de echar un vistazo a este enlace.

A disfrutarlo. Saludos
@JuanluElGuerre

TIP: Galasoft MVVM Light para Visual Studio 2013

image Muy buenas,

En esta ocasión, le toca el turno a Windows Phone. ¿Por qué ahora? Pues porque quiero comenzar un pequeño proyecto personal y creo que este es el mejor camino para hacerlo, es decir, compartiéndolo con vosotros

Cuando hablamos de Windows Phone, Windows Store, WPF y Silverlight, hablamos implícitamente del patrón MVVM (Model View ViewModel). En cuanto a este patrón, Microsoft lo implementa como puede verse en este enlace, no obstante, existen numerosas opciones adicionales que facilitan la implementación del mismo mediante Toolkits, Plantillas y/o snippets. Por citar algunas:

Ninguna de ellas es mejor ni peor que la otra, si no que cada una tiene su propósito o está más indicada para un tipo de proyecto. Por ejemplo, Galasoft lo implementa de manera sencilla y bastante ligera y es uno de los más conocidos y usado en la comunidad de desarrolladores, sin embargo, Prism, está principalmente indicado para proyectos que requieran un alto nivel de modularidad y por tanto su uso es algo más complejo.

Por el momento, me decantaré por Galasoft, puesto que además de ser uno de los más reconocidos, es el que mejor conozco y del que mejor feedback tengo.

Pues bien, después de esta pequeña introducción, veamos los pasos para comenzar a trabajar con Galasoft MVVM Light y claro está, con Visual Studio 2013:

  • Descargamos el Toolkit: MVVM Light Toolkit y seguimos los pasos de instalación como puede verse aquí.
  • Desde Visual Studio 2013,  importamos los snnipets a partir de la ruta de instalación de “MVVM Light Tookit (“C:Program Files (x86)Laurent Bugnion (GalaSoft)Mvvm Light ToolkitSnippetsCSharp”)
  • Accedemos a la ruta “C:Program Files (x86)Laurent Bugnion (GalaSoft)Mvvm Light ToolkitVsix” donde se encuentra los instalables “.VSIX”.  El que nos interesa para Windows Phone 8, es “MvvmLight.VS2012.vsix”. Sin embargo, a día de hoy no está preparado para VS2013, así que tendremos que hacer algunos cambios:
    • Renombramos el fichero “MvvmLight.VS2012.vsix” por “MvvmLight.VS2012.vsix.zip” y lo descomprimimos.
    • A continuación editamos el fichero “extension.vsixmanifest”:

image

    • Sustituimos los números de versión en los “InstallationTarget” cambiando los valores [11.0, 12.0) por  [12.0, 13.0) tal y como se muestra a continuación:

image

    • Volvemos a comprimir en formato “.zip” todo el contenido antes descomprimido, pero ahora con el nombre “MvvmLight.VS2013” y cambiamos la extensión “.zip” por “.vix”, de manera que finalmente quede como “MvvmLight.VS2013.vsix”.
    • Hacemos doble click e instalamos.

image

    • Y….., todo listo, ya tenemos nuestra plantilla de proyecto incluida en Visual Studio 2013.

image

¡Preparados para comenzar con nuestra aplicación para Windows Phone con Visual Studio 2013 y MVVM!

 

Saludos
@JuanluElGuerre

WCF as REST. Deeping as usual: SEO Routing, Newtonsoft.Json and more !!

Muy buenas,

Entre patines, mountaintbike, despedidas de soltero, resfriado/virus, playa y viajes de verano, saco un hueco para comentar este tema que tenía en borrador y que se estaba haciendo de rogar, es decir, como exponer servicios WCF como REST, haciendo uso de Routing para el enrutamiento basado en SEO y otras buenas prácticas al respecto.

Aunque la recomendación para la creación de nuevos servicios REST es WebApi, existirán ocasiones en la que esto no sea posible, para estos casos, he aquí las pautas a seguir a partir de nuestros servicios WCF:

1) Incluimos en los proyectos .NET la referencia a la dll “System.Web.Routing.dll” y “System.ServiceModel.Activation”.

2) Establecemos la compatibilidad ASP.NET en los servicios:

Incluimos en el Service: [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

Incluimos en el web.config, en la sección “system.serviceModel”:

   1: <serviceHostingEnvironment

   2:    aspNetCompatibilityEnabled="true"

   3:    multipleSiteBindingsEnabled="true" />

3) Añadimos a nuestro proyecto un fichero “global.asax” e ncluimos en el método “Application_Start” instrucciones similares a las siguientes para cada uno de los servicios a enrutar:
   1: RouteTable.Routes.Add(new ServiceRoute("", new WebServiceHostFactory(), typeof(Service1)));

   2: RouteTable.Routes.Add(new ServiceRoute("historial", new WebServiceHostFactory(), typeof(HistorialService)));

   3: RouteTable.Routes.Add(new ServiceRoute("private/buzon", new WebServiceHostFactory(), typeof(BuzonService)));

4) Modificamos los contracts asegurando la correcta “UriTemplate”:

   a) Ejemplo GET:

   1: [OperationContract]

   2: [WebGet(    

   3:     UriTemplate = "?data={value}",

   4:     ResponseFormat = WebMessageFormat.Json,

   5:     RequestFormat = WebMessageFormat.Json)]

   6: string GetData(int value);

   b) Ejemplo 2 GET:

   1: [OperationContract]

   2: [WebGet(    

   3:     UriTemplate = "{value}",

   4:     ResponseFormat = WebMessageFormat.Json,

   5:     RequestFormat = WebMessageFormat.Json)]

   6: string GetData(string value);

   c) Ejemplo PUT
   1: [OperationContract]

   2: [WebInvoke(

   3:     Method = "POST",    

   4:     UriTemplate = "",

   5:     BodyStyle = WebMessageBodyStyle.WrappedRequest,

   6:     ResponseFormat = WebMessageFormat.Json,

   7:     RequestFormat = WebMessageFormat.Json)]

   8: Stream Enviar(int IdMensaje, string Respuesta);

5) En los ficheros “.svc” de los servicios incluimos la siguiente instrucción añadiéndola a la existente generada por defecto:

   1: <%@ ServiceHost ...  Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>

6) Incluimos en el web.config la siguiente configuración con objeto de poder navegar desde nuestro browser a la definición de los servicios Rest:

   1: <sectionGroup

   2:     name="system.serviceModel"

   3:     type="System.ServiceModel.Configuration.ServiceModelSectionGroup, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">

   4:     <section name="standardEndpoints"

   5:         type="System.ServiceModel.Configuration.StandardEndpointsSection, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>

   6: </sectionGroup>

Dentro de la sección “System.ServiceModel”:
   1: <standardEndpoints>  

   2:     <webHttpEndpoint>    

   3:         <standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true">    

   4:         </standardEndpoint>  

   5:     </webHttpEndpoint>

   6: </standardEndpoints>

Para poder acceder a la información, bastará con incluir el sufijo “/help” a la url de cada servicio REST, de manera que la información obtenida sea similar a la siguiente:

image

imageimage

Nota: La ruta completa “SEO” se compone por la suma de los tres siguiente elementos:

  • Servidor: HTTP(S)://<SITE | DIRECTORIO VIRUTAL>[:PUERTO]/
  • Routing: “”
  • UriTemplate “{value}”, o, “?data={value}”, según el método del servicio a utilizar.

    De manera que la url completa sería: http://localhost/WcfService1/1

     

    7) [Opcional] Sólo en caso de publicar el servicio REST en  IIS6: Configuramos el Web Site o el Directorio Virtual del IIS como sigue:

    imageimage

    Donde la extensión será “C:WindowsMicrosoft.NETFrameworkv4.0.30319aspnet_isapi.dll” o “C:WindowsMicrosoft.NETFramework64v4.0.30319aspnet_isapi.dll”, asegurando que el check  “Comprobar si el archivo existe” está desmarcado.

    8) Uso de entidades JSON

    Una vez el servicio ya está adaptado a un servicio REST, ahora necesitamos revisar o crear o modificar las entidades de tipo JSON para nuestros métodos. Para ello utilizaremos la dll “Newtonsoft.Json”  que puede ser descargada desde aquí, o incluso desde NuGet: “PM> Install-Package Newtonsoft.Json”.

    Esta dll nos permitirá trasformar nuestras entidades en JSON, transformar String del tipo JSON en entidades y vicebersa, entre otras cosas, facilitando toda esta labor de conversión/transformación de una manera más fácil a como se podría hacer con la socialización por defecto a JSON de .NET, por tanto su uso es recomendado.

    Cada método debería retornar un entidad JSON con la información esperada, sin embargo también se hace necesario, en ocasiones, el retorno de códigos y descripciones de error. En este caso, la recomendación sería utilizar una entidad común de la forma siguiente, donde en cado de que Error=0, Mensaje será NULL y Data tendrá el dato esperado.

       1: [JsonObject]

       2: public class ResponseJson<T> 

       3: {

       4:     [JsonProperty]

       5:     public int Error { get; set; }

       6:     

       7:     [JsonProperty]

       8:     public string Messaje { get; set; }     

       9:     

      10:     [JsonProperty]

      11:     public T Data { get; set; }

      12:  }

    Adicionalmente, si tenemos otro tipo de requisitos que no se adapte a este formato, siempre podemos retornar un “Stream”. Podría ser un “String”, pero, en tal caso el JSon resultante no es correcto, incluyendo el carácter “”” con carácter especial “””:

       1: [JsonObject]

       2: public class ResponseJson<T> 

       3: {    

       4:     [JsonProperty]    

       5:     public int Error { get; set; }    

       6:  

       7:     [JsonProperty]    

       8:     public T Mensaje { get; set; }

       9: }

    En este caso, si Error=0, Mensaje tomará el dato esperado, mientras que si Error != 0, Mensaje tendrá un String con la descripción del error. En el siguiente snippet podemos ver un ejemplo de como quedaría nuestro método en términos generales para este último caso:

       1: public Stream Consultar(int page)

       2: {

       3:     int retCode = 0;

       4:     object retMessage = null;

       5:  

       6:     WebOperationContext.Current.OutgoingResponse.ContentType = "application/json";

       7:  

       8:     try

       9:     {         

      10:         //

      11:         // Procesar y obtener objeto obj1

      12:         //

      13:  

      14:         retMessage = new PageResponseJson<Entidad1Json>()

      15:         {

      16:             Pagina = 1,

      17:             PaginasTotales = obj1.TotalPages,

      18:             RegistrosTotales = obj1.TotalRecords,

      19:             Listado = obj1.Listado

      20:         };

      21:  

      22:     }

      23:     catch (Exception ex)

      24:     {

      25:         ExceptionHelper.HandleException(ex);

      26:  

      27:         retCode = -1;

      28:         retMessage = ex.Message;

      29:     }

      30:  

      31:     var result = new

      32:     {

      33:         Error = retCode,

      34:         Mensaje = retMessage

      35:     };

      36:  

      37:     return new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(result)));

      38: }

    Donde:

    • PageResponseJson, es un entidad Json para el retorno de listados paginados,
    • ExceptionHelper, es simplemente una clase para el tratamiento de excepciones con Entlib.

    Nota: El hecho de retornar un “Stream”, se debe a que, al devolver un objeto que puede ser de dos tipos, la serialización/deserialización falla debido a que la exposición de un servicio WCF como REST no serializa debidamente el string JSON resultante. Bien generando el string Json con caracteres especiales “” o bien, no realizándose la deserialización al tipo a retornar de manera correcta o incluso no funcionando. Otra solución, pasaría por crear el conversor adecuado, sin embargo, considero más eficiente retornar el Stream evitando así una conversión adicional.

    Con todo lo anterior en mente, nuestros servicios WCF serán servicios REST/JSON pudiendo ser consumidos 100% como tales cumpliendo además todas las especificaciones al respecto.

    Recodemos también que, Fiddler, va a permitirnos asegurar que nuestros JSON en las peticiones HTTP son totalmente correctos y se adaptan a las especificaciones requeridas.

    ¡Ahora si que dará que pensar: WCF as REST o WebAPI! ¿Tienes un departamento que controla 100% de WCF y/o WebApi, no quieres ser usada? ¿Por qué solución optarías? Si aún te quedan dudas, echa un vistazo a este link de MSDN: http://msdn.microsoft.com/en-us/library/jj823172.aspx, o incluso a este otro si tienes ganas de saber más sobre todo este tema: http://www.codeproject.com/Articles/341414/WCF-or-ASP-NET-Web-APIs-My-two-cents-on-the-subjec.

     

    Feliz verano/vacaciones a Tod@s
    Saludos @Higuera la Real
    JuanluElGuerre

  • Controlando el Clúster de HDInsight (Hadoop) programáticamente y ahorrando costes: Con C# !!!

    imageMuy buenas,

    Después de varios posts en los que hemos estado hablando de HDInsight (Hadoop de Microsoft), aun a pesar de quedarnos pendientes muchos puntos sobre los que hablar, en esta ocasión me gustaría profundizar en la gestión automática de HDInsight.

     

    La importancia de este tema radica principalmente en los siguientes puntos:

    • El coste de HDInsight, que dependerá del tiempo que tendremos activas las máquinas de Windows Azure. Recordemos que los nodos necesarios para trabajar con el clúster de HDInsight son como mínimo 2 máquinas virtuales: Una como “Head Node” A4  [L] (8 x 1.6GHz CPU, 14GB RAM, 2,040GB Storage) y otra como “Compute Node” A3 [XL] (4 x 1.6GHz CPU, 7GB RAM, 1,000GB Storage).
    • Ejecución temporal de Jobs

    Teniendo en cuenta estos puntos podemos:

    • Hacer un mayor aprovechamiento de HDInsight
    • Conseguir una reducción de los costes o, incluso,
    • Por el mismo coste, obtener una mayor velocidad de proceso, aprovisionando más máquinas durante el menor tiempo posible.

    Los pasos para conseguirlo programáticamente con C#:

    1) Crear un certificado e instalarlo en local con el fin de poder hacer el despliegue automático:

    "C:Program Files (x86)Microsoft SDKsWindowsv7.1ABinmakecert" -sky exchange -r -n "CN=WAHDInsight" -pe -a sha1 -len 2048 -ss My "WAHDInsight.cer"

     

    2) Crear un proyecto de tipo consola.

    3) Añadir al proyecto las referencias “Microsoft .NET API For Windows Azure HDInsight Cluster Management”  (versión actual: 0.8.4903.27316).

    4) Incluir el siguiente código:

       1: static void Main(string[] args)

       2: {

       3:     // Get certificate

       4:     var store = new X509Store();

       5:     store.Open(OpenFlags.ReadOnly);

       6:     var cert = store.Certificates.Cast<X509Certificate2>().First(

       7:         item => item.Thumbprint.Equals(Subscription.Thumbprint, StringComparison.InvariantCultureIgnoreCase));

       8:  

       9:  

      10:     // Create client object to connect to Azure

      11:     var client = new ClusterProvisioningClient(Subscription.SubscriptionId, cert);

      12:  

      13:  

      14:     // Specify details of the cluster to create

      15:     var clusterInfo = new HDInsightClusterCreationDetails();

      16:     clusterInfo.Name = Cluster.Name;

      17:     clusterInfo.Location = Cluster.Location;

      18:     clusterInfo.DefaultStorageAccountName = Subscription.StorageAccountName;

      19:     clusterInfo.DefaultStorageAccountKey = Subscription.StorageAccountKey;

      20:     clusterInfo.DefaultStorageContainer = Subscription.StorageContainer;

      21:     clusterInfo.UserName = Subscription.UserName;

      22:     clusterInfo.Password = Subscription.Password;

      23:     clusterInfo.ClusterSizeInNodes = Cluster.Size;

      24:             

      25:     var cluster = client.CreateCluster(clusterInfo);

      26:     Console.WriteLine("Created cluster: {0}", cluster.ConnectionUrl);

      27: }

    5) Crear un fichero de configuración “App.config” e incluir los siguientes parámetros:

       1: <appSettings>

       2:   <add key="SubscriptionId" value="###SUBSCRIPTION_ID###"/>

       3:   <add key="Thumbprint" value="‎###CERTIFICATE_THUMBPRINT###"/>

       4:  

       5:   <add key="StorageAccountName" value="###AZURE_STORAGE_NAME###.blob.core.windows.net"/>

       6:   <add key="StorageAccountKey" value="###AZURE_STORAGE_KEY###"/>

       7:   <add key="StorageContainer" value="###AZURE_STORAGE_CONTAINER###"/>

       8:  

       9:   <add key="UserName" value="admin"/>

      10:   <add key="Password" value="###PASSWORD_CLUSTER_HDINSIGHT###"/>

      11: </appSettings>

    6) Tras unos minutos, el Cluster estará creado y podremos trabajar con él tal y como hemos visto en post anteriores.

    image

    image

    7) Finalizada las tareas a realizar con el Cluster, borramos el cluster para evitar el consumo innecesario de €€€ por tener activas las Maquinas virtuales que componen el Cluster.

    Nota: Recordemos que se factura por hora de VM aunque estas no estén trabajando.

       1: // Get certificate

       2: var store = new X509Store();

       3: store.Open(OpenFlags.ReadOnly);

       4: var cert = store.Certificates.Cast<;X509Certificate2>().First(

       5:     item =>; item.Thumbprint.Equals(Subscription.Thumbprint, StringComparison.InvariantCultureIgnoreCase));

       6:  

       7:  

       8: // Create client object to connect to Azure

       9: var client = new ClusterProvisioningClient(Subscription.SubscriptionId, cert);

      10: 

      11: // Remove cluster "mycluster"

      12: client.DeleteCluster(Cluster.Name);

     

    Un ejemplo completo del código podéis encontrarlo aquí.

    Después de esto, nadie puede decir que Windows Azure no es Auto-Scalable, Guiño

     

    Continuaremos programando para HDInsight…

     

    Saludos and happy HDInsight programming  !

    Juanlu, ElGuerre