miércoles, 19 de abril de 2017

Xamarin, WCF, Stored Procedures y Entity Framework (Parte 2)

¡Hola! Bienvenido a la parte 2 de la serie Xamarin, WCF, Stored Procedures y Entity Framework en la que crearemos un servicio WCF que combinará las tecnologías de Entity Framework y JSON a fin de recuperar la información de una base de datos.

En la parte 1 (disponible aquí) creamos una base de datos con información de Artistas, Canciones y Conciertos (así como su detalle) junto con los procedimientos almacenados a fin de realizar las operaciones CRUD.

Antes de comenzar con el tema de hoy, quiero agradecer enormemente a todos los que se tomaron la molestia de leer la primer entrega 😃. Es motivante saber que la respuesta ha sido muy grata e incluso he recibido sugerencias y comentarios que permitirán mejorar futuras sesiones :) Los comentarios han sido positivos y la retroalimentación muy buena. Por ejemplo, en uno de los grupos de Facebook me solicitaron hablar de WebApi, otra sugerencia fue hablar de seguridad con JWT y finalmente, hacer la versión en video de estas sesiones. Dado que son muy buenas ideas, las voy a tener en cuenta, esperen su implementación en próximas entregas... Lo prometo

Espero que estas sesiones, aunque son algo largas, sean de utilidad para sus proyectos. Por otra parte, disculpen si me extiendo en ocasiones jeje, pero es que hay tanto de qué hablar que quiero compartir lo más que se pueda. Así que gracias por sus mensajes y sigan comentando de qué otros temas quisieran que hablara en el blog, así aprendemos entre todos 😀

Continuamos entonces. El repositorio de GitHub donde actualizaré el proyecto poco a poco se encuentra aquí (ya incluye la parte 2, por supuesto :) )

Consideraciones si descargas y quieres ejecutar la solución inmediatamente:

  • Debes haber realizado la práctica 1, es decir, que los procedimientos almacenados estén en la base de datos.
  • Debes modificar la cadena de conexión en Web.config para incluir los datos de tu servidor, usuario y contraseña.
  • Se recomienda limpiar la solución en Visual Studio (Build --> Clean Solution)
  • Debes restaurar los paquetes Nuget del proyecto (se debería realizar al compilar y ejecutar la aplicación).


En cambio, si quieres iniciar el proyecto desde cero (lo recomendable, para que aprendas, jeje), ¡pues continuamos donde nos quedamos!

Para esta sesión utilizaremos Visual Studio. Cualquier versión (2012, 2015, 2017) te va a servir. En este caso, yo lo hice con la versión Community 2017.

A lo largo de esta sesión analizaremos varios conceptos, a saber:

  • Servicios WCF
  • EntityFramework
  • Serialización JSON

Comenzamos describiendo el primer concepto. Un Servicio WCF (Windows Communication Foundation) es un framework de Microsoft que permite crear un servicio que expone un web endpoint (extremo) para enviar datos en algún formato de salida (típicamente JSON, pero existen otros, como XML, TXT, CSV...) hacia otro extremo, que normalmente es una aplicación que consume dicho servicio. Por lo general, este servicio es hospedado en un servidor (local o en la nube) para que esté disponible para su consumo por parte de una o varias aplicaciones. Algunas ventajas de este servicio WCF son:


  • Seguridad: Toda la información de conexión a la fuente de información se queda en el servidor donde esté alojado el servicio, por lo que no se expone riesgo de seguridad alguno al distribuir las aplicaciones cliente que consumen el servicio. Para maximiza la seguridad, es posible encriptar los datos que viajan por la red (¡luego lo hacemos en una futura entrada si quieren!), así como autentificar a los usuarios para que solo aquellos autorizados accedan a la información, pero esto depende del nivel de seguridad que deseemos implementar. 

  • Diversidad: Prácticamente cualquier tipo de aplicación puede consumir el servicio: aplicaciones de consola, IoT, móviles, web, de escritorio...

  • Independencia de lenguaje: No importa el lenguaje de programación en que estén desarrolladas las aplicaciones cliente que consumen el servicio (por ejemplo, el servicio WCF puede estar creado en C#/VB .NET y la aplicación cliente desarrollada en PHP, Java o incluso C#).

  • Independencia de plataforma: Si bien WCF es un modelo propio creado por Microsoft, esto no significa que las aplicaciones cliente que consuman el servicio tengan que serlo. Se puede escribir una aplicación en Java/Android/PHP/iOS que perfectamente puede obtener los datos del servicio.

Por el momento, dejamos la teoría ahí y vamos a Visual Studio para comenzar con el proyecto.

Crea un Nuevo proyecto de tipo Aplicación de Servicios WCF (en la categoría WCF) llamado ServicioMusicaWCF.



Por defecto, un proyecto de servicio WCF consta de varios elementos:



  • IService1.cs es una interfaz que define la estructura del servicio
  • Service1.svc es el servicio en sí y contiene una clase que implementa la interfaz con su funcionalidad
  • Web.config es el archivo de configuración típico de proyectos web 

Por medio del Explorador de Soluciones, elimina tanto la interfaz IService1.cs como el servicio Service1.svc. Ahora agrega al proyecto un ServicioWCF llamado ServicioMusica (en la categoría Web). Esto creará una interfaz llamada IServicioMusica.cs y el servicio ServicioMusica.svc con su clase.


Vamos a comenzar codificando la interfaz. Recuerda que una interfaz es un "contrato", ¿y qué pasa con los contratos?


Pues eso. Si una clase implementa una interfaz, todos los métodos que se definan en la interfaz deberán ser incluidos (y codificados) en la clase siguiendo las reglas establecidas (definición). Entonces, en la interfaz IServicioMusica definiremos los métodos que nuestro servicio ServicioMusica va a implementar posteriormente. Básicamente, estos métodos llaman a los 19 procedimientos almacenados definidos en la parte 1. El código es el siguiente:

using System;
using System.ServiceModel;

namespace ServicioMusicaWCF
{
    [ServiceContract]
    public interface IServicioMusica
    {
        [OperationContract]
        string ObtenerArtistas();

        [OperationContract]
        string BuscarArtista(int id);

        [OperationContract]
        string AgregarArtista(string nombre, string pais);

        [OperationContract]
        string ModificarArtista(int id, string nombre, string pais);

        [OperationContract]
        string EliminarArtista(int id);

        [OperationContract]
        string ObtenerCanciones();

        [OperationContract]
        string BuscarCancion(int id);

        [OperationContract]
        string AgregarCancion(string titulo, string duracion);

        [OperationContract]
        string ModificarCancion(int id, string titulo, string duracion);

        [OperationContract]
        string EliminarCancion(int id);

        [OperationContract]
        string ObtenerConciertos();

        [OperationContract]
        string BuscarConcierto(int id);

        [OperationContract]
        string AgregarConcierto(int idArtista, string lugar, DateTime fecha);

        [OperationContract]
        string ModificarConcierto(int id, int idArtista, string lugar, DateTime fecha);

        [OperationContract]
        string EliminarConcierto(int id);

        [OperationContract]
        string ObtenerDetallesConcierto(int idConcierto);

        [OperationContract]
        string AgregarDetalleConcierto(int idConcierto, int idCancion, int orden);

        [OperationContract]
        string ModificarDetalleConcierto(int idConcierto, int idCancion, int orden);

        [OperationContract]
        string EliminarDetalleConcierto(int idConcierto, int idCancion);
    }
}


Observamos lo siguiente:
  • Cada método contiene el atributo OperationContract, que significa que estará disponible para ser consumido por una aplicación externa (es decir, que aparecerá listado en el servicio). Si no lo tuviera, no aparecería en la definición del servicio.

  • Cada método se define exactamente igual que la llamada a su procedimiento almacenado respectivo (por ejemplo, el método BuscarArtista recibe de parámetro un entero e implementará el procedimiento almacenado Procedimiento_BuscarArtista, que también recibe un entero. La única diferencia es que aquí no necesitamos el parámetro de salida mensaje, más bien ése lo agregaremos nosotros en la implementación.

  • Cada método devuelve un string porque en la implementación convertiremos los resultados devueltos por el procedimiento almacenado al formato de salida JSON, que es muy utilizado hoy en día en aplicaciones web, móviles y demás debido a que es ligero, independiente de plataforma y sencillo de serializar/deserializar.

Antes de implementar el servicio, vamos a definir cómo nos vamos a conectar al origen de datos. Una forma sería a través del espacio de nombres System.Data (similar a como lo expliqué aquí, utilizando DataSets, DataTables, SQLConnections...) pero esa forma ya ha quedado un poco obsoleta. Una alternativa muy popular hoy en día es a través de EntityFramework, que es el segundo concepto que vamos a explicar en esta sesión.

EntityFramework es un ORM (Object-Relational Mapping) para la plataforma .NET que permite (como el tipo lo sugiere) mapear los tipos (Tablas/campos) de una base de datos a tipos definidos en un lenguaje de programación orientado a objetos (Clases/propiedades), maximizando la portabilidad y manteniendo la lógica entre la fuente de datos y la aplicación.

Por ejemplo, la tabla Artistas con los campos Id (int), Nombre (varchar) y Pais (varchar) se puede mapear a una clase (en EF típicamente la conocemos como Entidad) Artistas con las propiedades Id (int), Nombre (string) y Pais (string).

Pero el mapeo realizado por EF va más allá de una tabla/clase. También se crean los elementos necesarios para modelar llaves primarias, relaciones entre tablas (por medio de las llaves foráneas), procedimientos almacenados y más elementos.

El modelado (mapeo) es automatizado después de realizar ciertos pasos, que son los que vamos a realizar a continuación.

Agregamos un nuevo elemento a nuestro proyecto de tipo ADO .NET Entity Data Model (se encuentra en la categoría Datos) y lo llamaremos ModeloMusica.


Esto inicializará un asistente (wizard). En primer lugar, selecciona EF Designer desde base de datos y da clic en Siguiente.


Da clic en Nueva conexion.



Ahora selecciona Microsoft SQL Server en el Origen de datos y da clic en Aceptar.



En Propiedades de la conexión debes especificar varios datos en el siguiente orden:


  • Primero, el nombre del servidor (esto lo obtienes de tu conexión a SQL Server), que típicamente es el nombre de tu equipo. Si estás usando SQL Server Express, seguramente tendrás que agregar la instancia (por ejemplo equipo\SQLExpress).

  • A continuación, selecciona Autenticación de SQL Server (eso significa que debes tener un usuario en SQL Server).

  • Introduce el nombre de usuario y contraseña de tu usuario y marca la opción Guardar mi contraseña.

  • Selecciona la base de datos Musica.

  • Verifica que la conexión se puede realizar dando clic en el botón Probar conexión. Si funciona, da clic en Aceptar.

  • Resumen:


Ahora selecciona la opción Si, incluir datos confidenciales en la cadena de conexión. Además, marca la casilla de Guardar configuración en Web.Config y coloca el nombre MusicaEntidades. Da clic en Siguiente.



Selecciona la versión EntityFramework 6.x y da clic en Siguiente.



Ahora selecciona las tablas y procedimientos almacenados que se obtienen al realizar la conexión a la base de datos. Verifica que solo las 4 tablas y los 19 procedimientos almacenados creados estén seleccionados. Marca las opciones Incluir columnas de clave externa e Importar procedimientos almacenados y funciones seleccionados (pero NO marques la opción de Poner en plural o singular los nombres de objeto). El nombre del espacio de nombres será MusicaModelo. Da clic en Finalizar.


Si te aparece una advertencia de ejecución de una plantilla de texto, selecciona la opción No mostrar este mensaje de nuevo y da clic en Aceptar. Descuida, todo lo que estamos realizando es una operación segura.



Una vez concluida la importación de elementos se habrá creado un elemento llamado ModeloMusica.edmx, el cual muestra el modelo generado a partir de tu base de datos, es decir, se ha creado un mapeado a entidades (clases) dentro del proyecto.



Compara el diagrama de este modelo con el diagrama de tu base de datos que generamos en la sesión anterior:



¡Es igualito! Observa como cada tabla está incluida como una entidad, como cada campo es ahora una propiedad y cómo también se han incluido las relaciones entre tablas y su cardinalidad (ejemplo: Un Artista puede aparecer en muchos Conciertos).

Un punto importante. Si recuerdas en uno de los pasos aceptamos que la cadena de conexión fuera almacenada en Web.config. Bien, pues si abrimos dicho archivo, nos vamos a encontrar con lo siguiente:


EntityFramework agrega un nuevo elemento en la sección connectionStrings, además de la configuración del propio EF, indicando el proveedor de datos (SQL Server). Si avanzamos un poco más a la derecha en la conexión MusicaEntidades, encontraremos la información específica de la cadena de conexión: Data Source, Initial Catalog, User Id y Password.


Recuerda que si descargaste el proyecto para que tu aplicación funcione tienes que editar esta cadena de conexión, modificando los valores:

  • Data Source (en vez de icebeam, el nombre de tu servidor/equipo)
  • Initial Catalog (solo si tu base de datos se llamara diferente)
  • User Id (coloca tu usuario)
  • Password (coloca su password)

(Todo esto asumiendo que hiciste la práctica 1 y que ya tienes los procedimientos almacenados en tu base de datos).

Otro elemento muy importante y del cual hablaremos posteriormente son las propiedades de navegación, pues permite obtener un acceso rápido a elementos relacionados por llave foránea. Por ejemplo, la entidad Concierto contiene Id, IdArtista, Lugar, Fecha y también un objeto Artista, lo cual permitirá obtener el Nombre y Pais del Artista de un Concierto específico (tal como si hubiéramos hecho una consulta Join en SQL). Luego detallaremos este punto, pues es esencialmente útil en LINQ al simplificar la manera de escribir las consultas.

El archivo ModeloMusica.edmx es en realidad un archivo XML que contiene 3 elementos:

  • CSDL (El modelo conceptual)
  • SSDL (El modelo lógico)
  • MSL (El mapeo o asignaciones entre ambos modelos)

Abre el archivo ModeloMusica.edmx y da clic en el menú Ver --> Otras ventanas --> Explorador de Entity Data Model. Observa que en la sección Tipos de Entidad aparecen las tablas (si las abres, verás las propiedades que contienen).



¿Y dónde quedaron los procedimientos almacenados? Pues se encuentran en la sección Importación de funciones. Observa que también es posible abrir cada procedimiento para ver los parámetros de cada uno.


Cuando los procedimientos almacenados fueron mapeados al EntityFramework, se crearon entidades adicionales que pueden ser optimizadas a fin de tener un modelo más ligero. Este paso hay que hacerlo por cada procedimiento almacenado de búsqueda u obtener mapeado (7 en total), pero es sencillo.

Da doble clic en Procedimiento_BuscarArtista (o clic derecho sobre el procedimiento y seleccionando Editar). En la sección Devuelve una colección de selecciona Entidades y luego elige Artistas (por defecto aparece seleccionado el tipo Complejo con Procedimiento_BuscarArtista_Result, el cual es idéntico a Artistas, por lo que no necesitamos crear este tipo adicional a fin de optimizar el modelo). Da clic en Aceptar.


Repite este paso para los siguientes procedimientos y tipos:


  • Procedimiento_BuscarCanciones --> Entidades --> Canciones.
  • Procedimiento_BuscarConciertos --> Entidades --> Conciertos.
  • Procedimiento_BuscarDetallesConcierto --> Entidades --> DetallesConcierto.
  • Procedimiento_ObtenerArtistas --> Entidades --> Artistas
  • Procedimiento_ObtenerCanciones --> Entidades --> Canciones.
  • Procedimiento_ObtenerConciertos --> Entidades --> Conciertos.

Para el resto de los procedimientos (Agregar, Modificar y Eliminar) no hay que cambiar nada pero sí vamos a crear una clase que incluya un entero y una cadena y almacenaremos su valor ahí. Si recuerdas, estos procedimientos almacenados devuelven un 1 o 0 en caso de éxito/fracaso respectivamente, además de un mensaje (cadena de texto) indicando lo mismo.

No olvides guardar el modelo.

Agrega al proyecto una nueva clase llamada ResultadoSQL, que define 2 propiedades: Valor y Mensaje.



namespace ServicioMusicaWCF
{
    public class ResultadoSQL
    {
        public int Valor { get; set; }
        public string Mensaje { get; set; }
    }
}


Como necesitamos una salida en formato JSON, vamos a hacer uso de un paquete muy popular, poderoso y sencillo de utilizar en el mundo de C#: Newtonsoft.Json. Da clic derecho sobre el proyecto, selecciona Administrar paquetes Nuget y agrega dicho paquete (en este caso estoy usando la versión 10.0.2).


Debido a que vamos a serializar en formato JSON, necesitamos hacer una "ligera" modificación en nuestro Modelo (EntityFramework). En el Explorador de Soluciones expande ModeloMusica.edmx; a continuación, expande ModeloMusica.tt y abre los 4 archivos que se refieren a las entidades: Artistas.cs, Canciones.cs, Conciertos.cs y DetallesConciertos.cs


Lo que tenemos que hacer es agregar el atributo Serializable a cada Entidad, para que pueda ser convertida a otro formato y enviarla por la red. Además, necesitamos agregar el atributo JsonIgnore a todos aquellos elementos que NO pertenecen a la tabla (los elementos marcados como virtuales e ICollection). Observa las imágenes de cómo debe quedar cada entidad:

Serializable arriba de la clase Artistas y JsonIgnore arriba de la colección Conciertos.


Serializable arriba de la clase Canciones y JsonIgnore arriba de la colección DetallesConcierto.


Serializable arriba de la clase Conciertos y JsonIgnore arriba de las colecciones Artistas y DetallesConciertos.


Serializable arriba de la clase DetallesConcierto y JsonIgnore arriba de las colecciones Conciertos y Canciones.

NOTA: En caso de modificar el modelo, las 4 entidades se generarán automáticamente y desafortuadamente tendrás que rehacer este paso de agregar los atributos nuevamente. Pero si en teoría no modificamos nuestro modelo de datos, no tendríamos por qué modificar las entidades nuevamente.

¿Por qué agregamos la propiedad JsonIgnore a las colecciones virtuales? Porque el paquete Newtonsoft.Json detecta que hay referencias circulares entre las entidades (por ejemplo, un Artista tiene la propiedad Conciertos y a su vez un Concierto tiene un Artista, a esto en EntityFramework se le conoce como Propiedades de Navegación), por lo que la serialización no se puede realizar. De esta manera, marcando las propiedades que deben ignorarse, evitamos conflictos al momento de serializar. Es un paso adicional que debemos hacer si queremos aprovechar este paquete junto con EntityFramework.

Ahora vamos a modificar el servicio que vamos a exponer para que las aplicaciones interactúen con la base de datos a través de la implementación de métodos. Para ello, abrimos ServicioMusica.svc, la cual implementará la interfaz IServicioMusica que definimos previamente, es decir, codificaremos cada uno de los 19 métodos. Explicaré 3 de ellos, pues el resto son similares. 

Primero, veamos el código del método AgregarArtista:

public string AgregarArtista(string nombre, string pais)
{
  int valor;
  var mensaje = new ObjectParameter("mensaje", typeof(string));

  using (var conexion = new MusicaEntidades())
  {
    valor = conexion.Procedimiento_AgregarArtista(nombre, pais, mensaje);
    return JsonConvert.SerializeObject(new ResultadoSQL() { Valor = valor, Mensaje = mensaje.Value.ToString() });
  }
}

  • Línea 1: La definición del método AgregarArtista (idéntica a como se definió en la interfaz).
  • Línea 3: La variable valor almacenará el id del registro insertado (lo devuelve el procedimiento almacenado, ¿recuerdas?)
  • Línea 4: El procedimiento almacenado requiere un parámetro de salida (Output). Para ello, en EntityFramework se declara un System.Data.Entity.Core.Objects.ObjectParameter con el nombre del parámetro (mensaje) y el tipo de dato (string).
  • Línea 6: El contexto de datos (la conexión, en otras palabras). Siempre es bueno colocar las conexiones y acceso a datos (por ejemplo, apertura de archivos) en una sentencia using, pues se asegura que las conexiones a recursos solicitados (conexiones, archivos, flujos, etc) serán cerradas una vez que ya no se necesiten (es decir, cuando el programa salga del método), ocasionando un mejor uso de la memoria y evitando que sean bloqueados (maximizando concurrencia).
  • Línea 8: Se manda llamar el procedimiento almacenado respectivo, pasando los parámetros y almacenando el resultado en valor
  • Línea 9: Se crea un objeto ResultadoSQL que se devuelve a la llamada del método del servicio. Se asignan los valores de las propiedades Valor y Mensaje. Finalmente, este objeto es serializado (convertido) a una cadena Json, la cual es devuelta como resultado de la llamada al procedimiento. La serialización es posible a través del método SerializeObject, disponible en la clase estática JsonConvert de Newtonsoft.Json.

Los métodos Modificar y Eliminar son parecidos, por lo que no los explicaré (pero su código se incluye un poco más adelante).

Ahora veamos el método ObtenerArtistas:

public string ObtenerArtistas()
{
  using (var conexion = new MusicaEntidades())
  {
    return JsonConvert.SerializeObject(conexion.Procedimiento_ObtenerArtistas().ToList());
  }
}

Este método devuelve toda la tabla Artistas en una cadena JSON a través del procedimiento almacenado Procedimiento_ObtenerArtistas.

Finalmente, el método BuscarArtista se muestra  continuación:

public string BuscarArtista(int id)
{
  using (var conexion = new MusicaEntidades())
  {
    return JsonConvert.SerializeObject(conexion.Procedimiento_BuscarArtista(id).FirstOrDefault());
  }
}

Sencillo, ¿no? Simplemente hay que crear una conexión, llamar al procedimiento almacenado Procedimiento_BuscarArtista pasando el id respectivo y devolver el resultado con FirstOrDefault, el cual devolverá un objeto de la entidad Artistas, que será serializado a una cadena en formato JSON.

El código completo del servicio es el siguiente:


using System;
using System.Linq;
using System.Data.Entity.Core.Objects;
using Newtonsoft.Json;

namespace ServicioMusicaWCF
{
    public class ServicioMusica : IServicioMusica
    {
        public string AgregarArtista(string nombre, string pais)
        {
            int valor;
            var mensaje = new ObjectParameter("mensaje", typeof(string));

            using (var conexion = new MusicaEntidades())
            {
                valor = conexion.Procedimiento_AgregarArtista(nombre, pais, mensaje);
                return JsonConvert.SerializeObject(new ResultadoSQL() { Valor = valor, Mensaje = mensaje.Value.ToString() });
            }
        }

        public string AgregarCancion(string titulo, string duracion)
        {
            int valor;
            var mensaje = new ObjectParameter("mensaje", typeof(string));

            using (var conexion = new MusicaEntidades())
            {
                valor = conexion.Procedimiento_AgregarCancion(titulo, duracion, mensaje);
                return JsonConvert.SerializeObject(new ResultadoSQL() { Valor = valor, Mensaje = mensaje.Value.ToString() });
            }
        }

        public string AgregarConcierto(int idArtista, string lugar, DateTime fecha)
        {
            int valor;
            var mensaje = new ObjectParameter("mensaje", typeof(string));

            using (var conexion = new MusicaEntidades())
            {
                valor = conexion.Procedimiento_AgregarConcierto(idArtista, lugar, fecha, mensaje);
                return JsonConvert.SerializeObject(new ResultadoSQL() { Valor = valor, Mensaje = mensaje.Value.ToString() });
            }
        }

        public string AgregarDetalleConcierto(int idConcierto, int idCancion, int orden)
        {
            int valor;
            var mensaje = new ObjectParameter("mensaje", typeof(string));

            using (var conexion = new MusicaEntidades())
            {
                valor = conexion.Procedimiento_AgregarDetalleConcierto(idConcierto, idCancion, orden, mensaje);
                return JsonConvert.SerializeObject(new ResultadoSQL() { Valor = valor, Mensaje = mensaje.Value.ToString() });
            }
        }

        public string BuscarArtista(int id)
        {
            using (var conexion = new MusicaEntidades())
            {
                return JsonConvert.SerializeObject(conexion.Procedimiento_BuscarArtista(id).FirstOrDefault());
            }
        }

        public string BuscarCancion(int id)
        {
            using (var conexion = new MusicaEntidades())
            {
                return JsonConvert.SerializeObject(conexion.Procedimiento_BuscarCancion(id).FirstOrDefault());
            }
        }

        public string BuscarConcierto(int id)
        {
            using (var conexion = new MusicaEntidades())
            {
                return JsonConvert.SerializeObject(conexion.Procedimiento_BuscarConcierto(id).FirstOrDefault());
            }
        }

        public string EliminarArtista(int id)
        {
            int valor;
            var mensaje = new ObjectParameter("mensaje", typeof(string));

            using (var conexion = new MusicaEntidades())
            {
                valor = conexion.Procedimiento_EliminarArtista(id, mensaje);
                return JsonConvert.SerializeObject(new ResultadoSQL() { Valor = valor, Mensaje = mensaje.Value.ToString() });
            }
        }

        public string EliminarCancion(int id)
        {
            int valor;
            var mensaje = new ObjectParameter("mensaje", typeof(string));

            using (var conexion = new MusicaEntidades())
            {
                valor = conexion.Procedimiento_EliminarCancion(id, mensaje);
                return JsonConvert.SerializeObject(new ResultadoSQL() { Valor = valor, Mensaje = mensaje.Value.ToString() });
            }
        }

        public string EliminarConcierto(int id)
        {
            int valor;
            var mensaje = new ObjectParameter("mensaje", typeof(string));

            using (var conexion = new MusicaEntidades())
            {
                valor = conexion.Procedimiento_EliminarConcierto(id, mensaje);
                return JsonConvert.SerializeObject(new ResultadoSQL() { Valor = valor, Mensaje = mensaje.Value.ToString() });
            }
        }

        public string EliminarDetalleConcierto(int idConcierto, int idCancion)
        {
            int valor;
            var mensaje = new ObjectParameter("mensaje", typeof(string));

            using (var conexion = new MusicaEntidades())
            {
                valor = conexion.Procedimiento_EliminarDetalleConcierto(idConcierto, idCancion, mensaje);
                return JsonConvert.SerializeObject(new ResultadoSQL() { Valor = valor, Mensaje = mensaje.Value.ToString() });
            }
        }

        public string ModificarArtista(int id, string nombre, string pais)
        {
            int valor;
            var mensaje = new ObjectParameter("mensaje", typeof(string));

            using (var conexion = new MusicaEntidades())
            {
                valor = conexion.Procedimiento_ModificarArtista(id, nombre, pais, mensaje);
                return JsonConvert.SerializeObject(new ResultadoSQL() { Valor = valor, Mensaje = mensaje.Value.ToString() });
            }
        }

        public string ModificarCancion(int id, string titulo, string duracion)
        {
            int valor;
            var mensaje = new ObjectParameter("mensaje", typeof(string));

            using (var conexion = new MusicaEntidades())
            {
                valor = conexion.Procedimiento_ModificarCancion(id, titulo, duracion, mensaje);
                return JsonConvert.SerializeObject(new ResultadoSQL() { Valor = valor, Mensaje = mensaje.Value.ToString() });
            }
        }

        public string ModificarConcierto(int id, int idArtista, string lugar, DateTime fecha)
        {
            int valor;
            var mensaje = new ObjectParameter("mensaje", typeof(string));

            using (var conexion = new MusicaEntidades())
            {
                valor = conexion.Procedimiento_ModificarConcierto(id, idArtista, lugar, fecha, mensaje);
                return JsonConvert.SerializeObject(new ResultadoSQL() { Valor = valor, Mensaje = mensaje.Value.ToString() });
            }
        }

        public string ModificarDetalleConcierto(int idConcierto, int idCancion, int orden)
        {
            int valor;
            var mensaje = new ObjectParameter("mensaje", typeof(string));

            using (var conexion = new MusicaEntidades())
            {
                valor = conexion.Procedimiento_ModificarDetalleConcierto(idConcierto, idCancion, orden, mensaje);
                return JsonConvert.SerializeObject(new ResultadoSQL() { Valor = valor, Mensaje = mensaje.Value.ToString() });
            }
        }

        public string ObtenerArtistas()
        {
            using (var conexion = new MusicaEntidades())
            {
                return JsonConvert.SerializeObject(conexion.Procedimiento_ObtenerArtistas().ToList());
            }
        }

        public string ObtenerCanciones()
        {
            using (var conexion = new MusicaEntidades())
            {
                return JsonConvert.SerializeObject(conexion.Procedimiento_ObtenerCanciones().ToList());
            }
        }

        public string ObtenerConciertos()
        {
            using (var conexion = new MusicaEntidades())
            {
                return JsonConvert.SerializeObject(conexion.Procedimiento_ObtenerConciertos().ToList());
            }
        }

        public string ObtenerDetallesConcierto(int idConcierto)
        {
            using (var conexion = new MusicaEntidades())
            {
                return JsonConvert.SerializeObject(conexion.Procedimiento_ObtenerDetallesConcierto(idConcierto).ToList());
            }
        }
    }
}


Por si te lo estás preguntando, sí, ya terminamos. Ya solo falta probar el servicio. Presiona F5 para compilar y ejecutar el proyecto. Esto inicializará la herramienta svcutil (Cliente de prueba WCF), la cual nos permite conectarnos a un servicio par utilizar sus métodos. Esto es lo que aparecerá una vez implementado el proyecto en IIS (que es el servidor donde se montará este servicio).

Importante: Si recibes un error de que no se pudo implementar o ejecutar el proyecto, cierra Visual Studio y vuélvelo a abrir, pero ejecútalo como Administrador


Como puedes ver, los 19 métodos expuestos en el servicio aparecen en esta pantalla. También aparecen métodos asíncronos, los cuales no podemos probar con esta herramienta, pero serán de utilidad al crear una aplicación cliente que consuma el servicio.

Vamos a probar un servicio. Da doble clic en el método ObtenerArtistas y da clic en Invocar.


Si te aparece una Advertencia de seguridad, marca la opción No volver a mostrar este mensaje y da clic en Aceptar.


Y entonces se invocará el servicio y se devolverá su resultado en el panel inferior. Como puedes ver, se devuelve la información de los artistas en formato JSON.


Si quieres copiar el resultado, simplemente da clic en la pestaña XML que aparece debajo y selecciona el valor devuelto.


Aquí tienes el resultado, un arreglo en formato JSON:

[
  {
    "Id":1,
    "Nombre":"Katie Melua",
    "Pais":"Gran Bretaña"
  },
  {
    "Id":4,
    "Nombre":"Michael Buble",
    "Pais":"Canada"
  },
  {
    "Id":3,
    "Nombre":"The Corrs",
    "Pais":"Irlanda"
  }
]


Ahora revisemos el método BuscarCancion. Damos doble clic, escribimos un valor para el parámetro Id e invocamos el servicio, revisando que el resultado sea correcto y en formato JSON:


También podemos comprobar el funcionamiento de los métodos que modifican la información de la base de datos. Por ejemplo, el método AgregarArtista. Escribimos valores e invocamos el servicio, verificando que se devuelve un valor y mensaje.


Nota Importante: Por algún motivo siempre devuelve -1 en lugar del ID del último registro insertado. Voy a verificar qué es lo que está sucediendo y en la siguiente entrega haré la modificación respectiva (de momento, dejémoslo así, ¿sale? :) )

¡Listo! Hemos terminado. Sé que ha sido una entrega algo larga (por eso me tarde un poco más de un día jeje) pero espero que hayas podido seguirla sin problemas. Recuerda que el esfuerzo realizado el día de hoy tendrá su recompensa el día de mañana, así que aunque esta sesión haya sido algo larga, espero te haya sido de utilidad y el aprendizaje adquirido haya sido bastante.

Te recuerdo que el código completo del script lo puedes obtener desde el repositorio de GitHub creado para este proyecto.

Con esto concluimos con la segunda parte del demo, donde creamos un servicio WCF que hace uso de los procedimientos almacenados creados en la primer entrega. En la próxima sesión crearemos un cliente en Xamarin.Forms que consumirá el servicio generado.

Pero antes, realizaré en video la demostración de estas 2 primeras partes de la serie. Espéralo muy pronto, la actualización la publicaré aquí en el blog, pero también puedes suscribirte a mi canal de YouTube, donde los voy a subir.

Te recuerdo que la experiencia de aprendizaje continúa con el #XamarinDiplomado. Únete a nuestro grupo de estudio en Facebook, donde además de hablar del diplomado de Xamarin 3.0 impartido por Microsoft, también comentamos sobre otras oportunidades de aprendizaje, trabajo, resolvemos dudas y compartimos el conocimiento.

Si tienes alguna sugerencia, recomendación, comentario o algún paso no te funcionó, házmelo saber y con gusto te atenderé :) déjame un comentario y aquí lo revisamos.

Espero esta entrada te haya sido de utilidad, y si fue así, no dudes en compartirla :)

¡Saludos y hasta pronto!

-Luis

16 comentarios:

  1. Respuestas
    1. Gracias por leer la publicación, espero te sea útil en tus proyectos.

      Eliminar
  2. Respuestas
    1. Abre Web.config y modifica MusicaEntities por MusicaEntidades, ya con eso debería funcionar.

      Eliminar
  3. Excelente aporte el que realizas con este contenido. Realmente admirable, una consulta... Seria posible incluir en esta serie un contenido sobre como ejecutar notificaciones push sin azure?

    Gracias por todo...

    ResponderEliminar
    Respuestas
    1. Gracias por tu comentario y sugerencia, claro que sí, en una futura entrega lo considero para incrementar la funcionalidad de la aplicación :) Aunque con Azure es más sencillo, jeje, pero entiendo que no siempre es la tecnología que se quiere utilizar.

      Eliminar
  4. Hola Luis, muchas gracias por el excelente tutorial. Quisiera saber si tuviera un procedimiento con una consulta de más de dos tablas como lo utilizaria en el EF en el ejemplo que estas explicando.

    ResponderEliminar
  5. Muchas gracias por el esfuerzo y divulgación para toda la comunidad. Se felicita por el tiempo dedicado. Por favor sugiero el tema de Microsoft Cognitive y Xamarin.

    ResponderEliminar
  6. Hola, una duda, realmente soy principiante en la programación, cuando le dí F5 abre el navegador y lista los archivos,en lugar de la pantallita del WCF, alguna sugerencia? No sé que le moví.

    ResponderEliminar
  7. Mil gracias, esperaré ansioso como consumir el WS por XAMARIN. Saludos y gracias.-

    ResponderEliminar
  8. Excelente el aporte, lo he revisado y espero la parte de Azure.

    Saludos...

    ResponderEliminar
  9. Excelente tus aportes, cuando subirás como consumir el WS desde xamarin

    ResponderEliminar
  10. Hola ...
    Que bueno curso, pero cuando continuas publicando mas contenido ?

    ResponderEliminar
  11. Excelente Material, te recomiendo realizar videos y puedas monetizar esta enseñanza, si exite un forma de contrubicion me gustaria aportar y sientas mas apollo de tus materiales.

    ResponderEliminar
  12. Hola amigo, y la parte donde consumes ese WCF en android trabajando con .net

    ResponderEliminar
  13. Hola amigo. Llegue un poco tarde pero tus explicaciones esran buenísimas. Donde puedo ver la tercera parte?

    ResponderEliminar