6.29.2011

Invocando un Servicio WCF Asincrónicamente

En WCF existen varias formas para consumir un servicio de forma asincrónica. La más simple quizás es poner el asincronismo del lado del cliente, que es lo que vamos a hacer en este post.

Para iniciar vamos a crear un proyecto del tipo WCF ServiceLibrary y vamos a crear un servicio muy simple que tiene solamente un método que retorna el número de frutas – strings – que se lo soliciten vía parámetro a la operación. La interface – contrato – del servicio se ve a continuación:

[ServiceContract(Namespace="http://drojasm.net")]
public interface IServicioProductos
{
[OperationContract]
List<string> ObtenerProductos(int pCantidad);
}

Seguidamente procedemos con el código para implementar la operación. Como podemos ver en el siguiente código, este simplemente lo que hace es hacer un for y agregar un string a la lista por cada iteración. Nótese además la línea en donde ponemos a dormir el thread del servicio, esto con el fin de que nos de tiempo de hacer algo diferente en el UI mientras el servicio se ejecuta.

    public class ServicioProductos : IServicioProductos
{
public List<string> ObtenerProductos(int pCantidad)
{
List<string> _productos = new List<string>();

for (int i = 0; i < pCantidad; i++)
{
_productos.Add("Fruta Número " + i.ToString());
}

System.Threading.Thread.Sleep(5000);
return _productos;

}
}
}

Ahora vamos a proceder a crear el cliente que va a consumir el servicio. En este caso vamos a utilizar una aplicación WPF. Lo primero que vamos a hacer después de crear la aplicación es agregar una referencia al servicio como se hace tradicionalmente; es decir, botón derecho sobre el proyecto, seleccionar agregar referencia, y en la pantalla de configuración de la referencia del servicio, poner la dirección del servicio – en este caso hosteado en el wcfsvchost – y por último obtener el wsdl del mismo.


image


Sin embargo, esta vez vamos a seleccionar además el botón de “Advanced” para configurar la generación del proxy. Luego en esta pantalla vamos a configurar la generación del proxy, seleccionando en esta la opción para generar clientes asincrónicos. Esta opción nos va a generar además de los métodos tradicionales sincrónicos, los métodos necesarios para invocar el servicio utilizando asincronismo.


image


Luego seleccionamos Ok en las siguientes dos pantallas y se procede con la generación del proxy. Como podemos ver en la siguiente figura, el proxy genera los métodos para consumir asincrónicamente el servicio.


image


Ahora procedemos a crear la pantalla en WPF para invocar el servicio. Para esto, vamos a agregar un listbox en donde pintamos el resultado de cada servicio. Además vamos a poner un textbox para que el usuario – yo Sonrisa - digite cuantos elementos quiere en la lista. También vamos a poner dos botones, uno para invocar el servicio sincrónicamente y otro asincrónicamente. Por último, vamos a poner un botón que va a lanzar un MessageBox cuando se le da click; el objetivo de este es demostrar que cuando invocamos el servicio de forma asincrónica, podemos llevar a cabo otras tareas mientras que cuando lo hacemos de forma sincrónica la pantalla se bloquea. El XAML de la pantala es el siguiente:

<Window x:Class="ClienteAsincronicoWCF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="447">
<
Grid>
<
ListBox Height="197" HorizontalAlignment="Left" Margin="47,74,0,0" Name="lstFrutas" VerticalAlignment="Top" Width="120" />
<
Label Content="Frutas" Height="28" HorizontalAlignment="Left" Margin="47,40,0,0" Name="label1" VerticalAlignment="Top" />
<
Button Content="Cargar Sincronicamente" Height="23" HorizontalAlignment="Left" Margin="213,220,0,0" Name="btnSincronico" VerticalAlignment="Top" Width="140" Click="btnSincronico_Click" />
<
Button Content="Cargar Asincronicamente" Height="23" HorizontalAlignment="Left" Margin="213,249,0,0" Name="btnAsincronico" VerticalAlignment="Top" Width="140" Click="btnAsincronico_Click" />
<
Label Content="Cantidad de Productos" Height="28" HorizontalAlignment="Left" Margin="213,146,0,0" Name="label2" VerticalAlignment="Top" />
<
TextBox Height="23" HorizontalAlignment="Left" Margin="213,180,0,0" Name="txtCantidad" VerticalAlignment="Top" Width="140" />
<
Button Height="75" HorizontalAlignment="Left" Margin="269,40,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click">
<
Button.Content>
<
StackPanel>
<
Image Height="48" Name="image1" Stretch="Fill" Width="48" Source="/ClienteAsincronicoWCF;component/pens.png" />
<
Label Content="Otra Tarea" Height="28" Name="label3" />
</
StackPanel>
</
Button.Content>
</
Button>
</
Grid>
</
Window>


Y la pantalla en modo de diseño es la siguiente:


image


El código del botón para la invocación sincrónica del servicio es el siguiente:

private void btnSincronico_Click(object sender, RoutedEventArgs e)
{
lstFrutas.ItemsSource = null;
ServicioProductosClient _proxy = new ServicioProductosClient();
lstFrutas.ItemsSource = _proxy.ObtenerProductos(int.Parse(txtCantidad.Text));
}

No olvidar agregar el namespace del servicio – el que digitamos en la pantalla para agregar la referencia al mismo.

using ClienteAsincronicoWCF.ReferenciaServicioProductos;

Como podemos ver en el código del botón, la forma de invocar este servicio es la tradicional; este procedimiento bloquea la pantalla y no permite realizar ninguna otra tarea mientras la invocación al servicio se esta ejecutando.


image


Ahora procedemos a agregar el código de la llamada asincrónica.

private void btnAsincronico_Click(object sender, RoutedEventArgs e)
{
lstFrutas.ItemsSource = null;
ServicioProductosClient _proxy = new ServicioProductosClient();
AsyncCallback _callBack =
delegate(IAsyncResult pResult)
{
this.Dispatcher.BeginInvoke((Action)delegate { lstFrutas.ItemsSource = _proxy.EndObtenerProductos(pResult); });
};

_proxy.BeginObtenerProductos(int.Parse(txtCantidad.Text), _callBack, _proxy);
}

En este código nos vamos a detener un momento para analizarlo un poco mas detalladamente. En primera instancia procedemos a crear un delegate en donde vamos a obtener la respuesta del servicio. En este delegate además, debemos hacer un llamado a la operación BeginInvoke porque el thread en donde se invoca el proceso es diferente al thread del UI, por lo tanto no se puede asignar el resultado directamente a la lista. Por último, invocamos el servicio de forma asincrónica utilizando el método BeginObtenerProductos.


En el botón del MessageBox tenemos el siguiente código:

private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Iniciando otra operación");
}

Ahora procedemos a ejecutar el servicio con el botón btnAsincronico. Como podemos ver en la siguiente imagen, se pudo invocar al messageBox mientras el servicio estaba procesándose.


image


En realidad este método puede ser reemplazado fácilmente utilizando threads del lado del cliente. Sin embargo, esta es una forma muy simple de obtener asincronismo con WCF en el cliente. En post posteriores vamos a analizar el asincronismo del lado del servicio WCF.


Por último, también es relevante aclarar que una cosa es el asincronismo y otra cosa el paralelismo – tema que espero poder abarcar en post posteriores.


Etiquetas de Technorati: ,,

3 comentarios:

César Abreu dijo...

Hola Diego. Interesante post. A mí se suele resultar más sencillo usar la variante ObtenerProductosCompleted para establecer el callback y luego llamar a ObtenerProductosAsync.

Siempre me ha parecido que queda más sencillo y más legible.

Diego Rojas dijo...

Hola César,
gracias por leer el blog. Efectivamente, existen varias formas de hacer el llamado asincrónico. En próximos post voy a enforcarme en el asincronismo pero del lado del servidor.

Saludos

José Benítez dijo...

Interesante Diego, sabes que soy nuevo en la programación asincorna estoy en eso tal vez podrias subir el proyecto completo con el codigo para poder probarlo, yo necesito conectarme asincronamente por ssh a multiples servers pero con visual basic.net