7.27.2011

Métodos de Extensión en .NET

¿A quién no le ha pasado la siguiente situación?

Empiezo a programar un método para que haga X cosa, de pronto me doy cuenta que necesito una función que no existe en esa clase. ¿Qué hago? Si puedo heredarla, la heredo y agrego el método, con el consiguiente problema de que si requerimos que todos los desarrolladores la usen, deben de olvidarse de utilizar la clase “estándar” y empezar a usar la clase que acabo de crear. Por otro lado, si la clase esta sellada –sealed – no la puedo heredar, lo que conlleva a crear alguna clase “Helper” que tenga el método y pueda ser invocada. Por ejemplo, supongamos que necesito saber si un string es numérico o no. En este caso procedo a crear un método en una clase Helper dado que la clase string esta sellada en .NET – es decir, no se puede heredar.

image

Ahora, para utilizar este método estático, simplemente lo hago de la siguiente forma:

image

El problema con esta solución es que todo el mundo debe de estar consciente que esta clase existe y que se puede reutilizar – esa palabra por la cual todos vivimos pero casi nunca logramos aplicar de forma constante en nuestros desarrollos – por lo cual es muy probable que veamos la misma solución aplicada de diversas formas en otros proyectos internos.

La Solución

Sin embargo, a partir de la versión del framework 3.5 se agregó una característica que personalmente utilizo mucho y pienso que es de gran utilidad: los métodos de extensión. Esto métodos de extensión son métodos que pueden formar parte de una clase y pueden ser utilizados directamente desde la instancia o el tipo de la misma. Esta característica se utiliza muchísimo sobre todo en el desarrollo de LINQ. Para entender este concepto vamos a modificar el ejemplo anterior para convertirlo en un método de extensión. Lo único que tenemos que hacer es modificar la forma en que declaramos el parámetro del método IsNumeric, tal y como se puede ver en el siguiente código:

image

Como podemos ver en el código anterior, lo único que hice fue agregarle la palabra reservada this al parámetro del método. Esto convierte el método en una extensión de la clase string, con lo cual puedo utilizarlo directamente en las instancias de este tipo de dato.

image

Etiquetas de Technorati: ,

7.26.2011

Manejo de Errores con el ASP.NET MVC

Una de las características más interesantes del MVC es el manejo de errores que tiene el framework. Por defecto, el MVC trae una vista para el manejo de errores en el campo visual, la cual se despliega cada vez que se produce un error en el sitio Web. Si vemos la estructura de nuestro proyecto al crearlo usando MVC 3, nos vamos a dar cuenta que en el folder Shared de las vistas vamos a ver una vista llamada Error.aspx.

image

En este caso, cambiamos el texto de la vista Error.aspx para que despliegue un mensaje un poco más “amigable” al usuario final.

image

Ahora procedemos a “provocar” un error en el sitio Web para verificar que el despliegue de la vista funcione. Para esto vamos a solicitar una vista que no exista en el método Index del HomeController.

image

Si ejecutamos el código no nos aparece la vista de errores que modificamos anteriormente, pero ¿por qué?

imageLo que nos faltó habilitar fue la propiedad CustomErrors=On en el web.config. Esto habilita el mecanismo para desplegar errores de forma segura y “amigable” en nuestras aplicaciones ASP.NET MVC.

image

Ahora si ejecutamos de nuevo la aplicación provocando el error, podemos ver la vista que modificamos desplegada.

image

Sin embargo, el Framework del MVC no se queda ahí. Este nos permite ser más granulares en lo que respecta al manejo de errores. Por ejemplo, podemos definir un tipo de vista que se pueda utilizar en las acciones que yo específicamente le indico. Vamos a crear una nueva vista, que llamaremos “ErroresPersonalizados”. Esta lista estará tipificada con el tipo HandleErrorInfo del System.Web.Mvc. Este tipo será el que utilicemos en el modelo de la vista; es decir, cuando se invoca la vista, se le pasa en el modelo, la variable que se produjo en el error – en este caso la excepción.

image

La vista generada esta tipificada con el tipo que le indicamos en el model class.

image

Ahora vamos a modificar la vista para presentar de forma personalizada la excepción, nótese que ahora el modelo tiene todas las propiedades de la excepción que se va a producir.

image

El script HTML que vamos a usar en esta forma es el siguiente:

image

Ahora en la acción Index del controlador, vamos a especificar vía un ActionFilter, la vista de manejo de error que se debe de mostrar en caso de que ocurra un error en la misma.

image

Al ejecutar el código anterior, la vista que nos aparece es la siguiente:

image

Si se produce un error en cualquier otra acción de cualquier otro controlador se presentará la vista estándar – Error.aspx – pero si se produce un error en la acción Index del HomeController se verá la vista anterior.

Pero el MVC va más allá en el tema de la granularidad del manejo de errores. Supongamos que queremos que todas las acciones desplieguen los errores en la vista estándar, solo que deseamos que si la acción Index del HomeController lanza una excepción del tipo InvalidOperationException, entonces se invoque la vista anterior. Para esto, procedemos a modificar el atributo de manejo de errores con la siguiente información:

image

En el código anterior se lanza un error del tipo InvalidOperationException en el código para probar que la vista adecuada sea la que se presente. Si en esa acción se produce cualquier otra excepción, se presenta la vista estandar Error.aspx y no la vista ErroresPersonalizados.aspx

Etiquetas de Technorati: ,

7.20.2011

Charla de Cloud Computing

Los quiero invitar para que el próximo 4 de agosto me acompañen en la charla de Cloud Computing: Principios y Arquitectura. La charla es gratuita y el lugar donde se va a llevar a cabo es el Auditorio de la Bolsa Nacional de Valores en Forum 1. Les dejo la invitación para que le puedan echar un vistazo a el contenido de la misma.

InvitacionSOACC

Etiquetas de Technorati: ,

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: