7.06.2011

Invocando un Servicio WCF Asincrónicamente–Parte 2

En un post pasado, escribí un artículo acerca de como hacer asincronismo con WCF del lado del cliente a la hora de consumir un servicio. Esta vez voy a continuar con ese post pero haciendo asincronismo del lado del servidor.

Para lograr esto, nos vamos a aprovechar del Task<T>, la cual es una clase que representa una operación asincrónica que puede retornar un valor, tal y como dice la definición del msdn.

Construyendo el Servicio

Para crear un servicio que tenga operaciones asincrónicas, vamos a crear un contrato como normalmente lo hacemos, pero esta vez vamos a agregar el atributo AsynPattern a la operación.Además, vamos a crear la operación de inicio de la misma, y luego la operación de terminación; nótese que solamente se expone como contrato la operación de inicio.

[ServiceContract(Namespace="http://drojasm.net")]
public interface IServicioConsulta
{
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginConsulta(int pTotalPosts, AsyncCallback pCallback, object state);

List<string> EndConsulta(IAsyncResult pResultado);
}

Es importante destacar el patrón del nombre que se debe usar para que el framework entienda la asociación entre ambos métodos.


Ahora procedemos a implementar el servicio. En este caso vamos a crear una propiedad que se llama TotalRegistros, la cual vamos a utilizar para crear los elementos en una estructura for. Luego implementamos el método BeginConsulta. En este método se recibe como parámetro la dirección del método en el cual se va a finalizar la llamada asincrónica; además, se recibe el estado del objeto, el cual para este post no hará mucha diferencia. Por otro lado, vamos a crear una nueva instancia del proceso asincrónico utilizando el método Factory.StartNew el cual recibe como parámetros el método que se inicia asincrónicamente y el estado actual del request. Por último le indicamos al método que continúe su ejecución en el puntero al método enviado a través de pCallback, el cual es EndConsulta. EndConsulta lo que hace es retornar el resultado de la operación ejecutada.

public class ServicioConsulta : IServicioConsulta
{
public int TotalRegistros { get; set; }

public IAsyncResult BeginConsulta(int pTotalRegistros, AsyncCallback pCallback, object pState)
{
TotalRegistros = pTotalRegistros;
var task = Task<List<string>>.Factory.StartNew(this.ObtenerResultado, pState);
return task.ContinueWith(res => pCallback(task));
}

public List<string> EndConsulta(IAsyncResult pResultado)
{
return ((Task<List<string>>)pResultado).Result;
}

private List<string> ObtenerResultado(object pState)
{
List<string> _lista = new List<string>();

for (int i = 0; i < TotalRegistros; i++)
{
_lista.Add("Registro " + i.ToString());
}
return _lista;
}
}

Ahora vamos a agregar la referencia al servicio. Dado que el servicio retorna una lista genérica de strings, el proxy convierte estas listas a un arreglo de strings tradicional string[], algo que podemos modificar si el cliente que vamos a utilizar es WCF. Para esto, vamos al proxy generado a la hora de agregar la referencia y cambiamos los tipos de retorno del método de string[] a List<string>

[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
public List<string> EndConsulta(System.IAsyncResult result) {
return base.Channel.EndConsulta(result);
}
public List<string> Consulta(int pTotalPosts) {
return base.Channel.Consulta(pTotalPosts);
}
[System.ServiceModel.OperationContractAttribute(Action="http://drojasm.net/IServicioConsulta/Consulta", ReplyAction="http://drojasm.net/IServicioConsulta/ConsultaResponse")]
List<string> Consulta(int pTotalPosts);
List<string> EndConsulta(System.IAsyncResult result);

Luego de esto procedemos a crear una pantalla en la aplicación WPF que luce como se muestra a continuación:


image


El código XAML de este ejemplo es el siguiente:

<Grid>
<
Label Content="Ítems Asincrónicos" Height="28" HorizontalAlignment="Left" Margin="35,28,0,0" Name="label1" VerticalAlignment="Top" />
<
TextBox Height="23" HorizontalAlignment="Left" Margin="36,54,0,0" Name="txtCantidad" VerticalAlignment="Top" Width="69" />
<
Button Content="Invocar" Height="23" HorizontalAlignment="Left" Margin="35,129,0,0" Name="btnInvocar" VerticalAlignment="Top" Width="75" Click="btnInvocar_Click" />
<
ListBox Height="157" HorizontalAlignment="Left" Margin="221,41,0,0" Name="lstItems" VerticalAlignment="Top" Width="120" />
<
Label Content="Ítems Asincrónicos" Height="28" HorizontalAlignment="Left" Margin="221,21,0,0" Name="label2" VerticalAlignment="Top" />
<
ListBox Height="157" HorizontalAlignment="Left" Margin="367,41,0,0" Name="lstSincronicos" VerticalAlignment="Top" Width="120" />
<
Label Content="Ítems Sincrónicos" Height="28" HorizontalAlignment="Left" Margin="367,21,0,0" Name="label3" VerticalAlignment="Top" />
<
TextBox Height="23" HorizontalAlignment="Left" Margin="35,100,0,0" Name="txtSincronicos" VerticalAlignment="Top" Width="70" />
<
Label Content="Ítems Sincrónicos" Height="28" HorizontalAlignment="Left" Margin="33,74,0,0" Name="label4" VerticalAlignment="Top" />
</
Grid>

Ahora vamos a proceder con el código del evento del botón llamado btnInvocar. En este botón primero vamos a llamar al método WCF de forma asincrónica, y luego, mientras este método se procesa invocamos el mismo método de forma sincrónica. Como este último bloquea la ejecución cuando este termina, el método asincrónico ya ha terminado igualmente, con lo que a la hora de hacer databinding a las listas correspondientes, ambas se llenarán casi simultáneamente.

private void btnInvocar_Click(object sender, RoutedEventArgs e)
{
int _cantidadAsincronicos = int.Parse(txtCantidad.Text);
int _cantidadSincronicos = int.Parse(txtSincronicos.Text);

var _proxy = new ServicioConsultaClient();
object _estado = "No tiene relevancia ahorita";
var Task = Task<List<string>>.Factory.FromAsync(_proxy.BeginConsulta, _proxy.EndConsulta, _cantidadAsincronicos, _estado);

//Mientras invocamos sincronicamente
List<string> _resultado = _proxy.Consulta(_cantidadSincronicos);
//Luego vamos por el resultado
List<string> _resultadoAsincronico = Task.Result;
//Cerramos el proxy
_proxy.Close();
lstItems.ItemsSource = _resultadoAsincronico;
lstSincronicos.ItemsSource = _resultado;

}

El resultado a la hora de ejecutar el código anterior es el siguiente:


image


Etiquetas de Technorati:

No hay comentarios: