14 sept 2012

WCF 4 Parte 4: Instalando servicio en IIS

Cuarta entrega de mis notas sobre sobre el libro “Windows Communication Fundation 4”, una vez más recomendar su adquisición y lectura para su correcto aprovechamiento.

En el último post dejamos nuestro servicio con cuatro operaciones ejecutándose en el servidor de desarrollo de Visual Studio, además de una aplicación cliente que prueba las operaciones. En este nuevo post vamos a pasar nuestro servicio del servidor de desarrollo a un IIS (De hecho desde el punto de vista de un desarrollo normal este sería el siguiente paso lógico).

Primero necesitamos ejecutar Visual Studio con permisos de Administrador así que, le damos clic derecho a nuestro acceso directo de Visual Studio y en el menú emergente elegimos “Run as administrator”.


Una vez con Visual Studio abierto, escogemos la solución con la que hemos venido trabajando. Sobre el proyecto web que contiene nuestro servicio hacemos clic derecho y del menú emergente elegimos “Publish Web Site


Con esto nos aparecerá el cuadro de diálogo para la publicación del sitio, con este abierto hacemos clic en el botón de buscar la ubicación (el botón que contiene tres puntos).En la nueva pantalla que aparece elegimos “Local IIS” en el panel de la izquierda, “Default Web Site” en la lista de la derecha. Con esto seleccionado hacemos clic en icono el icono “Create New Web Application”, renombramos el nuevo sitio (yo lo llamé ProductoServicio) y hacemos clic en “Open”.


En la anterior pantalla anterior hacemos clic en OK. Y con esto hemos publicado nuestro servicio en el IIS. Para comprobar esto, vamos a la ruta C:\inetpub\wwwroot\ProductoServicio creada automaticamente por medio de los pasos anteriores.


Aquí podemos comprobar que nuestro sitio compilado (muy importante) ya se encuentra en el directorio correspondiente al IIS.  No vemos nuestros archivos de código porque el sitio se encuentra compilado.

Pero aun nos falta un detalle para que nuestro sitio este completamente capacitado para funcionar, estos asignarle el Application Pool correcto en el IIS, Para esto abrimos el IIS (para mi la forma mas rápida la combinación de teclar Windows y R, y luego escribir inetmgr y enter). Verificamos que exista un Application Pool con el .Net Framework 4.0 (si no existe, antes de crearlo, es prudunte registrar el framework primero, para esto usamos la ventana de comandos de Visual studio 2012 y introducimos la siguiente instrucción “aspnet_regiis.exe –i”, sin comillas), si no lo creamos:


Contando ya con un Application Pool adecuado nos ubicamos en nuestro sitio web y el panel de Actions Elegimos Basic Configuration, nos aparecerá una ventana de diálogo donde podemos cambiar el Application Pool.


¡Listo! Para comprobar que nuestro servicio se esta corriendo adecuadamente lo comprobamos introduciendo la ruta http://localhost/ProductoServicio/Service.svc en el browser, nos debe aparecer algo similar a esto.


Ahora bien tenemos nuestro servicio ejecutándose en el IIS, eso significa que demos configurar nuestra aplicación cliente para que lo acceda desde el IIS y no desde el servidor de desarrollo. Como no ha cambiando nada en la implementación ni en el contrato, todo se reduce a un cambio de configuración.
Nos ubicamos en visual studio en nuestro proyecto consola, abrimos el archivo App.config y dentro de éste ubicamos la sección del endpoint y cambiamos el address para que apunta a la dirección del IIS, quedando asi:
<endpoint address="http://localhost/ProductoServicio/Service.svc"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IProductoServicio"
                contract="TestWCF.IProductoServicio" name="BasicHttpBinding_IProductoServicio" />
Ejecutamos nuestra aplicación. (si la aplicación no devuelve datos, es muy probable que se deba a que el Application Pool este usando un identity que no tiene permisos para acceder a la base de datos, usando Advance Settings y cambiando el Identity en la sección Process Model, por una cuenta que sea db_owner para la base de datos que estamos usando se debería resolver el problema)


Todo funciona exactamente igual que cuando nos conectamos al servidor de desarrollo, solo que ahora nos conectamos al IIS.

Esta forma de alojar el servicio en IIS es muy útil para establecer comunicaciones entre aplicaciones o servicios a través de internet, es prácticamente igual que desarrollar web services. Pero y si estas aplicaciones o servicios están dentro de nuestra organización ¿No habrá formas mas eficientes de establecer esta comunicación?

Efectivamente las hay, en el siguiente post ejemplificaremos un par de opciones.

9 sept 2012

WCF 4 Parte 3: Más operaciones, actualizando referencia

Continuamos con nuestras notas del libro “Windows Communication Foundation 4”, volvemos a recalcar que esas notas son de carácter personal y lo ideal seria que cualquiera que quiera aprender WCF, lea el libro en cuestión.

En nuestros primeros dos post creamos un servicio WCF y un cliente que lo consume. Este servicio es en realidad muy simple: un único método que devuelve una lista de string con los id de los productos de nuestra base de datos. En este post añadiremos unas cuantas operaciones más.

Estas operaciones serán las siguientes DetalleProducto (devolverá la descripción y el precio de un producto dado un id), StockProducto (dado un id de producto devolverá el stock conjunto de todas las bodegas que tengan ese producto), AnadirStock (añadirá la cantidad del producto a la bodega según se indiquen en los parámetros).

Nuestro estilo es un desarrollo “contract-first”, el contrato primero. Así, primero diseñamos las operaciones en la clase interfaz del contrato y una vez definidos procedemos a su implementación. Comencemos entonces con la clase IProductoServicio, El código nos quedaría más o menos así:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace TestWCF
{
    //Esto define la clase que devuelve el detalle de un producto
    [DataContract]
    public class ProductoDetalle
    {
        [DataMember]
        public string ProductoId;

        [DataMember]
        public string Descripcion;

        [DataMember]
        public decimal? Precio;
    }

    [ServiceContract]
    public interface IProductoServicio
    {
        // devuelve el id de todos los productos
        [OperationContract]
        List<string> ListaProductos();

        //Devuelve el detalle de un producto
        [OperationContract]
        ProductoDetalle DetalleProducto(string ProductoID);

        //Devuelve el stock completo de un producto
        [OperationContract]
        int StockProducto(string ProductoID);

        //Añade la cantidad del producto indicados a la bodega indicada
        [OperationContract]
        bool AnadirStock(string ProductoID, int Bodega, int cantidad);
    }
}

Analicemos el código, lo primero que notamos es la clase ProductoDetalle, esta una clase etiquetada como contrato de datos (DataContract) y cada una de sus variables como DataMember, es similar a definir a un tipo compuesto. Esta clase la usaremos para devolver un objeto con el detalle del producto en la operación correspondiente, como es publica quedará disponible en el cliente también, por medio del proxy.

Por si no se percatan, la variable Precio es de tipo decimal? El signo de pregunta significa que es de tipo nullable, o sea, que es un tipo decimal que puede contener el valor null (un decimal normal no puede contener el valor null), en este escenario es necesario por cuanto en la base de datos el valor Precio esta como “Allow Null” (hubiese sido mejor no permitir nulos y colocar un valor cero por defecto, digamos que fue un desliz de diseño).

Luego se añadieron las tres operaciones propuestas, notar que DetalleProducto devuelve un “tipo” ProductoDetalle, la clase que definimos primero como DataContract. StockProducto devuelve un entero con la cantidad total de inventario de un producto. Así mismo AnadirStock devuelve un booleano, esto es verdadero si se pudo añadir la cantidad, falso si se produjo algún error.

Ya con nuestro contrato definido procedemos a la implementación de cada una de las operaciones en el servicio propiamente dicho. El archivo ProductoServicio.cs nos queda más o menos así:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using ModeloProductos;

namespace TestWCF
{
    public class ProductoServicio : IProductoServicio
    {

        // Devuelve el id de todos los productos
        public List<string> ListaProductos()
        {
            // Se crea la lista para los id de Productos
            List<string> lstProductos = new List<string>();
            try
            {
                // Conectando a la base de datos usando Entity Framework
                using (TestDBEntities db= new TestDBEntities ())
                {
                    // LINQ para obtener la lista de id de los productos
                    var productos = from producto in db.Productos 
                                   select producto.ProductoID;
                    lstProductos = productos.ToList();
                }
            }
            catch
            {
                // De momento no se implementa el manejo de excepciones
            }
            // devuelve la lista de productos
            return lstProductos;
        }

        // Devuelve el detalle de un producto
        public ProductoDetalle DetalleProducto(string ProductoID)
        {
            // Creamos la instancia de la clase Producto definida en el contrato
            ProductoDetalle DatosProducto = null;
            try
            {
                // conectamos con la base de datos por medio del Entity Framework
                using (TestDBEntities db = new TestDBEntities ())
                {
                    // LINQ para encuentrar el primer registro cuyo id corresponda con el parametro
                    Producto productoEncontrado = db.Productos.First(
                    p => String.Compare(p.ProductoID , ProductoID) == 0);

                    DatosProducto  = new ProductoDetalle ()
                    {
                        ProductoId = productoEncontrado.ProductoID,
                        Descripcion = productoEncontrado.Descripcion,
                        Precio = productoEncontrado.Precio 
                    };
                }
            }
            catch
            {
                //  De momento no se implementa el manejo de excepciones
            }
            // Retorna el detalle de producto
            return DatosProducto;
        }

        // Devuelve el stock completo de un producto
        public int StockProducto(string ProductoID)
        {
            // Obtiene el nivel total de stock del producto incluyendo todas bodegas
            int stock = 0;
            try
            {
                // conectamos con la base de datos por medio del Entity Framework
                using (TestDBEntities db = new TestDBEntities())
                {
                    // LINQ para sumar todo el stock de todas los bodegas
                    stock = (from i in db.Inventarios
                                  join p in db.Productos
                                  on i.ProductoID equals p.ProductoID
                             where String.Compare(p.ProductoID, ProductoID) == 0
                                  select (int)i.Stock).Sum();
                }
            }
            catch
            {
                // De momento no se implementa el manejo de excepciones
            }
            // Devuelve el stock total del producto
            return stock;
        }

        // Añade la cantidad del producto indicados a la bodega indicada
        public bool AnadirStock(string ProductoID, int Bodega, int cantidad)
        {
            //Añade la cantidad indicada para el producto y en la bodega indicados
            try
            {
                // conectamos con la base de datos por medio del Entity Framework
                using (TestDBEntities db = new TestDBEntities())
                {
                    // LINQ para aseguramos que el id del producto exista
                    string producto =
                    (from p in db.Productos
                     where String.Compare(p.ProductoID, ProductoID) == 0
                     select p.ProductoID).First();

                    // LINQ para extraer de la tabla el registro del inventario 
                    // correspondiente al producto y la bodega                    
                    Inventario inventario = db.Inventarios.First(
                    i => String.Compare(i.ProductoID , ProductoID) == 0 &&
                    i.BodegaID == Bodega );
                    // realizamos el update sobre el registro encontrado
                    inventario.Stock += cantidad;
                    // salvamos los cambios en la base de datos
                    db.SaveChanges();
                }
            }
            catch
            {
                // retorna false si se produjo algun error
                return false;
            }
            // Retorna true si la operacion fue exitosa
            return true;
        }
    }
}

Examinemos las operaciones que implementamos para discernir mejor el uso de LINQ. En la operación DetalleProducto utilizamos la función First de Linq para traernos el primer registro que corresponda con la comparación del id del producto. Además como esta comparación es entre strings lo realizamos utilizando el String.Compare, esta es una práctica habitual en la consultas vía Linq, ya que es la forma segura de comparar cadenas de caracteres. Una vez obtenido el registro vaciamos sus propiedades en nuestra clase de contrato de datos para enviarla al cliente.

En la operación StockProducto usamos una sentencia Linq un poco más complicada: realizamos un join entre las tablas Producto e Inventario, enlazadas por medio de la columna ProductoId, además buscamos por el parámetro haciendo la comparación con el String.Compare tal y como señalamos anteriormente. Finalmente utilizamos la operación de agregación “sum” para sumar los stocks de todas las bodegas. El resultado se lo asignamos a la variable stock la cual devolvemos al final de la operación.

Finalmente en la operación AnadirStock, primero volvemos a usar la sentencia First de Linq para determinar si el id de producto existe en la base de datos. Seguidamente también usamos First para devolver el primer registro de la tabla inventario que cumpla con los criterios de producto id y bodega. Luego, al registro devuelto, en la columna stock, le sumamos la cantidad pasada por parámetro y finalmente salvamos los cambios en la base de datos por medio de SaveChanges del Entity Framework.

Ahora nos queda actualizar nuestra aplicación cliente para hacer una prueba de cada una de las operaciones. Primero necesitamos actualizar nuestra referencia para que el cliente conozca las nuevas operaciones y contrato de datos si los hay. Para esto nos vamos a nuestro proyecto consola  ProductoCliente y hacemos clic derecho sobre la referencia que teníamos previamente, en el menú emergente y elegimos “Update Service Reference



Ya con la referencia actualizada podemos trabajar en el código del cliente. El archivo Program.cs debería quedar más o menos asi:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using ProductoCliente.TestWCF;

namespace ProductoCliente
{
    class Program
    {
        static void Main(string[] args)
        {
            // Se crea el proxy para conectar con el servicio
            ProductoServicioClient proxy = new ProductoServicioClient();

            // Probamos el metodo que obtiene los id de productos
            Console.WriteLine("Probando ListaProductos: Listado de Id de Productos");
            List<string> ListaProductos = proxy.ListaProductos();
            foreach (string idProdcto in ListaProductos)
            {
                Console.WriteLine("Id Producto: {0}", idProdcto);
            }
            Console.WriteLine();

            Console.WriteLine("Probando DetalleProducto: Detalle de producto B-0011");
            ProductoDetalle detalle = proxy.DetalleProducto("B-0011");
            Console.WriteLine("Producto ID: {0}", detalle.ProductoId);
            Console.WriteLine("Descripción: {0}", detalle.Descripcion);
            Console.WriteLine("Precio: {0}", detalle.Precio.ToString());

            Console.WriteLine();

            Console.WriteLine("Probando StockProducto: Stock completo del producto B-0011");
            int stock = proxy.StockProducto("B-0011");
            Console.WriteLine("Stock Completo: {0}", stock.ToString());

            Console.WriteLine();


            Console.WriteLine("Probando AnadirStock: Colocando en la bodega 3 la cantidad de 30 más");
            if (proxy.AnadirStock ("B-0011", 3, 30))
            {
                stock = proxy.StockProducto("B-0011");
                Console.WriteLine("Stock Modificado. Nuevo Stock: {0}", stock);
            }
            else
            {
                Console.WriteLine("Actualización de stock fallido");
            }
            Console.WriteLine();

            // Desconectado del servicio
            proxy.Close();
            Console.WriteLine("Presione ENTER para terminar.");
            Console.ReadLine();
        }
    }
}

Como podemos ver la clase ProductoDetalle esta disponible y de hecho la utilizamos para mostrar el detalle de producto, también usamos StockProducto para verificar que AnadirStock funciona correctamente.

Antes de ejecutar el cliente la información en mi base de datos se ve más o menos así:




Ejecutamos nuestra aplicación cliente, la cual nos arroja el siguiente resultado:



Todo según el plan.

En el siguiente post pasaremos nuestro servicio al IIS, tal y como sería una vez finalizadas las pruebas de un desarrollo de este tipo.

5 sept 2012

WCF 4 Parte 2: Creando el cliente

Continuando con el post anterior vamos a elaborar la aplicación cliente para probar nuestro servicio WCF. Recordar que estas son notas del libro “Windows CommunicationFoundation 4”,  preferiblemente leerlo.

Primero a nuestra solución añadimos un nuevo proyecto tipo consola (yo lo nombré como ProductoCliente), seguidamente añadimos la referencia a System.ServiceModel esto en la pestaña .Net.

Ahora necesitamos añadir la referencia a nuestro proyecto Web que aloja el servicio WCF, esto para crear un proxy por medio del cual establecer la comunicación con el servicio. Procedemos de la siguiente manera: sobre el proyecto recién creado hacemos clic derecho y elegimos “Add Service Reference…” nos aparecerá una pantalla como esta:


En esta hacemos clic sobre el botón Discover, esto realiza una búsqueda de servicios en nuestra solución. Elegimos el servicio asegurándonos que nuestro método exista en la sección “Operations” y hacemos clic en OK:


Importante notar que se cambió el namespace para hacerlo más descriptivo. Con esto hemos creado el proxy necesario para comunicarnos con el servicio, pero ¿Dónde esta este Proxy? Lo podemos encontrar haciendo clic el botón de mostrar todos los archivos y luego expandiendo los folder: Service Reference, TestWCF, Reference.svmap y haciendo doble clic sobre el archivo Refence.cs


Este proxy fue creado automáticamente por Visual Studio, por lo que no es recomendable modificarlo manualmente ya que los cambios se perderán cada vez que se actualice la referencia.

Ahora podemos dedicarnos a escribir el código de la aplicación cliente que consumirá nuestro servicio WCF. El archivo Program.cs quedaría similar a esto:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using ProductoCliente.TestWCF;

namespace ProductoCliente
{
    class Program
    {
        static void Main(string[] args)
        {
            // Se crea el proxy para conectar con el servicio
            ProductoServicioClient proxy = new ProductoServicioClient();

            // Probamos el metodo que obtiene los id de productos
            Console.WriteLine("Listado de Id de Productos");
            string[] ListaProductos = proxy.ListaProductos();
            foreach (string idProdcto in ListaProductos)
            {
                Console.WriteLine("Id Producto: {0}", idProdcto);
            }
            Console.WriteLine();

            // Desconectado del servicio
            proxy.Close();
            Console.WriteLine("Presione ENTER para terminar.");
            Console.ReadLine();

        }
    }
}

El código es fácil de entender, lo único relevante de señalar es la desconexión del proxy; es importante realizar esta tarea para liberar cualquier recurso abierto en el servidor donde esta alojado el servicio WCF. Esta tarea también se puede lograr usando la  sentencia  using (ProductoServicioClient proxy = new ProductoServicioClient())  {…} ya que la clase del servicio implementa la interfaz IDisposable, lo que significa que al cerrar el bloque using se llamará automáticamente al método close del proxy.

Si ejecutamos esta consola obtendremos los id de los productos registrados en la base de datos.


Pero aquí es donde debemos detenernos y hacernos preguntas ¿Cómo la aplicación cliente sabe donde encontrar el servicio? ¿Cómo maneja los tipos del mensaje de respuesta del servicio? Esto es importante porque estas cuestiones son la esencia detrás del WCF.

Revisemos el App.config de nuestra aplicación cliente:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IProductoServicio" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
                    useDefaultWebProxy="true">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <security mode="None">
                        <transport clientCredentialType="None" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="UserName" algorithmSuite="Default" />
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:50533/WCFServiceTest/Service.svc"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IProductoServicio"
                contract="TestWCF.IProductoServicio" name="BasicHttpBinding_IProductoServicio" />
        </client>
    </system.serviceModel>
</configuration>

Todo esto se creó cuando añadimos la referencia al servicio. En este momento nos interesa la sección <client> es aquí donde se definen los endpoints, estos proveen los detalles por medio de los cuales se vuelve posible la comunicación con el servicio. Estos detalles son:

-        -  Address: es la ruta donde el servicio esta localizado, en este ejemplo como el servicio se encuentra hosteado en el servidor de desarrollo de Visual Studio la ruta es una local via http y con un puerto generado por el mismo servidor.

-        -  Binding y bindingConfiguration: especifica cosas como el mecanismo de trasporte, el protocolo de comunicación, entre otros. Existen bindings preestablecidos dentro del WCF, si no se especifica ninguno se utiliza el de por defecto, que es basicHTTPBinding, el cual es compatible con la mayoría de Servicios Web existentes. Ademas se pueden especificar cosas como Timeouts, encodings para los mensajes y requerimientos de seguridad. Es muy importante señalar que la aplicación cliente debe usar la misma información de binding que el servicio para que la comunicación sea exitosa. Si se cambia el binding en el servicio debe replicarse ese cambio en todas las aplicaciones cliente que lo consuman.

-        -  Contract: es el que define los mensajes de envío y recepción para el servicio, este está especificado como una interfaz en el proxy que se crea al añadir la referencia al servicio

Adicionalmente, por medio del atributo name se puede definir un nombre para el endpoint, el cual se puede usar en código para señalar que endpoint usar por medio de una sobre carga en el constructor del proxy.

Ahora y finalmente, si recordamos la implementación de nuestro método ListaProductos en el servicio, recordaremos que el mensaje de vuelta se implementó como una lista de strings; sin embargo en nuestra aplicación cliente lo estamos recibiendo como un arreglo de strings ¿A que se debe esto?

Se debe a la manera en que se trasmiten los mensajes. Recordemos que estos se trasmiten por medio de XML. Esto es, cuando definimos operaciones en el servicio lo hacemos usando tipos del .Net framework, cuando se recibe un mensaje el WCF runtime convierte el XML entrante en tipos .Net y cuando envía la respuesta convierte tipos .Net en el XML de respuesta. Algo idéntico ocurre en la aplicación cliente. La mayoría de las veces el mapeo entre XML y tipos .Net es satisfactorio. Sin embargo para las colecciones XML hay varias representaciones válidas en tipos .Net y por defecto se utilizan arreglos para mapearlas.

Esto se puede cambiar de la siguiente manera: hacemos clic derecho sobre la referencia del servicio, en el menú emergente elegimos “Configure Service Reference…” nos aparecerá una pantalla como la siguiente:




En el combo “Collection Type” elegimos System.Collections.Generic.List y hacemos clic en OK. Ya con este cambio nuestra aplicación cliente ya no se ejecuta, puesto que nos da un error de conversión de tipos entre el arreglo y la lista genérica. Hacemos un ajuste de manera que cambiamos el array por:

List<string> ListaProductos = proxy.ListaProductos();

Ejecutamos nuevamente y obtenemos el mismo resultado, sólo que ahora obtenemos una lista genérica con los id de los productos en lugar de un arreglo con los mismos.

En el siguiente post vamos a agregar más operaciones a nuestro servicio…

1 sept 2012

WCF 4 Parte 1: LINQ y Entity Framework

Estoy en el proceso de leer un libro sobre WCF y, como mencioné en el post anterior, me parece una muy buena práctica el dejar notas respecto a este tipo de lecturas, por lo tanto voy a ir dejando algunas entradas en el blog respecto al libro “WindowsCommunication Foundation 4”. Como en el post anterior lo recomendable es hacerse con el libro y leerlo, pues más bien estas notas vienen siendo personales, con muchos vacíos, detalles que se asumen, etc.

Ahora bien, voy a tratar de realizar una serie de post con ejemplos de WCF. El primero tratará sobre un inventario. Voy a tener dos tablas: una de productos y otra de inventario. La primera tendrá columnas como el id del  producto, descripción, precio y la segunda también el id producto, número de bodega y cantidad en stock. En realidad muy sencillo.


Para este ejemplo se usan varias tecnologías como Entity Framework, Linq y el propio WCF. Este primer post tratará de juntar las piezas y crear un servicio WCF que me devuelva todos los id productos de la base de datos, para luego, en entradas posteriores, hacer otros métodos que me devuelvan el detalle de un producto, consultar el stock de un producto en todas las bodegas y actualizarlo.

Buenos, ¡manos a la obra!.  Ya contamos con las tablas creadas en nuestro SQLServer. Ahora vamos a Visual Studio. Primero vamos a usar el Entity Framework para crear nuestras entidades de programación a partir de las tablas en nuestra base de datos. Para lograr esto primero creamos un proyecto de tipo Class Library, le ponemos como deseemos (en mi caso ModeloProductos); una vez creado, eliminamos la clase que crea por defecto el wizard.

A continuación hacemos clic derecho sobre el proyecto y elegimos Add New Item, en esta nueva ventana elegimos ADO.NET Entity Data Model le nombramos como deseemos (ModeloProductos) y clic en Add. Nos aparecerá un wizard en el que elegiremos Generate From Database, a continuación nos pedirá establecer una conexión, esta deberá estar apuntando a la DB donde tenemos nuestras tablas. Una vez establecida la conexión hacemos clic en Next y nos parecerá una ventana en la que eligiremos que objetos de la base de datos incluir en nuestro de modelo


 Elegimos la tablas deseadas y, si queremos, cambiamos el namespace del modelo para seguidamente hacer clic en Finish. Y “voilà”, están listas nuestras entidades.



Ahora comencemos con el  servicio WCF. Hacemos clic derecho sobre la solución y elegimos Add -> New Web Site… En la pantalla que nos aparece, de los Online Templates, elegimos WCF Service


Este wizard crea dos clases en el folder App_Code: IService.cs y Service.cs.  El primero de estos es la interfaz que define las operaciones que contendrá el servicio y el segundo es la implementación de esta definición. Es algo así como un contrato que engloba todo lo que el servicio debe hacer y la implementación propiamente de los pasos necesarios para llevar acabo lo especificado en el contrato.

Renombremos las clases según deseemos (IProductoServicio.cs y ProductoServicio.cs en mi caso). Comencemos con la interfaz, el contrato, en el archivo IProductoServicio.cs eliminamos todo el código dejando únicamente las directivas using, añadimos el siguiente código:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace TestWCF
{
    [ServiceContract]
    public interface IProductoServicio
    {
        // devuelve el id de todos los productos
        [OperationContract]
        List<string> ListaProductos();
    }
}

La interfaz se decora con la etiqueta ServiceContract que indica que es un contrato que debe implementarse, cada operación de este contrato debe decorarse con la etiqueta OperationContract para indicar que son operaciones del contrato a implementar. En este caso nuestra operación no recibe parámetros y devuelve una lista de strings correspondientes a los id de los productos. Se pueden usar listas siempre que los tipos que devuelvan sean serializables, de hecho todo lo que reciba el servicio así como lo que devuelva debe ser serializable. Notar además las directivas System.ServiceModel estas son las que contienen las bases para la creación del servicio WCF.

Ahora la implementación, en el archivo ProductoServicio.cs dejamos únicamente las sentencias using. Luego necesitamos añadir las referencias para utilizar las entidades del Entity Framework, para esto hacemos clic derecho sobre el proyecto y luego elegimos Add Reference, elegimos nuestro proyecto de Entidades (en mi caso ModeloProductos); luego volvemos añadir otra referencia pero esta vez buscamos en la pestaña .Net y añadimos la referencia System.Data.Entity. Ahora con esto finalmente añadimos el código de la operación quedando de la siguiente manera:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using ModeloProductos;

namespace TestWCF
{
    public class ProductoServicio : IProductoServicio
    {
        public List<string> ListaProductos()
        {
            // Se crea la lista para los id de Productos
            List<string> listProductos = new List<string>();
            try
            {
                // Conectando a la base de datos usando Entity Framework
                using (TestDBEntities db= new TestDBEntities ())
                {
                    // LINQ para obtener la lista de id de los productos
                    var products = from producto in db.Productos 
                                   select producto.ProductoID;
                    listProductos = products.ToList();
                }
            }
            catch
            {
                // De momento no se implementa el manejo de excepciones
            }
            // devuelve la lista de productos
            return listProductos;
        }
    }
}

Primero crea la lista de string para meter los id productos, luego usando Entity FrameWork nos conectamos a la base de datos; usamos Linq para realizar una consulta simple de los id de productos, el resultado lo transformamos en una lista y finalmente devolvemos esa lista.

Ya con esto esta casi listo el servicio, faltan algunos detalles de configuración. Primero en la raíz de proyecto ubicamos el archivo Service.svc, este archivo apunta a la implementación del servicio. Lo modificamos para que apunte al archivo y clase correctos:

<%@ ServiceHost Language="C#" Debug="true" Service="TestWCF.ProductoServicio" CodeBehind="~/App_Code/ProductoServicio.cs" %>

Notar que en el valor de Service se pone el nombre de la clase con todo y namespace.

Finalmente necesitamos el string de conexión a la base de datos en el archivo de configuración del sitio. Para esto nos fijamos en el archivo App.config del proyecto Enttiy Framework y copiamos todo lo que este entre las llaves <connectionStrings> estas inclusive y luego copiamos esto en web.config del sitio del servicio, luego de la apertura de la llave <configuracion> pero antes de la llave <system.web> de la siguiente manera:

<?xml version="1.0"?>
<configuration>
  <connectionStrings>
    <add name="TestDBEntities" connectionString="metadata=res://*/ModeloProductos.csdl|res://*/ModeloProductos.ssdl|res://*/ModeloProductos.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=FOYPC\FOYSERVER;initial catalog=TestDB;integrated security=True;multipleactiveresultsets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>
  <system.web>

El resultado final es similar a un web service, pero en WCF. Para probar que todo esta bien ejecutamos el servicio, como cualquier sitio web y nos debe aparecer algo parecido a esto:

                                
Notar que esta corriendo en el servidor de desarrollo de Visual Studio, cuando lo ideal sería que se ejecutase en el IIS, quedará para una próxima entrada. Nos falta construir una aplicación cliente para consumir este servicio, pero como este post ya se ha extendido mucho lo dejo para la siguiente entrada.