domingo, 6 de septiembre de 2015

[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.

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 }

La utilizaremos de cualquier de las dos formas siguientes:



  1. list.DistinctBy(item => item.Id);

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

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


 


Saludos
Juanlu

Etiquetas: ,


martes, 21 de julio de 2015

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

image

Unit Test

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

Etiquetas: , ,


jueves, 2 de julio de 2015

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 SDKs\Windows Phone\v8.1\Tools”) 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

Etiquetas: , , , ,


domingo, 7 de junio de 2015

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.

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

Etiquetas: , , ,


domingo, 24 de mayo de 2015

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:\Users\juan.luis.guerrero\AppData\Local\Microsoft\VisualStudioEmulator\Android\Containers\Local\Devices\vhd\5_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

Etiquetas: , ,


jueves, 22 de agosto de 2013

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:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll” o “C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_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


  • Etiquetas: ,


    This page is powered by Blogger. Isn't yours?