15 dic 2012

Filtrado de List<>

Problema: tengo una lista de objetos y necesito una lista de la misma clase de objetos pero filtrada dado un criterio basado en un alguna propiedad de la clase del objeto.

Solución: hay varias: recorrer la lista con un Foreach, hacer una consulta con Linq o usar una expresión lambda. Como se ve más elegante la expresión y ademas es un ámbito que casi no domino probemos esta última.

Probando: Primero creamos nuestra clase de prueba. En este caso una sencilla clase estudiante que contiene el nombre, una nota y un booleano para saber si esta eximido.
public class estudiante
{
    public string nombre { get; set; }
    public int nota { get; set; }
	public bool eximido { get; set; }
}
Segundo, la consola en la que utilizaremos una lista de estudiantes.
static void Main(string[] args)
{
    List  estudiantes = new List(){
        new estudiante(){nombre ="Foy", nota=90, eximido = true },
        new estudiante(){nombre ="Fer", nota=60, eximido = false },
        new estudiante(){nombre ="Fran", nota=92, eximido = true },
        new estudiante(){nombre ="Fidel", nota=91, eximido = true },
    };
Finalmente usamos el método FindAll que recibe una función predicado, o sea, una función lambda que devuelva true, para los items que deseamos obtener como una lista de la misma clase estudiante. Y el recorrido para mostrar el resulta en consola.
List Eximidos = estudiantes.FindAll(i => i.eximido == true);

foreach (estudiante eximido in Eximidos)
{
    Console.WriteLine("{0} Nota: {1}", eximido.nombre, eximido.nota);
}
Console.ReadLine();
En esta caso la lista será filtrada por los estudiantes que tiene el indicador de eximidos en true, resultando en:

Es prudente señalar que por dentro el FindAll utiliza un foreach para devolver el resultado pero como podemos ver es un método sencillo y elegante para filtrar listas genéricas.

17 nov 2012

Instalando .Net Framework 3.5

Estaba preparando una máquina virtual y necesitaba instalar un programa que me solicitaba el .Net Framework 3.5. De hecho automáticamente me dirigía a una página desde donde se puede bajar un instalador del mismo. Sin embargo resulta que la máquina no cuenta con acceso a Internet.

Luego de una pequeña búsqueda encontré que existía una especie de paquete para instalación desconectada que de hecho pesa 207MB, así que pensé: problema solucionado.

Pero no, casi nunca las coas resultan tan fáciles, una vez descargado el archivo y ejecutado me continuaba solicitando bajar archivos de la Internet, con el consiguiente error. Luego de una nueva búsqueda encontré el problema y la solución.

El problema: El instalador busca en nuestro sistema qué paquetes de lenguaje necesita y procede a descargarlos.

La solución: indicarle al instalador que no instale ningún paquete de lenguaje. Ahora ¿Cómo hacemos esto?

1. Bajamos el paquete del .Net Framework 3.5
2. Con winrar o algún programa similar instalado, hacemos clic derecho sobre el archivo dotnetfx35.exe y extraemos su contenido a una nueva carpeta.
3. Dentro de la extraído ubicamos el archivo dotNetFx35setup.exe
4. Abrimos el la ventana de comandos del Windows (ejecutar cmd) .
5. Dentro del cmd nos ubicamos en la carpeta que contiene el archivo dotNetFx35setup.exe
6. Ejecutamos la siguiente instrucción dotNetFx35setup.exe LANG:ENU

Esto nos permite instalar el .Net Framework 3.5 (y las versiones anteriores) sin paquete de lenguaje y por ende sin necesitad de conectarnos a Internet.

Problema resuelto...

24 oct 2012

WCF 4 Parte 5: Diferentes Host para nuestro servicio

Continuamos con las notas del libro “Windows Communication Fundation 4”. En los post anteriores creamos un servicio WCF, creamos un cliente que lo probara, le añadimos nuevas operaciones y finalmente lo publicamos en un IIS. En esta nueva entrada vamos explorar nuevas formas de alojar nuestro servicio de WCF.

Ahora bien, que nuestro servicio este alojado (host)  en el IIS es una excelente alternativa si los clientes o servicios que lo utilizarán lo harán por medio de internet utilizando http o https, por ejemplo un proveedor o cualquier agente externo a nuestra organización. Pero cuando nuestros clientes se encuentran dentro de los alcances de nuestra intranet, hay mejores alternativas.


Windows Process Activation Service (WAS)

Comencemos con la utilización del Windows Process Activation Service (WAS). Este extiende la funcionalidad del IIS removiendo la dependencia del protocolo HTTP. Utilizando WAS para alojar nuestros servicios WCF podemos hacer uso de protocolos como el TCP, name pipes, MMQ (colas). Estando en este punto es importante recordar una de las máximas del WCF: la definición e implementación del contrato y del servicio son casi completamente independientes del ambiente en que se aloja o del protocolo de comunicación, quedando éstos como detalles de configuración.

Instalemos pues el WAS. Desde el panel de controles elegimos “Programs and Features” luego “Turn Windows Features on or off”, en la pantalla que se nos presenta seleccionados “Windows Process Activation Services” y todas sub-dependencias, también seleccionamos “Windows .Net Framework 3.5.1” y todas sus dependencias (.Net 3.5.1 contiene características necesarias para ejecutar WCF dentro de WAS)



Una vez realizado esto debemos asegurarnos de volver a registrar el .Net Framework 4.0, para esto nos vamos a la ventana de comandos de Visual Studio 2010 como administrador y ejecutamos la instrucción “aspnet_regiis -iru” sin comillas.

Ahora bien ejecutamos el “Internet  Information Services (IIS) Manager” como Administrador, una vez aquí hacemos clic derecho sobre “Default Web Site” y en el menú emergente hacemos clic sobre sobre “Edit Bindings…”. Si el WAS fue instalado correctamente la pantalla que se nos presentará será similar a la siguiente:



Podemos elegir alguno de los bindings y editar sus características, por ejemplo en net.tcp podemos añadir más puertos por los cuales escuchar peticiones o cambiar el puerto por defecto (808). De momento dejamos la configuración por defecto y hacemos clic en el botón Close.

Ahora seleccionamos el sitio correspondiente a nuestro servicio WCF y en el panel “Actions” elegimos “Advanced Settings…”, editamos la propiedad “Enabled Protocols” para añadir net.tcp, esto se hace separando con coma los protocolos:



Hacemos clic en el botón OK. Ya tenemos configurado nuestro servicio escuchando solicitudes tanto por medio HTTP (en el IIS) como por TCP (en el WAS). Ahora configuremos nuestra aplicación cliente para que se comunique con el servicio por medio de TCP.

Dentro de Visual Studio en la solución en la que hemos venido trabajando elegimos el proyecto que contiene nuestra consola cliente y abrimos el archivo App.config. Nos ubicamos en la sección de client para añadir un nuevo endpoint quedando de la siguiente manera:
<client>
  <endpoint address="http://localhost/ProductoServicio/Service.svc"
      binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IProductoServicio"
      contract="TestWCF.IProductoServicio" name="BasicHttpBinding_IProductoServicio" />

  <endpoint address="net.tcp://localhost/ProductoServicio/Service.svc"
      binding="netTcpBinding" contract="TestWCF.IProductoServicio"
      name="NetTcpBinding_IProductoServicio" />
</client>
Como se ve, el primer endpoint es el que ya teníamos, el que usamos para comunicarnos con el servicio en el IIS utilizando HTTP, el segundo es el que recién añadimos: la ruta es net.tcp y el binding es netTcpBinding, este binding esta parte del juego de bindings predefinidas en el WCF runtime.

Si ejecutamos el cliente sin mayor modificación nos dará un error tipo InvalidOperationException en el que se nos indica que nuestro contrato tiene más de un endpoint válido.  Para solventar esta situación lo que necesitamos es utilizar el método sobrecargado encargado de crear el Proxy para que sepa cual endpoint utilizar.  Abrimos el archivo program.cs y cambiamos la línea de creación del proxy de manera que quede de la siguiente manera:
// Se crea el proxy para conectar con el servicio
ProductoServicioClient proxy = new ProductoServicioClient("NetTcpBinding_IProductoServicio");
Ahora si ejecutamos nuestra aplicación cliente el resultado debe ser el mismo que cuando nos conectábamos al IIS por HTTP solo que ahora sería al WAS vía TCP:

Este tipo de implementación del servicio flexibiliza su consumo, de manera que en una sola ubicación el servicio puede responder tanto a peticiones externas a la organización via http o https como a las peticiones internas vía TCP, lo cual es más eficiente en el escenario interno.

Aplicación Windows

Segunda opción para alojar nuestro servicio para consumo interno: una Aplicación Windows. Para lograr este acople entre nuestro servicio y una aplicación debemos comenzar reconociendo que nuestro servicio no puede ser un sitio web, debemos implementarlo como WCF Service Library.

Comencemos, en nuestra solución añadimos un nuevo proyecto, en la ventana que se nos presenta en la sección de Templates elegimos WCF y WCF Service Library, cambiamos el nombre por uno más significativo (En mi caso ProductoServicioLibrary)

Eliminamos los archivos IService.cs y Service1.cs, seguidamente hacemos clic derecho sobre nuestro nuevo proyecto y, del menú emergente, seleccionamos Add -> Exisitng Item..  y elegimos nuestro contrato e implementación del sitio web que habíamos construido previamente y hacemos clic en el botón Add.

Hay que añadir además la referencia a nuestro proyecto de modelos de datos (en este ejemplo ModeloProductos) y la referencia del System.Data.Entity para finalizar el establecimiento de nuestro servicio. Como podemos deducir, el código que escribimos para el sitio web es casi exactamente el mismo que vamos a utilizar en este nuevo “host”, solo debemos eliminar esta línea de código:
using System.ServiceModel.Web;
Ahora necesitamos una aplicación Windows que nos sirva de alojo para nuestro servicio.  En nuestra solución añadimos un nuevo proyecto, este será un WPF Application. En mi caso yo le pondré ProductoServicioHost:

Ahora en nuestro recien creado proyecto, necesitamos renombrar el archivo MainWindow.xaml por HostServicio.xaml (en una app WPF recordemos que este es el archivo que contiene la ventana o el form principal). Abrimos el archivo App.xaml y cambiamos el atributo StartupUri para que apunte a HostController.xaml:
<Application x:Class="ProductoServicioHost.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="HostServicio.xaml">
    <Application.Resources>
         
    </Application.Resources>
</Application>
Ahora modificamos la ventana de la aplicación de la siguiente manera:  hacemos doble clic en HostServicio.xaml para aceder al diseñador y al codigo XAML procurando que quede más o menos de la siguiente forma.

Ahora vamos a trabajar en el código. Primero debemos añadir la referencia al assembly System.ServiceModel y una más a nuestro proyecto de WCF Library (en mi caso a ProductoServicioLibrary) Una vez añadidas estas referencias procedemos a modificar el código para que quede de la siguiente manera:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.ServiceModel;
using TestWCF;

namespace ProductoServicioHost
{
    /// <summary>
    /// Interaction logic for HostServicio.xaml
    /// </summary>
    public partial class HostServicio : Window
    {
        private ServiceHost servicioHost;

        public HostServicio()
        {
            InitializeComponent();
        }

        private void Deterner_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                servicioHost.Close();
                btnDetener.IsEnabled = false;
                btnIniciar.IsEnabled = true;
                txtEstado.Text = "Detenido";
            }
            catch (Exception ex)
            {
                ManejoExcepcion(ex);
            }
        }
        
        private void Iniciar_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                servicioHost = new ServiceHost(typeof(ProductoServicio));
                servicioHost.Open();
                btnDetener.IsEnabled = true;
                btnIniciar.IsEnabled = false;
                txtEstado.Text = "En Ejecución";
            }
            catch (Exception ex)
            {                
                ManejoExcepcion (ex);
            }
        }

        private void ManejoExcepcion(Exception e)
        {
            MessageBox.Show(e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
        }
    }
}
Expliquemos. Añadimos una variable privada de tipo ServiceHost que es la que posibilita el alojamiento del servicio. Luego en el botón iniciar instanciamos nuestro servicio dentro del host y lo “abrimos” de manera que escuche las peticiones. Por el contrario, en el botón Detener “cerramos” el host con lo que no se procesarán mas solicitudes. Adicionalmente se crea una función utilitaría para el manejo de las excepciones.

Ahora necesitamos agregar la configuración del endpoint. Esto es algo que cuando desarrollamos el servicio para el IIS o para el WAS de lado del servidor era muy transparente. Pero en esta nueva situación debemos manejarlo de forma diferente.

Primer añadimos un Archivo de configuracion, App.config, a nuestro Proyecto (ProductoServicioHost) [Add New Item -> Application Configuration File] seguidamente le damos clic derecho sobre el mismo y le damos  Edit WCF Configuration:

Esto abrirá una nueva aplicación por medio de la cual podemos configurar nuestro servicio:

A continuación le damos cli en el link de la derecha “Create a New Service” una vez abierta la siguiente pantalla le damos clic al boton “Browse…” y nos vamos hasta la carpeta Bin -> Debug y escogemos la dll correspondiente a nuestro proyecto WCF Library (ProductoServicioLbrary.dll en mi caso) y clic en Open, seguidamente escogemos la implementación de nuestro servicio y de nuevo clic en Open.

Hacemos clic en Next. En la pantalla siguiente dejamos la especificación del contrato tal y como esta y le damos clic en Next. Nos aparecerá la pantalla para elegir el modo de comunicación en el cual elegimos TCP:

Hacemos clic en Next. En esta pantalla debemos colocar la dirección del endpoint. La dirección que utilizaremos para el ejemplo será net.tcp://localhost:8080/TcpService  

Hacemos clic en Next. Se nos presenta un resumen y el botón para finalizar esta parte de la configuración.

Luego de dar clic en Finish aún nos queda hacer una última cosa. Del lado derecho expandimos la carpeta Endpoints, seleccionamos el nodo “(Empty Name)” y del lado izquierdo cambiamos la propiedad Name para que nos quede de acuerdo al estándar (en mi caso sería NetTcpBinding_IProductoServicio)

Hacemos clic en File -> Save y cerramos la ventana. Seguidamente hacemos doble clic sobre el App.config y le añadimos luego de la apertura del tag <configuration> la cadena de conexión a la base de datos (esta lo podemos copiar del archivo de configuración del proyecto de modelo datos). Finalmente el archivo debe quedar similar a lo siguiente:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <connectionStrings>
      <add name="TestDBEntities" connectionString="metadata=res://*/ModeloProductos.csdl|res://*/ModeloProductos.ssdl|res://*/ModeloProductos.msl;provider=System.Data.SqlClient;provider connection string="Data Source=FOYPC\FOYSERVER;Initial Catalog=TestDB;Integrated Security=True;MultipleActiveResultSets=True;Application Name=EntityFramework"" providerName="System.Data.EntityClient" />
    </connectionStrings>  
    <system.serviceModel>
        <services>
            <service name="TestWCF.ProductoServicio">
                <endpoint address="net.tcp://localhost:8080/TcpService" binding="netTcpBinding"
                    bindingConfiguration="" name="NetTcpBinding_IProductoServicio"
                    contract="TestWCF.IProductoServicio" />
            </service>
        </services>
    </system.serviceModel>
</configuration>
¡Listo! Ahora solo necesitamos probar todo lo creado, esto lo podemos realizar por medio de nuestra consola de pruebas. Vamos a utilizar el mismo endpoint que utilizamos para probar el servicio en WAS. Lo que vamos a hacer es abrir el archivo app.config de nuestra consola de pruebas y cambiar la dirección del endpoint de la siguiente manera:
<endpoint address="net.tcp://localhost:8080/TcpService"
      binding="netTcpBinding" contract="TestWCF.IProductoServicio"
      name="NetTcpBinding_IProductoServicio" />
Cambiamos un poquito el código para que nos de tiempo de levantar el servicio primero por lo que al principio de nuestra consola añadimos las siguientes dos líneas de código justo antes de instanciar el proxy:
Console.Write("Presione Enter cuando el servicio se encuentre en ejecución.");
Console.ReadLine();

// Se crea el proxy para conectar con el servicio
ProductoServicioClient proxy = new ProductoServicioClient("NetTcpBinding_IProductoServicio")
Ahora habilitemos el inicio de múltiples proyectos para levantar tanto la consola de pruebas como nuestra aplicación WPF que aloja nuestro servicio. Para esto damos clic derecho sobre la solución y seleccionamos la opción Properties del menú emergente. Del lado izquierdo seleccionamos “Starup Project” y del lado derecho hacemos clic en el radiobutton Multiple Startup Projects, seguidamente seleccionamos Start en Action de los dos proyectos que necesitamos inicien.

Ejecutamos.

Iniciamos el servicio en el host y luego damos en enter en la aplicación Cliente.


Como podemos apreciar todo funciona igual a como funcionaba con el servicio alojado el IIS o el WAS, sólo que ahora lo hace desde una aplicación WPF.

Servicio Windows

Ahora finalmente y como tercera opción alojemos nuestro servicio WCF en un servicio Windows. Como fácilmente se puede deducirse al alojar un WCF en una aplicación como en el ejemplo dependería de un usuario el iniciar el servicio y detenerlo. Una forma común de resolver este inconveniente es alojar el WCF en un servicio Windows que inicia automáticamente con el sistema. Se Puede consultar la entrada Construyendo un Servicio Windows para más claridad ya que iremos un poco más rápido en este tópico.

Primeramente creamos nuestro proyecto de servicio Windows. Clic derecho sobre la solución luego Add ->New Project…->Windows Service (se encuentra en la opción Windows del panel de plantillas) yo nombre como ProudctosServicioWindows.

Una vez generado el proyecto renombramos el archivo service1.cs por ServicioHost.cs y hacemos clic en “Yes” cuando se nos pregunte por renombrar todas las referencias. Ahora añadir las siguientes referencias System.ServiceModel, System.Runtime.Serialization y System.Data.Entity. Ademas añadimos la referencia a nuestro proyecto de Modelado de datos (ModeloProductos en mi caso).

Ahora para ahorrarnos el digitar el código, vamos a copiar los archivos del proyecto WCF Library que habíamos hecho antes (ProductoServicioLibrary). De ahí copiamos los archivos IProductoServicio.cs, ProductoServicio.cs y también vamos agregar el archivo app.config de nuestra aplicación de WPF anterior (ProductoServicioHost).

Ahora vamos al servicio Windows para añadir el código necesario para controlar el servicio WCF. Hacemos clic derecho sobre el archivo ServicioHost.cs y elegimos View Code. El código final debe verse similar al siguiente:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.ServiceModel;
using TestWCF;

namespace ProudctosServicioWindows
{
    public partial class ServicioHost : ServiceBase
    {
        //Variable que mantiene la instancia del servicio WCF
        private ServiceHost ProductosHost;
        public ServicioHost()
        {
            InitializeComponent();
            //Nombre del servicio
            this.ServiceName = "ProductosServicio";
            //Permitir al administrador detenero o reiniciarlo
            this.CanStop = true;
            //usar el event log de windows para los procesos de inicio y detencion del servicio
            this.AutoLog = true;
        }

        protected override void OnStart(string[] args)
        {
            ProductosHost = new ServiceHost(typeof(ProductoServicio));
            ProductosHost.Open();
        }

        protected override void OnStop()
        {
            ProductosHost.Close();
        }
    }
}
Ahora añadimos un instalador a nuestro servicio. Hacemos doble clci sobre el archivo ServicioHost.cs para activar la vista de diseño, sobre la misma hacemos clci derecho y elegimos Add Installer

Hacemos clic en el componente serviceInstaller1 recien creado y en su ventana de propiedades configuramos el nombre del servicio y cambiamos el valor de la propiedad startType a Automatic.

 Finalmente seleccionamos el componente ServiceProcessIntaller1 y en la ventana de propiedades cambiamos el valor de Account a LocalSystem y compilamos la solución.

Ahora instalemos el servicio. Abrimos el Visual Studio Command Prompt (2010) y nos vamos a la carpeta en la que creamos el servicio Windows hasta la carpeta Bin/Debug de la misma. Ahí ejecutamos la siguiente instrucción.
installutil ProudctosServicioWindows.exe
Este comando instalará el servicio. Ahora podemos a hasta la consola de servicios Windows e iniciamos nuestro servicio. Clic derecho el servicio y hacemos clic sobre Start.

Con nuestro servicio ejecutándose y sin hacer ningún cambio en nuestra consola de pruebas podemos probar que todo esté bien.

Podemos ver que se comporta exactamente igual que en las demás implementaciones. Para estar seguro que estamos usando el servicio Windows podemos detenerlo y ejecutar de nuevo la consola de pruebas, obtendríamos un error similar al siguiente:

Como hemos visto en estas notas, existen varias posibilidades para alojar nuestro servicio WCF, más de las aquí expuestas. Lo importante es recordar la independencia de la implementación del servicio propiamente dicha de su consumo o alojamiento.

Hasta aquí queda esta serie de notas, cerrando con el post más extenso que he realizado hasta ahora…


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.