ASP.NET MVC3 – Filtros que no son atributos

Antes que nada una nota: Este post está basado en la preview 1 de ASP.NET MVC3. Todo lo que comento puede cambiar en futuras previews de MVC3.

En el post de ayer comentaba las novedades de ASP.NET MVC3 preview1 y una de las mejoras más interesantes es el soporte de inyección de dependencias para los filtros. Eso se consigue con el uso de una nueva interfaz IFilterProvider y esa interfaz nos trae de regalo, una posibilidad adicional: Tener filtros que no sean atributos.

Veamos un ejemplo y como podríamos usar este nueva capacidad en MVC3.

1. Que queremos?

Queremos poder usar el concepto de filtros de MVC, pero sin usar atributos. Es decir sin tener que decorar los controladores o sus acciones con atributos tipo [Authorization].

Vamos a proporcionar una mini api de configuración, para que pueda configurar los filtros, usando una api fluent:

FluentFilterProvider ffp = new FluentFilterProvider();
ffp.AddForController<HomeController, MyCustomFilter>().ForAction("Index");
ffp.AddForController<HomeController, MyOtherCustomFilter>();

Aquí configuro dos filtros para el controlador Home: Uno para la acción “Index” (MyCustomFilter) y otro para todas sus acciones (MyOtherCustomFilter).

2. Creación del esqueleto de lo que necesitamos

El primer paso es crearnos nuestro propio proveedor de filtros, es decir nuestra clase que implemente la interfaz IFilterProvider. Nada más sencillo que esto:

public class FluentFilterProvider : IFilterProvider
{
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
return null;
}
}

3. Creación de la interfaz de configuración fluent

La idea es que nuestro proveedor de filtros tenga una colección de IFilterConfigItem y cada IFilterConfigItem tenga la información para saber cuando instanciar el filtro (es decir a que controlador y acción afecta y cual es la clase que implementa el filtro).

Como no sabemos si eso puede complicarse más, definimos una clase FluentFilterProviderConfig que mantenga esta colección:

class FluentFilterProviderConfig
{
private List<FilterConfigItem> _items;

public FluentFilterProviderConfig()
{
_items = new List<FilterConfigItem>();
}

internal ICollection<FilterConfigItem> Items
{
get { return _items; }
}
}

La colección es de objetos de la clase FilterConfigItem, esta clase es la que implementa IFilterConfigItem. Veamos primero como está definido la interfaz IFilterConfigItem:

public interface IFilterConfigItem
{
void ForAction(string actionName);
}

Un solitario método ForAction que nos permite indicar que dicho filtro se aplica a una acción en concreto.

Y ahora veamos la clase FilterConfigItem:

class FilterConfigItem : IFilterConfigItem
{
private Type _filterType;
private Type _controllerType;
private string _actionName;

internal FilterConfigItem(Type tf, Type controllerType)
{
_filterType = tf;
_controllerType = controllerType;
_actionName = null;
}

internal Type ControllerType { get { return _controllerType; } }
internal Type FilterType { get { return _filterType; } }
internal string ActionName { get { return _actionName; } }

public void ForAction(string actionName)
{
_actionName = actionName;
}
}

Dicha clase guarda toda la información requerida:

  • El tipo de la clase que implementa el filtro (_filterType)
  • El tipo del controlador al que se aplica el filtro (_controllerType)
  • El nombre de la acción a la que se aplica el filtro (_actionName). Este valor es opcional (si vale null el filtro se aplicará a todas las acciones).

Tenemos también propiedades para acceder a esta información, pero fijaos que son internal y no forman parte de la interfaz. Esto es porque esas propiedades sólo son para la implementación de la API, no para su uso público.

Finalmente en la clase FluentFilterProvider vamos a guardar un objeto con la configuración y vamos a proporcionar el punto de entrada a la API fluent: el método AddForController:

public class FluentFilterProvider : IFilterProvider
{
private FluentFilterProviderConfig _cfg;

public FluentFilterProvider()
{
_cfg = new FluentFilterProviderConfig();
}

public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
// Ya veremos que metemos aquí...
}

public IFilterConfigItem AddForController<TC, TF>()
where TC : IController
where TF : class
{
FilterConfigItem fci = new FilterConfigItem(typeof(TF), typeof(TC));
_cfg.Items.Add(fci);
return fci;
}
}

Fijaos que el método AddForController crea un objeto FilterConfigItem, lo añade a la colección y lo devuelve. Esta devolución es lo que permite encadenar las llamadas (característica básica de una interfaz fluent).

4. Creación del filter provider

Bien, sólo nos queda terminar de implementar la clase FluentFilterProvider con la implementación del método GetFilters. El código es muy sencillo:

public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
var items = _cfg.Items.Where(x => x.ControllerType == controllerContext.Controller.GetType()
&& (x.ActionName == null || x.ActionName.Equals(actionDescriptor.ActionName)));
foreach (var item in items)
{
var sl = MvcServiceLocator.Current;
var filterInstance = sl.GetInstance(item.FilterType);
yield return new Filter(filterInstance, FilterScope.Action);
}
}

Nos recorremos la colección de FilterConfigItems y por cada uno miramos si aplica al controlador pedido y a la acción indicada (los parámetros controllerContext y actionDescriptor nos dan información sobre el controlador y la acción para la cual debemos encontrar los filtros).

Y por cada elemento FilterConfigItem encontrado:

  1. Creamos la instancia del objeto que implementa el filtro. Fijaos que lo hacemos usando MvcServiceLocator.Current: eso permite aplicar inyección de dependencias.
  2. Creamos un objeto Filter (el objeto que espera el framework), lo rellenamos con los datos y lo devolvemos.

Y listos! Nuestro filter provider está listo para usar… Sólo nos queda crearlo, configurarlo y registrarlo en MVC:

FluentFilterProvider ffp = new FluentFilterProvider();
ffp.AddForController<HomeController, MyCustomFilter>().ForAction("Index");
ffp.AddForController<HomeController, MyOtherCustomFilter>();
FilterProviders.Providers.Add(ffp);

Recordad que las clases MyCustomFilter y MyOtherCustomFilter son las clases que implementan los filtros (son las que implementan IActionFilter, IAuthorizationFilter, IResultFilter o IExceptionFilter).

No se que os parece a vosotros, pero a mi me parece una pasada!!! 🙂

Un saludo!!!!

ASP.NET MVC 3 – Preview 1

Ayer saltaba la noticia en el blog de scottgu: la preview 1 de ASP.NET MVC3 ya está disponible para descargar. En fin, podríamos discutir largo y tendido sobre la política de actualizaciones a lo bestia de las APIs que está realizando microsoft desde hace algún tiempo, pero como cada uno tendría su opinión, mejor vamos a ver las novedades que trae esa preview 1. Antes que nada, podéis instalarla sin miedo: se instala side by side con MVC2 y además los proyectos que ya teníais no se ven afectados.

Una vez instalado MVC3, aparecen tres nuevos tipos de proyectos en VS2010: ASP.NET MVC 3 Web Application (ASPX), ASP.NET MVC 3 Web Application (Razor) y ASP.NET MVC 3 Emtpy Web Application. No hay soporte para VS2008 puesto que MVC3 usa el .NET Framework 4. Vamos a ver las novedades que tiene esta preview1 de MVC3 🙂

1. Razor View Engine

El nuevo Razor viene incluído en MVC3. Razor no es nada más que otro ViewEngine para MVC. Ya se ha hablado largo y tendido de Razor porque se incluye también en WebMatrix. Si bien en WebMatrix, Razor venía acompañado del concepto de ASP.NET WebPage, en ASP.NET MVC dicho concepto carece de sentido y Razor es simplemente otro ViewEngine más.

Razor ofrece una sintaxis alternativa a la sintaxis clásica de <% %> tan típica de ASP.NET MVC, pero no ofrece nada nuevo que no ofrezcan el ViewEngine de .aspx u otros como Nhaml o Spark.

Mi “decepción” ha sido que de hecho, tanto si usamos el ViewEngine de .aspx como si usamos Razor, las clases de las vistas derivan de System.Web.Mvc.ViewPage<> (yo tenía la esperanza de que Razor ofreciera un framework mucho más ligero, puesto que ViewPage deriva de System.Web.UI.Page que tiene muchas propiedades y métodos que tienen sentido en WebForms pero sólo añaden ruído en MVC).

P.ej. en Razor para mostrar una lista de elementos (FooItem) que tuviesen dos propiedades llamadas First y Second usaríamos:

@inherits System.Web.Mvc.WebViewPage<IEnumerable<MvcApplication1.Models.FooItem>>

@{
View.Title = "TableView";
LayoutPage = "~/Views/Shared/_Layout.cshtml";
}

<h2>TableView</h2>

<ul>
@foreach (var item in Model)
{
<li>@item.First - @item.Second</li>
}
</ul>

Este es el código entero de la vista: como veis es mucho más compacto que su equivalente en .aspx. ¿Cual preferís? Es una cuestión de gustos, pero según comenta Scott en su post, será posible probar templates de Razor de forma individual sin necesidad de tener un servidor web, lo que ayudará a la generación de pruebas… Por lo que parece esto no está previsto para el ViewEngine de .aspx.

Las vistas en Razor (usando c#) tienen la extensión .cshtml y de momento el soporte en vs2010 de Razor es muy limitado en esta preview1: Ni sintaxis coloreada, ni intellisense… 🙂

2. Mejor soporte para DI

MVC2 ya tenía un soporte más que decente para dependency injection, pero en MVC3 lo han mejorado: no sólo han simplificado la tarea de usar DI sinó que ahora también se soporta la inyección de dependencias en los filtros.

El soporte para contenedores IoC se basa en la interfaz IServiceLocator y como es uno de los puntos más interesantes a mi parecer, dejadme que me extienda un poco 🙂

Primero, antes que nada, en la preview1, la interfaz IServiceLocator está definida dentro del propio assembly de System.Web.Mvc.dll, en lugar de usar el del ensamblado Microsoft.Practices.ServiceLocation.dll. Esto genera algunos problemas y es algo que está previsto que cambie en futuras previews.

P.ej. si usamos Unity 2.0, para obtener el IServiceLocator nos basta con hacer:

IUnityContainer iuc = new UnityContainer();
Microsoft.Practices.ServiceLocation.IServiceLocator ul = new UnityServiceLocator(iuc);

La idea es el IServiceLocator que hemos obtenido (ul) lo podamos usar en MVC3, pero por ahora es imposible. La razón es la que comentaba: MVC3 define su propio IServiceLocator en lugar de utilizar el que viene en Microsoft.Practices.ServiceLocation.dll. Así pues, de momento, estamos obligados a implementarnos nuestro “propio” IServiceLocator:

public class MvcUnityServiceLocation : IServiceLocator
{
private readonly IUnityContainer _container;

public MvcUnityServiceLocation(IUnityContainer ctr)
{
_container = ctr;
}


public IEnumerable<object> GetAllInstances(Type serviceType)
{
return _container.ResolveAll(serviceType);
}

public IEnumerable<TService> GetAllInstances<TService>()
{
return _container.ResolveAll<TService>();
}

public object GetInstance(Type serviceType, string key)
{
return _container.Resolve(serviceType, key);
}

public object GetInstance(Type serviceType)
{
return _container.Resolve(serviceType);
}

public TService GetInstance<TService>(string key)
{
return _container.Resolve<TService>(key);
}

public TService GetInstance<TService>()
{
return _container.Resolve<TService>();
}

public object GetService(Type serviceType)
{
return _container.Resolve(serviceType);
}
}

La interfaz que estamos implementando NO ES Microsoft.Practices.IServiceLocator sino System.Web.Mvc.IServiceLocator. Como digo eso es algo que se supone cambiará en futuras previews.

Ahora ya podemos registrar este IServiceLocator, usando la clase MvcServiceLocator (esto se suele hacer en el Application_Start):

IUnityContainer iuc = new UnityContainer();
System.Web.Mvc.IServiceLocator ul = new MvcUnityServiceLocation(iuc);
MvcServiceLocator.SetCurrent(ul);

Bueno… Con eso establecemos cual va a ser el ServiceLocator que usará MVC para instanciar sus objetos. Ahora debemos configurarlo. Una cosa que me he encontrado es que debemos establecer la factoría de controladores explicitamente en el contenedor de IoC:

iuc.RegisterType<IControllerFactory, DefaultControllerFactory>
(new ContainerControlledLifetimeManager());

Aquí estoy diciendo a MVC que use DefaultControllerFactory como factoría de controladores, y la forma de hacerlo es simplemente establecer un mapping entre IControllerFactory y DefaultControllerFactory en mi contenedor IoC.

Y aquí viene la mejora: DefaultControllerFactory ya está preparada para usar el IServiceLocator, por lo que ya tenemos DI en los controladores. Sin hacer nada más (antes uno debía crearse su propia IControllerFactory). Podemos establecer un mapping cualquiera en nuestro contenedor:

iuc.RegisterType<IFoo, Foo>();

Y ya podemos usar IFoo sin ningún problema en nuestros controladores:

public class HomeController : Controller
{
public HomeController(IFoo foo)
{
// ...
}
}

Cada vez que se cree un HomeController se le inyectará el parámetro IFoo.

Antes he comentado que una de las novedades de MVC3 es el soporte de inyección de dependencias para los filtros… Los que conozcais un poco ASP.NET MVC seguramente os estareis preguntando cómo, dado que los filtros son clases que heredan de Attribute y por lo tanto es el propio runtime de .NET quien los crea, sin que el contenedor de IoC pueda intervenir…

Para ayudar a la inyección de dependencias en filtros, en MVC3 se han inventado el concepto del proveedor de filtros, representado por la interfaz IFilterProvider y que es el responsable de obtener instancias de todos los filtros que necesite el framework. Nosotros ahora podemos crear nuestra propia clase que implemente IFilterProvider y que aplique la inyección de dependencias. Veamos primero como está definido IFilterProvider:

public interface IFilterProvider
{
IEnumerable<Filter> GetFilters(ControllerContext controllerContext,
ActionDescriptor actionDescriptor);
}

Un solo método GetFilters que es el encargado de devolver el conjunto de filtros necesarios cada vez. La clase Filter no es el filtro en sí, sinó una clase de metadatos que contiene una referencia al objeto que implementa el filtro (en nuestro caso el atributo), además de información sobre el orden (que ya existía en MVC2) y el àmbito del filtro (p.ej. si es un filtro que se aplica a un controlador, a una acción,…). Los filtros se ejecutan unos antes de otros en función de su ámbito y de su orden.

Bien, si queremos implementar filtros usando atributos podemos hacerlo igual que en MVC2 derivando de FilterAttribute:

public class CustomActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
}
}

Nada nuevo aquí, lo nuevo viene ahora: Podemos crear un IFilterProvider que nos inyecte las dependencias a los filtros. Este IFilterProvider, obviamente, no creará los filtros (puesto que son atributos y los crea el runtime de .NET). Entonces que hace? Pues tres cosas:

  1. Recoge los objetos que implementan los filtros (creados por el runtime)
  2. Por cada objeto le inyecta las dependencias. Para ello necesitamos que el contenedor de DI soporte inyectar dependencias a objetos ya existentes y usar inyección de propiedades.
  3. Finalmente crea el objeto Filter que contendrá el objeto con las dependencias creadas.

Un ejemplo, usando Unity:

public class UnityFilterAttributeFilterProvider : FilterAttributeFilterProvider
{
private IUnityContainer _container;

public UnityFilterAttributeFilterProvider(IUnityContainer container)
{
_container = container;
}
protected override IEnumerable<FilterAttribute> GetControllerAttributes(
ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
var attributes = base.GetControllerAttributes(controllerContext, actionDescriptor);
foreach (var attribute in attributes)
{
_container.BuildUp(attribute.GetType(), attribute);
}
return attributes;
}

protected override IEnumerable<FilterAttribute> GetActionAttributes(
ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
var attributes = base.GetActionAttributes(controllerContext, actionDescriptor);
foreach (var attribute in attributes)
{
_container.BuildUp(attribute.GetType(), attribute);
}
return attributes;
}
}

Derivamos de FilterAttributeFilterProvider (que implementa IFilterProvider) y usamos Unity para inyectar las dependencias (BuildUp) a los objetos que implementan los filtros.

Y ahora? Pues como siempre: registrar este FilterAttributeProvider en el framework a través del Applicaton_Start de global.asax:

var provider = new UnityFilterAttributeFilterProvider(container);  
FilterProviders.Providers.Add(provider);

Por defecto hay 3 IFilterProviders instalados en el framework (en la colección Providers):

  1.  
    1. Un filter provider que se encarga de devolver los controladores (sí, los controladores son filtros también (desde siempre)).
    2. Un filter provider que se encarga de devolver los filtros que son atributos
    3. Un filter provider que se encarga de devolver los filtros globales

Dado que nosotros estamos añadiendo otro filter provider que sustituye al que viene por defecto al que se encarga de devolver los filtros que son atributos, no estaría de más eliminar el que viene por defecto antes de registrar el nuestro (aunque funciona si no lo hacemos):

FilterProviders.Providers.Remove(FilterProviders.Providers.FirstOrDefault(x => x is FilterAttributeFilterProvider));

Y listos! Ahora ya tenemos inyección de dependencias en nuestros filtros! Recordad que debemos usar inyección de propiedades, dado que los filtros son creados por el runtime de .NET. Es decir algo como:

public class CustomActionFilterAttribute : ActionFilterAttribute
{
// Esta propiedad la inyecta Unity...
[Dependency]
public IFoo Foo { get; set; }
// ...
}

Que os parece? No es mucho trabajo para el beneficio que obtenemos!

Otra ventaja del uso de filter providers es poder tener filtros que no sean atributos, algo que no estaba permitido en MVC2: cualquier “cosa” que el filter provider devuelva será considerado un filtro para el framework.

3. Otras mejoras menores

Ahora listo brevemente un conjunto de “mejoras menores” que pese a no suponer un avance brutal sirven para simplificarnos el trabajo o mejorar el código:

  1. Soporte para objetos JSON en POST: MVC3 es capaz de tratar datos que están en POST en formato JSON y usarlos cuando hacemos binding del viewmodel. MVC2 no era capaz, aunque no era muy complejo añadir un custom value provider que diera esa capacidad. De hecho en MvcFutures ya estaba y es lógico que haya entrado en esta versión.
  2. Nueva propiedad ViewModel: ViewModel es una propiedad declarada como dynamic que permite usar la antigua ViewData de forma tipada. Es decir en lugar de hacer ViewData[“Foo”] = new Bar(); podemos hacer ViewModel.Foo = new Bar(); y el resultado es equivalente. Esto mejora la legibilidad y facilita refactorings.
  3. Soporte del interfaaz IValidatableObject: Esta interfaz (propia de .NET 4) tiene un sólo método (Validate) que determina si un objeto es “válido”. El model binder por defecto ahora usa ese método si el viewmodel implementa dicha interfaz.
  4. Nuevos ActionResults: Para devolver un 404 (HttpNotFoundResult), o un código específico de HTTP (HttpStatusCodeResult).
  5. Filtros globales: Filtros que se aplican a todos los controladores de nuestra aplicación de forma automática.

Bueno… como podeis ver MVC3 viene con un buen puñado de novedades, muchas de ellas pequeñitas pero sin duda alguna interesantes… larga, larga, larga vida a MVC!!! 🙂

Referencias

El post de ScottGu explicando MVC3

El post de Brad Wilson explicando la DI en filtros

[C# Básico] Delegates

Hola a todos! Este es el tercer post de esa “serie” de C# Básico. En el primero vimos las interfaces y en segundo intenté responder a la pregunta de que es la herencia.

Hoy quiero hablaros de un tema sobre el que bastante gente tiene dificultades y sobre el que hay bastante confusión, pero que en el fondo es mucho más simple de lo que parece! Sí, me estoy refiriendo a los delegates (o delegados).

1. La necesidad de los delegates

Cuando queremos enviar valores a un método que son desconocidos para este, lo que hacemos es usar parámetros. Vamos, a nadie le sorprende el código:

int Sumar (int a, int b)
{
return a + b;
}

El método Sumar recibe dos parámetros y hace con ellos lo que sea (en este caso sumarlos). El uso de parámetros es vital porque sino deberíamos declarar un método por cada combinación posible de valores… En nuestro caso deberíamos hacer un método Sumar1y1 que sumase 1+1, otro Sumar1y2 que sumase 1+2…  ya se ve que eso por un lado es imposible y por otra un poco (bastante) estúpìdo. El uso de parámetros es algo que todos aprendemos más bien pronto al desarrollar y con los que todos nos sentimos cómodos.

Un parámetro tiene básicamente dos cosas:

  • Un nombre, que se usa dentro del método para referirse a él
  • Y un tipo que indica que clase de valores puede tener el parámetro.

El tipo es lo que me importa ahora: Si el parámetro es de tipo int puede contener cualquier entero, si es un double puede contener cualquier valor en coma flotante y si es FooClass puede contener cualquier objeto de la clase FooClass. Nada nuevo bajo el sol.

Bien, ahora imaginad que teneis una lista de cadenas y queréis ordenarla. Para ello os creais un método tal que:

void SortList(List<string> lst)
{
// ...
}

El método recibe un parámetro que es la lista a ordenar. Da igual que algoritmo de ordenación implementéis, en algún momento deberéis hacer algo como:

void SortList(List<string> lst)
{
// En str1 y str2 tenemos dos elementos que debemos comparar
if (FirstStringGoesBefore(str1, str2)) { ... }
}

El método FirstStringGoesBefore es un método helper vuestro que indica si una cadena va antes que otra según vuestro criterio de ordenación. P.ej. podría ser:

bool FirstString (string first, string second)
{
return first.Length <= second.Length;
}

Esto ordenaria las cadenas según su número de carácteres.

Vale… ¿observas un pequeño problema? ¿Cuál?

¡Exacto! Nuesta función SortList tiene el criterio de ordenación fijado, siempre ordena las cadenas según su número de caracteres! El algoritmo para ordenar cadenas (implementado en SortList) siempre es igual sea cual sea el criterio. Pero el criterio está en el método FirstStringGoesBefore() que decide, dadas dos cadenas, cual va antes que cual. No se a vosotros pero a mi, criterios de ordenación de cadenas me salen muchísimos: por número de carácteres, alfabéticamente, según representación numérica (es decir “01” antes que “112” porque 1 es menor que 112), etc, etc

La pregunta es debo hacer un método SortList para cada criterio? Es decir debo hacer un SortListByLength, otro SortListByAlphabetic,…

Dicho de otro modo: no le podríamos pasar al método SortList el criterio a utilizar como un parámetro? Ya, pero resulta que el criterio de ordenación resulta que es un método, no un valor (como int) o un objeto. Pues bien si comprendes la necesidad de pasar métodos como parámetros, comprendes la necesidad de los delegates. Y es que un delegate no es nada más que la posibilidad de pasar un método como parámetro.

2. Declración de un delegate

Cuando declaramos un parámetro, el tipo de ese parámetro determina que valores son válidos. Si el parámetro és int el valor 2 es correcto pero el valor 3.14159 no, ya que cae fuera de los valores válidos para int. Si el parámetro es FooClass entonces cualquier objeto FooClass es válido, pero un objeto de cualquier otra clase (p.ej. string) pues no.

Los delegates sirven para poder pasar métodos como parámetros y también hay varios tipos de delegates. Tiene lógica: no es lo mismo un método que espera dos cadenas y devuelve un bool que otra que espera dos ints y no devuelve nada. El primero sería un criterio de ordenación válido, la segunda no. Si yo creo el método SortList y admito un delegate como parámetro para el criterio de ordenación, es lógico que quiera asegurarme que sólo funciones que aceptan dos strings y devuelven bool són válidas. Total: yo voy a usar ese delegate pasándole dos cadenas y esperando un bool.

Por ello, cuando se define un delegate debe definirse que métodos acepta ese delegate: o sea los parámetros y el valor de retorno que los métodos deben tener.

Así no es de extrañar que la definición de un delegate se parezca sospechosamente a la definición de un método:

delegate bool CriterioOrdenacion(string str1, string str2);

Fijaos en el uso de la palabra clave delegate, que indica que lo que viene a continuación es la declaración de un delegate (y no de un método). El ejemplo declara un delegate llamado CriterioOrdenacion que permite pasar como parámetro métodos que acepten dos cadenas y que devuelva bool.

Nota: Que las dos cadenas se llamen str1 y str2 es totalmente superficial y no tiene ninguna importancia. Si yo creo un método que acepte dos cadenas y devuelva un bool podré pasarlo como parámetro usando este delegate aunque las cadenas no se llamen str1 y str2. Personalmente creo que el hecho de que en la declaración de un delegate los parámetros aparezcan nombrados sólo crea confusión.

3. Uso de un delegate

Una vez tengo definido el delegate ya puedo usarlo. Cuando defino un delegate creo un nuevo tipo de datos. Así pues la el método SortList quedaría como:

void SortList(List<string> lst, CriterioOrdenacion criterio)
{
// ...
if (criterio(first, second)) {...}
}

Fijaos en dos cosas:

  1. El parámetro criterio cuyo tipo es CriterioOrdenacion que es el delegate que habíamos definido antes.
  2. La “invocación” al delegate. Para invocar a un delegate se invoca como si fuese un método. Es decir criterio(first, second) llamará realmente al método que hayamos recibido como parámetro. Nosotros no sabemos cual es ese método, pero gracias al delegate
    1. Sabemos que recibe dos cadenas y devuelve un bool
    2. Podemos invocarlo

Bien, ahora nos falta ver como podemos llamar al método SortList pasándole un criterio de ordenación en concreto. Para ello hemos de ver como instanciar un delegate.

Imaginad que tengo un método llamado CriterioOrdenacionSegunLength que queremos usar como un criterio de ordenación:

bool CriterioOrdenacionSegunLength(string s1, string s2)
{
return s1.Length <= s2.Length;
}

Y ahora quiero invocar al método SortList usando el método CriterioOrdenacionSegunLength como criterio de ordenación. Para ello defino una variable de tipo CriterioOrdenacion (el delegate) y la instancío con el método:

CriterioOrdenacion miCriterio = 
new CriterioOrdenacion(CriterioOrdenacionSegunLength);

Para instanciar un delegate se usa new al igual que para instanciar una clase, y se pasa el nombre del método con el que se instancia este delegate.

Y ahora ya puedo llamar a SortList:

SortList(lst, miCriterio);

Y listos! El método SortList usará ahora el criterio de ordenación del delegate miCriterio que es el método CriterioOrdenacionSegunLength.

4. Delegates y eventos

Mucha gente confunde delegates y eventos, aunque no es exactamente lo mismo. Ya hemos visto un delegate, y lo que conocemos como “evento” se basa, ciertamente, en delegates. Lo que pasa es que podemos ver un evento como una lista de delegates (lo que en .NET llamamos un MulticastDelegate). Cuando en C# estamos haciendo:

btn.Click += new EventHandler(btn_Click);

Estáis creando un delegate de tipo EventHandler y lo añadís a “la lista” de delegates del evento. Cuando el botón lanza el evento, lo que hace es invocar uno a uno todos los delegates de la lista, y así es como se llama al método btn_Click. Si mirais como está definido EventHandler vereis que su definición es:

public delegate void EventHandler(object sender, EventArgs e);

De ahí los famosos dos parámetros que tenemos en (casi) todas nuestras funciones gestoras de eventos!

5. Delegates y threads

Hay gente que también confunde delegates con threads. Delegates ya hemos visto que son, y los threads no son nada más que ejecutar un código en segundo plano. La confusión viene porque para crear un thread debemos decirle cual es el código a ejecutar en segundo plano. Es decir que método ejecutar en segundo plano. Es decir, debemos pasarle un delegate.

6. Para ver más…

Me he dejado cosas en tintero, porque sino este post, honestamente, no se terminaría nunca… Si os interesa profundizar sobre el tema, sabed que me he dejado adrede (os dejo algunos links):

  1. Delegates y genéricos
  2. Delegates y métodos anónimos
  3. Lambda expressions
  4. Delegates y reflection
  5. Reglas de covarianza y contravarianza de los delegates

Si alguien está interesado en profundizar sobre alguno de esos temas, que me lo diga y veremos que podemos hacer al respecto! 😉

Espero que este post os haya ayudado a tener más claro el concepto de un delegate!

Un saludo! 😉

[C# Básico] ¿Que es la herencia?

Hola a todos! Despues de la buena acogida que tuvo la primera entrega de C# Básico (dedicada a las interfaces), me gustaría abordar hoy una de las cuestiones que se pusieron en los comentarios: ¿Qué es la herencia? De nuevo os recuerdo que esta serie es vuestra: no tengáis reparos en pedir posts de algún tema en concreto o aclaraciones e intentaré contestaros dentro de mis conocimientos 🙂

En la wikipedia se define herencia como: En orientación a objetos la herencia es el mecanismo fundamental para implementar la reutilización y extensibilidad del software. A través de ella los diseñadores pueden construir nuevas clases partiendo de una jerarquía de clases ya existente (comprobadas y verificadas) evitando con ello el rediseño, la remodificación y verificación de la parte ya implementada. La herencia facilita la creación de objetos a partir de otros ya existentes, obteniendo características (métodos y atributos) similares a los ya existentes.

MMMmm… no se que queréis que os diga: a mi no me queda muy claro que significa exactamente esta definición de herencia, así que vamos a buscar una que, aunque quizá no sea tan correcta sea, al menos, más entenedora. Pero antes aclaremos…

¿Que es una clase?

En muchos escritos sobre POO se define clase como una abstracción de un objeto, una plantilla para crear objetos, una definición en base a la que crear objetos, etc… Esto nos lleva a buscar la definición de objeto, para encontrarnos, muchas veces, que se nos define como la instancia de una clase. Pues vaya.

Una clase representa un concepto. Cuando desarrollamos un programa lo hacemos para solucionar una cierta necesidad de negocio, y por lo tanto debemos modelar los conceptos de negocio necesarios. Cada concepto que modelemos es una clase. Si p.ej. estamos creando un software para la creación de mesas, el concepto de mesa seguramente tendrá sentido y nos aparecerá la clase Mesa. Si estamos creando un software de dibujo, serán otros conceptos los que necesitaremos y así nos aparecerán la clase Círculo o Rectángulo. Las clases no sólo representan conceptos de negocio, también conceptos “más técnicos” como pueden ser un fichero de log, o un gestor de transacciones. Entonces, si las clases representan conceptos…

Relaciones entre clases son relaciones entre conceptos

Lógico: si las clases representan conceptos (ideas) las relaciones entre clases son relaciones entre conceptos. Y que relación puede haber entre dos conceptos? Pues que un concepto represente una idea más específica de otro concepto (o al revés que un concepto represente una idea más general que otro).

P. ej. todos estaremos de acuerdo que el concepto de Mamífero es más específico que el concepto de Animal o que el concepto de Coche es más específico que el concepto de Vehículo. Pues bien, esta relación, en a la programación orientada a objetos se traduce en la relación de generalización (en este caso Vehículo es la clase general, clase base o superclase mientras que Coche es la clase derivada, clase hija o subclase)… sí, en POO nos gusta poner nombres raros a las cosas 🙂

Una relación de generalización trae varias consecuencias y una de ellas es precisamente la herencia: la clase derivada hereda todos los miembros (campos, propiedades y métodos) de la clase base.

Eso tiene su lógica, imaginad una clase Vehículo tal que:

class Vehiculo
{
public int Velocidad { get { ... } }
public void Acelerar (int delta) { ...}
public void Frenar (int vf) { ... }
public void Girar (int angulos) { ... }
}

La clase Vehiculo representa un concepto de algo que tiene una determinada Velocidad y que puede Acelerar, Frenar y Girar. Ahora bien, si nos aparece el concepto de Coche y lo modelamos como una nueva clase, es de esperar que un Coche también tenga una Velocidad, pueda Acelerar, pueda Frenar y pueda Girar… Para que vamos a codificar de nuevo todos esos métodos si ya los hemos definido (codificado, recordad que una clase implementa un concepto) para Vehiculo?

Este es un punto importante: la herencia no es herencia sólo de interfaz sinó también de implementación. Es decir, si tengo mi clase:

class Coche : Vechiculo
{
public string Marca { get { ... } }
public string Modelo { get { ... } }
}

Un Coche es un Vehículo que además tiene una Marca y un Modelo. Así yo puedo hacer:

Coche c = new Coche();
c.Acelerar(100);
// A que velocidad va mi coche?
int velocidad = c.Velocidad;
// Es un SEAT?
if (c.Marca == "SEAT") { ... }

Fijaos que puedo llamar a la propiedad Marca (definida en la propia clase Coche) para evaluar de que marca es mi coche, pero también puedo llamar al método Acelerar y a la propiedad Velocidad

Así pues la clase Coche obtiene “una copia” de todos los métodos y propiedades de la clase de la cual deriva (su clase base) y además puede añadir métodos o propiedades nuevos. Eso es, ni más ni menos lo que entendemos por herencia.

Es importante que os quedéis con la idea de que la clase derivada obtiene una copia de los métodos y propiedades heredados porque así podréis entender el…

Problema de la herencia múltiple

C# (al igual que Java) es un lenguaje con herencia simple. Eso significa que una clase sólo puede derivar de una clase base a la vez. P.ej. esto en C# no es válido:

class VehiculoAereo { ... }
class VehiculoMaritimo { ... }
// Error: Herencia múltiple no soportada en C# (no lo está en .NET en general)
class Hidroavion : VehiculoAereo, VehiculoMaritimo { ... }

Existen otros lenguajes (como C++) que soportan herencia múltiple y donde lo anterior seria válido. La teoría de programación orientada a objetos no pone restricciones al respecto… En muchos libros se menciona esa limitación aduciendo simplemente “que la herencia múltiple es peligrosa”… pero, por que?

Es difícil contar exactamente el problema en el que puede (y subrayo el puede) incurrir la herencia múltiple sin entrar en aspectos demasiado técnicos sobre como los compiladores implementan la herencia realmente, pero el motivo por el cual la herencia múltiple es peligrosa se llama “Herencia en diamante”. Básicamente el problema de la herencia en diamante se produce cuando una clase D, hereda de dos clases B y C, las cuales ambas heredan de A. Imaginad ahora que:

  • La clase A define un método FooA()
  • La clase B (que hereda de A) obtiene una copia del método FooA()
  • La clase C (que hereda de A) obtiene una copia del método FooA()
  • La clase D que hereda de B y de C… obtiene dos copias del método FooA (el de B y el de C). Pero una clase no puede tener dos métodos FooA() con el mismo nombre y parámetros… así que eso da error.

Los lenguajes que soportan herencia múltiple (como C++) abordan ese problema mediante la llamada herencia virtual pero otros lenguajes optan por eliminar el problema de raiz impidiendo la herencia múltiple. Entre estos lenguajes están todos los de .NET (C#, VB.NET, …) o Java entre ellos.

No tener herencia múltiple puede suponer una limitación y en algún momento puede serlo, pero nunca es muy grave: muchos diseños que requieren herencia múltiple pueden repensarse para usar sólo herencia simple. La clave está en que la mayoría de veces realmente no queremos herencia múltiple sinó polimorfismo múltiple. El polimorfismo múltiple está soportado en C# (y en .NET en general) mediante el uso de interfaces: Una clase puede implementar uno o más interfaces.

Nota: En el contexto de este post polimorfismo significa poder usar un objeto de una clase CD que derive de CB, o que implemente una interfaz IX en cualquier sitio donde se espera un objeto de la clase base CB o de la interfaz IX.

Redefinición de métodos

Imaginad ahora la siguiente jerarquía de clases:

class Rectangulo
{
public Color Color { get; set; }
public void Dibujar() { ... }
}

class RectanguloPintado : Rectangulo
{
public Color ColorFondo { get; set;}
}

Tenemos una clase Rectangulo que define una propiedad llamada Color (cuyo tipo es un objeto de una clase Color) y un método Dibujar. Luego tenemos una clase derivada RectanguloPintado que añade otra propiedad ColorFondo y obtiene por herencia la propiedad Color y el método Dibujar().

Pero claro… un RectanguloPintado no se dibuja como un Rectangulo verdad? Cuando nos encontramos en este caso es cuando debemos usar la redefinición de métodos: Es decir modificar el método heredado en la clase derivada con el código necesario para que funcione correctamente (en nuestro caso dibujar el rectángulo pintado).

Pero ojo, y eso es importante, la clase base debe estar bien pensada y debe preveer que va a ser extendida y que dicho método puede ser redifinido: la clase base (Rectangulo) debe declarar el método Dibujar como virtual. Eso es algo que la gente que viene de Java le cuesta entender, puesto que en Java todos los métodos son virtuales por defecto. En C# (y en VB.NET) debemos explicitar que un método es virtual. Recordad el significado de virtual, es: Si alguien deriva de esta clase puede redefinir este método si lo necesita.

Una vez la clase base define el método como virtual, la clase derivada puede redefinir el método:

class Rectangulo
{
public Color Color { get; set;}
public virtual void Dibujar() { ... }
}

class RectanguloPintado : Rectangulo
{
public ColorFondo {get; set;}
public override void Dibujar()
{
base.Dibujar();
// Pintamos...
}
}

Fijaos en cuatro aspectos clave:

  1. El método Dibujar está declarado como virtual en la clase base
  2. El método Dibujar está declarado como override en la clase derivada
  3. El método de la clase derivada debe llamarse exactamente igual y tener los mismos parámetros, valor de retorno y visibilidad que el método de la clase base.
  4. La llamada a base.Dibujar() lo que hace es llamar al método Dibujar() de la clase base. En este caso lo usamos porque para dibujar un rectángulo pintado lo que hacemos es dibujar el rectángulo primero y luego pintarlo. Para dibujar el rectángulo, en lugar de duplicar el código del método Dibujar() de la clase Rectangulo es mucho mejor llamara  a dicho código. Y eso es lo que se puede hacer con base. Obviamente el uso de base no es obligatorio (en según que casos el método redefinido tiene una implementación totalmente distinta y no se puede aprovechar el método de la clase base).

En fin… espero que este post os ayude a comprender un poco más que es la herencia y como funciona… Como siempre comentarios, dudas son bienvenidos! 😉

Un saludo!

ASP.NET MVC Q&A: ¿Cómo se usan las cookies?

Buenoooo… sigo esta serie que se inició a raíz de las preguntas que recibí en el webcast que realicé sobre ASP.NET MVC para la gente del DotNetClub de la UOC. Si queréis ver que otros posts de esta serie hay (o habrá) echad un vistazo al post que sirve de índice.

En este caso la pregunta fue “Cómo y desde donde acceder a las cookies en una aplicación MVC”. Este post está muy relacionado con el mismo que hablaba sobre el acceso a la sesión. En particular las normas sobre desde donde acceder serían las mismas:

  1. No acceder a las cookies desde las vistas. Las vistas deben ser agnósticas sobre la procedencia de los datos que muestran.
  2. (Intentar) no acceder desde el modelo: Esto “ata” el modelo a “objetos http”. Intenta mantener el modelo libre de esas dependencias, y si por alguna razón no puedes encapsula todo este acceso en clases helper y preferiblemente inyecta esas clases usando un contenedor IoC.
  3. Acceder desde los controladores. Los controladores ya están atados a todos los objetos http, así que es uno de los mejores lugares para acceder a las cookies.

Acceso a las cookies de forma manual

Para establecer una cookie basta con usar la clase HttpCookie y la colección Cookies que está en la Response:

string valor = "valor de cookie";
HttpCookie cookie1 = new HttpCookie("foo_one", valor);
ControllerContext.HttpContext.Response.SetCookie(cookie1);

Aquí estamos estableciendo la cookie llamada foo_one con el valor contenido en la variable valor. La colección Cookies que está en la Response contiene las cookies que se envían del servidor al cliente.

Para leer una cookie que el cliente nos envía, debemos acceder a la colección cookies, pero ahora debemos usar la que está en la Request:

var c1 = ControllerContext.HttpContext.Request.Cookies["foo_one"];
if (c1 != null)
{
string valor = c1.Value;
}

En el código recuperamos la cookie foo_one que nos ha enviado el cliente.

Acceso a las cookies via Value Provider

Bien… que os parece este código?

public ActionResult GeCookieManual()
{
var c1 = ControllerContext.HttpContext.Request.Cookies["foo_one"];
var c2 = ControllerContext.HttpContext.Request.Cookies["foo_two"];
FooData fd = null;

if (c1 != null && c2 != null)
{
fd = new FooData()
{
OneValue = c1.Value,
AnotherValue = c2.Value
};
}

return View("Details", fd);
}

Fijaos en lo que hace… obtiene dos cookies y con ellas crea un objeto de la clase FooModel.

¿Y bien? ¿No os suena mucho al binding todo eso? Si los datos estuviesen en la querystring (GET) o bien en POST ASP.NET MVC seria capaz de crear automáticamente el objeto FooModel… y con las cookies no puede?

Pues no. Al menos no “de serie”… pero por suerte ASP.NET MVC es tan extensible que introducirle esta capacidad es muy, muy fácil…

La clave está en crearnos un IValueProvider propio, que trabaje con las cookies. Ya he comentado en anteriores posts que los value providers son los encargados de “parsear” la request del cliente, recoger los datos y dejarlos todos en un mismo sitio para que el model binder pueda realizar el binding. Por defecto hay value providers para querystring y para POST (además de alguno más rarito que no nos importa ahora), pero no lo hay para cookies. Veamos como podríamos crearlo.

No voy a entrar mucho en detalle, solo comentaré que es necesario crear la clase que implementa IValueProvider y luego crear la factoría (la clase que creará nuestro value provider). En mi post sobre Value Providers tenéis más detalles.

La clase que implementa IValueProvider es muy simple:

public class CookieValueProvider : IValueProvider
{
private readonly HttpCookieCollection cookies;

public CookieValueProvider(HttpCookieCollection cookies)
{
this.cookies = cookies;
}

public bool ContainsPrefix(string prefix)
{
return this.cookies[prefix] != null;
}

public ValueProviderResult GetValue(string key)
{
var cookie = this.cookies[key];
return cookie != null ?
new ValueProviderResult(cookie.Value, cookie.Value, CultureInfo.CurrentUICulture) : null;
}
}

Básicamente ContainsPrefix debe devolver si la clave existe dentro de este value provider y GetValue debe devolver el valor asociado a la clave. En nuestro caso simplemente debemos mirar dentro de la colección de cookies que recibimos del constructor.

Ahora toca la factoria, que lo único que debe hacer es crear un CookieValueProvider y pasarle la colección Cookies de la Request:

public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
return new CookieValueProvider(controllerContext.HttpContext.Request.Cookies);
}

Y finalmente, en el Application_Start (en Global.asax) registramos esa factoría de value providers:

ValueProviderFactories.Factories.Add(new CookieValueProviderFactory());

Ahora si tenemos la clase FooData deifnida como:

public class FooData
{
public string OneValue { get; set; }
public string AnotherValue { get; set; }
}

Y si hemos establecido cookies que se llaman igual que las propiedades (OneValue y AnotherValue), podemos obtener los valores dejando que actúe el binding de ASP.NET MVC:

public ActionResult GetCookieAuto(FooData data)
{
// data está rellenado con el valor de las cookies
}

Y listos! Nuestro value provider obtiene el valor de las cookies y el model binder se encarga del resto! 🙂

Os dejo el link de un post de donde vi hace algún tiempo el value provider para cookies en ASP.NET MVC.

Un saludo a todos!!!

[C# Básico] Interfaces

Hola a todos! El otro día recibí un correo que decía lo siguiente:

¿Podrías escribir algo sobre el uso de Interfaces? Yo por ahi he leido que es algo recomendado crear interfaces que es como un patrón.. Yo la verdad no las uso en mis proyectos pero me gustaría saber para qué sirven y porque se deberían usar y en qué casos.

Reconozco que es un correo para reflexionar: muchas veces tendemos a escribir sobre lo más: lo más avanzado, lo más novedoso, lo más cool… y quizá nos olvidamos de que hay gente que lo que busca son artículos para iniciarse. Es cierto que Tutoriales de C# los hay a patadas en internet, pero una cosa es un tutorial planteado como un curso y otra un pequeño post sobre algún tema básico concreto.

Por esto me he decidido a hacer este post. Como digo en el título es C# básico, pero si como quien me mandó el correo, tienes dudas sobre como funcionan las interfaces en C#… bienvenido a bordo! 😉

Seguiré la serie [C# Básico] si veo que hay demanda de ella, es decir que si alguien me propone que escriba sobre temas concretos (a nivel de introducción) no tengo ningún problema en hacerlo! 🙂

1.¿ Que es la interfaz de una clase?

En teoría de orientación a objetos, la interfaz de una clase es todo lo que podemos hacer con ella. A efectos prácticos: todos los métodos, propiedades y variables públicas (aunque no deberían haber nunca variables públicas, debemos usar propiedades en su lugar) de la clase conforman su interfaz.

Dada la siguiente clase:

class Contenedor
{
public int Quitar();
public void Meter(int v);
private bool EstaRepetido(int v);
}

Su interfaz está formada por los métodos Quitar y Meter. El método EstaRepetido no forma parte de la interfaz de dicha clase, ya que es privado.

En orientación a objetos decimos que la interfaz de una clase define el comportamiento de dicha clase, ya que define que podemos y que no podemos hacer con objetos de dicha clase: dado un objeto de la clase Contenedor yo puedo llamar al método Quitar y al métdo Meter pero no puedo llamar al método EstaRepetido.

Así pues: toda clase tiene una interfaz que define que podemos hacer con los objetos de dicha clase.

2. Interfaces idénticas no significa clases intercambiables

Fíjate en estas dos clases:

class Contenedor
{
public int Quitar() { ... }
public void Meter (int v) { ... }
}
class OtroContenedor
{
public int Quitar() { ... }
public void Meter (int v) { ... }
}

Que puedes deducir de ellas? Exacto! Su inerfaz es la misma: con ambas clases podemos hacer lo mismo: llamar al método Quitar y al método Meter.

Ahora imagina que en cualquier otro sitio tienes un método definido tal y como sigue:

public void foo (Contenedor c)
{
// Hacer cosas con c como p.ej:
int i = c.Quitar();
c.Meter(10);
}

El método recibe un Contenedor y opera con él. Ahora dado que las interfaces de Contenedor y OtroContenedor son iguales, uno podría esperar que lo siguiente funcionase:

OtroContenedor oc = new OtroContenedor();
foo(oc);

Pero esto no va a compilar. ¿Por que? Pues aunque nosotros somos capaces leyendo el código de comparar la interfaz de ambas clases, el compilador no puede hacer esto. Para el compilador Contenedor y OtroContenedor son dos clases totalmente distintas sin ninguna relación. Por lo tanto un método que espera un Contenedor no puede aceptar un objeto de la clase OtroContenedor.

Quiero recalcar que el hecho de que el compilador no compare las interfaces de las clases no se debe a una imposibilidad técnica ni nada parecido: se debe a que no tiene sentido hacerlo.

¿Por que? Pues simplemente porque las interfaces son idénticas por pura casualidad. Supón que fuese legal llamar a foo con un objeto OtroContenedor, ok?

Entonces podría pasar lo siguiente:

  1. Alguien añade un método público a la clase Contenedor.
  2. Se modifica el método foo para que llame a dicho método nuevo. Eso es legal porque foo espera un Contenedor como parámetro
  3. La llamada a foo(oc) donde oc es OtroContenedor… como debe comportarse ahora? OtroContenedor no tiene el método nuevo que se añadió a Contenedor!

Así pues: dos clases con la misma interfaz no tienen relación alguna entre ellas y por lo tanto no se pueden intercambiar.

3. Implementación de interfaces

El ejemplo anterior ejemplifica un caso muy común: tener dos clases que hacen lo mismo pero de diferente manera. P.ej. imagina que Contenedor está implementado usando un array en memoria y OtroContenedor está implementando usando, que sé yo, pongamos un fichero en disco. La funcionalidad (la interfaz) es la misma, lo que varía es la implementación. Es por ello que en programación orientada a objetos decimos que las interfaces son funcionalidades (o comportamientos) y las clases representen implementaciones.

Ahora bien, si dos clases representan dos implementaciones distintas de la misma funcionalidad, es muy enojante (y estúpido) que no las podamos intercambiar. Para que dicho intercambio sea posible C# (y en general cualquier lenguaje orientado a objetos) permite explicitar la interfaz, es decir separar la declaración de la interfaz de su implementación (de su clase). Para ello usamos la palabra clave interface:

interface IContenedor
{
int Quitar();
void Meter(int i);
}

Este código declara una interfaz IContenedor que declara los métodos Quitar y Meter. Fíjate que los métodos no se declaran como public (en una interfaz la visibilidad no tiene sentido, ya que todo es public) y que no se implementan los métodos.

Las interfaces son un concepto más teórico que real. No se pueden crear interfaces. El siguiente código NO compila:

IContenedor c = new IContenedor();
// Error: No se puede crear una interfaz!

Es lógico que NO podamos crear interfaces, ya que si se nos dejara, y luego hacemos c.Quitar()… que método se llamaría si el método Quitar() no está implementado?

Aquí es donde volvemos a las clases: podemos indicar explícitamente que una clase implementa una interfaz, es decir proporciona implementación (código) a todos y cada uno de los métodos (y propiedades) declarados en la interfaz:

class Contenedor : IContenedor
{
public int Quitar() { ... }
public void Meter(int i) { ... }
}

La clase Contenedor declara explícitamente que implementa la interfaz IContenedor. Así pues la clase debe proporcionar implementación para todos los métodos de la interfaz. El siguiente código p.ej. no compila:

class Contenedor : IContenedor
{
public void Meter(int i) { ... }
}
// Error: Y el método Quitar()???

Es por esto que en orientación a objetos decimos que las interfaces son contratos, porque si yo creo la clase la interfaz me obliga a implementar ciertos métodos y si yo uso la clase, la interfaz me dice que métodos puedo llamar.

Y ahora viene lo bueno: Si dos clases implementan la misma interfaz son intercambiables. Dicho de otro modo, en cualquier sitio donde se espere una instancia de la interfaz puede pasarse una instancia de cualquier clase que implemente dicha interfaz.

Podríamos declarar nuestro método foo anterior como sigue:

void foo(IContenedor c)
{
// Cosas con c...
c.Quitar();
c.Meter(10);
}

Fíjate que la clave es que el parámetro de foo está declarado como IContenedor, no como Contenedor o OtroContenedor, con esto indicamos que el método foo() trabaja con cualquier objeto de cualquier clase que implemente IContenedor.

Y ahora, si supones que tanto Contenedor como OtroContenedor implementan la interfaz IContenedor el siguiente código es válido:

Contenedor c = new Contenedor();
foo(c); // Ok. foo espera IContenedor y Contenedor implementa IContenedor
OtroContenedor oc = new OtroContenedor();
foo(oc); // Ok. foo espera IContenedor y OtroContenedor implementa IContenedor
// Incluso esto es válido:
IContenedor ic = new Contenedor();
IContenedor ic2 = new OtroContenedor();

4. ¿Cuando usar interfaces?

En general siempre que tengas, o preveas que puedes tener más de una clase para hacer lo mismo: usa interfaces. Es mejor pecar de exceso que de defecto en este caso. No te preocupes por penalizaciones de rendimiento en tu aplicación porque no las hay.´

No digo que toda clase deba implementar una interfaz obligatoriamente, muchas clases internas no lo implementarán, pero en el caso de las clases públicas (visibles desde el exterior) deberías pensarlo bien. Además pensar en la interfaz antes que en la clase en sí, es pensar en lo que debe hacerse en lugar de pensar en como debe hacerse. Usar interfaces permite a posteriori cambiar una clase por otra que implemente la misma interfaz y poder integrar la nueva clase de forma mucho más fácil (sólo debemos modificar donde instanciamos los objetos pero el resto de código queda igual).

5. Segregación de interfaces

Imagina que tenemos un sistema que debe trabajar con varios vehículos, entre ellos aviones y coches, así que declaramos la siguiente interfaz:

interface IVehiculo
{
void Acelerar(int kmh);
void Frenar();
void Girar(int angulos);
void Despegar();
void Aterrizar();
}

Luego implementamos la clase avión:

class Avion : IVehiculo
{
public void Acelerar(int kmh) { ... }
public void Frenar() { ... }
public void Girar (int angulos) { ... }
public void Despegar() { ... }
public void Aterrizar() { ... }
}

Y luego vamos a por la clase coche… y aquí surge el problema:

class Coche : IVehiculo
{
public void Acelerar(int kmh) { ... }
public void Frenar() { ... }
public void Girar (int angulos) { ... }
public void Despegar() {throw new NotImplementedException("Coches no vuelan"); }
public void Aterrizar(){throw new NotImplementedException("Coches no vuelan"); }
}

La interfaz IVehiculo tiene demasiados métodos y no define el comportamiento de todos los vehículos, dado que no todos los vehículos despegan y aterrizan. En este caso es mejor dividir la interfaz en dos:

interface IVehiculo
{
void Acelerar(int kmh);
void Frenar();
void Girar (int angulos);
}

interface IVehiculoVolador : IVehiculo
{
void Despegar();
void Aterrizar();
}

Fíjate además que IVehiculoVolador deriva de IVehiculo (en orientación a objetos decimos que hay una relación de herencia entre IVehiculoVolador y IVehiculo), eso significa que una clase que implemente IVehiculoVolador debe implementar también IVehiculo forzosamente. Por lo tanto podemos afirmar que todos los vehículos voladores son también vehículos.

Ahora si que la clase Coche puede implementar IVehiculo y la clase Avion puede implementar IVehiculoVolador (y por lo tanto también IVehiculo). Si un método foo() recibe un objeto IVehiculoVolador puede usar métodos tanto de IVehiculoVolador como de IVehiculo:

void foo (IVehiculoVolador vv)
{
vv.Acelerar(10); // Ok. Acelerar es de IVehiculo y IVehiculoVolador deriva de IVehiculo
vv.Despegar(); // Ok. Despegar es de IVehiculoVolador
}

Al reves no! Si un método foo recibe un IVehiculo no puede llamar a métodos de IVehiculoVolador. Lógico: todos los vehículos voladores son vehículos pero al revés no… no todos los vehículos son vehículos voladores!

Siempre que haya segregación no tiene por que haber herencia de interfaces. Imagina el caso de que además de vehículos debemos tratar con Armas de guerra. Tenemos otra interfaz:

interface IArmaDeGuerra
{
void Apuntar();
void Disparar();
}

Ahora podrían existir clases que implementen IArmaDeGuerra como p.ej. una torreta defensiva:

class TorretaDefensiva : IArmaDeGuerra
{
public void Apuntar() { ... }
public void Disparar() { ... }
}

Pero claro… también tenemos vehículos que pueden ser a la vez armas de guerra, p.ej. un tanque! Que hacemos? Ningún problema: una clase puede implementar más de una interfaz a la vez! Para ello debe implementar todos los métodos de todas la interfaces:

class Tanque : IVehiculo, IArmaDeGuerra
{
public void Acelerar(int kmh) { ... }
public void Frenar() { ... }
public void Girar (int angulos) { ... }
public void Apuntar() { ... }
public void Disparar() { ... }
}

Ahora si un método foo() recibe un IVehiculo le puedo pasar un Tanque y si otro método foo2 recibe un IArmaDeGuerra también le puedo pasar un Tanque. O dicho de otro modo, los tanques se comportan como vehículos y como armas de guerra a la vez!

Así pues, es importante segregar bien nuestras interfaces porque en caso contrario vamos a tener dificultades a la hora de implementarlos. La segregación de interfaces es uno de los 5 principios SOLID (concretamente la letra I: interface segregation principle).

Bueno… espero que este post os haya ayudado a comprender mejor que son las interfaces y como usarlas!

Un saludo a todos!

ASP.NET MVC Q&A: ¿Como reciben parámetros los controladores?

Este es el segundo post de la serie que nace a raíz de las preguntas que se me realizaron en el Webcast que di sobre ASP.NET MVC.

Estaba explicando la tabla de rutas por “defecto” de ASP.NET MVC, indicando que el primer valor era el controlador, el segundo la acción y el tercero un parámetro llamado id:

routes.MapRoute(
"Default", // Nombre de la ruta
"{controller}/{action}/{id}", // Formato de url /Controlador/Accion/Id
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Valores x defecto
);

Comenté que dada esa tabla de rutas una url /Home/Index/100 se mapeaba a l’acción Index del controlador Home con un parámetro id a valor 100 (parámetro que era opcional).

En el Webcast no comenté nada más… voy a ampliar el tema ahora, porque es realmente interesante.

1. Acciones y tabla de rutas

Vamos a aclarar algunos puntos que son fundamentales relativos a las acciones y la tabla de rutas:

  1. Las acciones se distinguen por su nombre, sin tener en cuenta sus parámetros. Eso significa que un controlador no puede definir dos veces la misma acción con parámetros distintos, salvo que se responda a verbos http distintos (p.ej. una acción responda a GET y otra a POST).
  2. La tabla de rutas mapea una URL a una única acción de un controlador.
  3. La tabla de rutas tiene una o más rutas, que se evalúan en orden. Cuando una URL hace matching con una ruta, se termina la evaluación.

Así el siguiente código es erróneo:

public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Index(int id)
{
return View();
}
}

Uno podría pensar que la tabla de rutas enrutaría /Home/Index al método Index() sin parámetros y /Home/Index/100 al método Index() que acepta un entero, pero no. La tabla de rutas enruta tanto /Home/Index/ como /Home/Index/100 a la acción Index. Luego el framework cuando va a invocarla se encuentra con dos métodos que la implementan y da un error: The current request for action ‘Index’ on controller type ‘HomeController’ is ambiguous between the following action methods: System.Web.Mvc.ActionResult Index() on type MvcControllerParams.Controllers.HomeController System.Web.Mvc.ActionResult Index(Int32) on type MvcControllerParams.Controllers.HomeController

2. Parámetros opcionales en las rutas

¿Y que ocurriría con el siguiente código?

public class HomeController : Controller
{
public ActionResult Index(int id)
{
return View();
}
}

La tabla de rutas enruta /Home/Index/100 a dicha acción y el parámetro id vale 100… pero que ocurre si entramos /Home/Index?

Pues que da error: El parámetro id está declarado como opcional en la ruta, eso significa que puede no aparecer en la url. Así pues la url /Home/Index es válida y se enruta a la acción Index. Y cuando el framework se encuentra que quiere pasarle un valor al parámetro id, no puede porque no hay parámetro id en la url y int no es nullable. El mensaje de error que da es:

The parameters dictionary contains a null entry for parameter ‘id’ of non-nullable type ‘System.Int32’ for method ‘System.Web.Mvc.ActionResult Index(Int32)’ in ‘MvcControllerParams.Controllers.HomeController’. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.

La solución? Pues declarar que el parámetro id sea nullable:

public class HomeController : Controller
{
public ActionResult Index(int? id)
{
return View();
}
}

Fíjate en el uso de int? (que es la sintaxis que da C# para usar Nullable<int>).

Otra posible opción seria modificar la tabla de rutas para que en lugar de declarar el parámetro simplemente como opcional, asignarle un valor por defecto:

routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = 0 } // Parameter defaults
);

Fíjate que donde antes ponía id = UrlParameter.Optional ahora pone id = 0. La diferencia es sutil pero importante: antes si id no aparecía el framework asumía que no tenía valor, ahora si el parámetro no aparece el framework le asigna un valor por defecto de 0. Así, ahora en el controlador podemos volver a usar int en lugar de Nullable<int> (int? en c#).

El “pero” de esta opción es que no puedes distinguir entre /Home/Index y /Home/Index/0. Ambas URLs invocan la acción Index con el valor id=0.

Bien… ¿y que ocurrirá si entro la url /Home/Index/eiximenis? Pues que la tabla de rutas mapeará esta url a la acción Index y cuando el framework vaya a invocarla intentará convertir “eiximenis” a int y dicha conversión devolverá null al no ser posible. Así:

  • Si teníamos Index(int id) el framework dará error, indicando que no puede asignar “null” a int.
  • Si teníamos Index (int? id) recibiremos el valor null en el parámetro id. Efectivamente en este caso es lo mismo /Home/Index que /Home/Index/eiximenis

Si en lugar de declarar la acción con un parámetro int la declaro que recibe una cadena:

public ActionResult Index(string id)
{
return View();
}

Todo es mucho más sencillo:

  • /Home/Index –> Invoca Index() con id = null
  • /Home/Index/100 –> Invoca Index() con id = “100”
  • /Home/Index/eiximenis –> Invoca Index() con id=”eiximenis”

3. Acciones sin parámetros opcionales

Vamos a cambiar de nuevo la tabla de rutas para que quede:

routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index" } // Parameter defaults
);

Fíjate que hemos eliminado la parte donde ponía id = UrlParameter.Optional. Así ahora el parámetro id es obligatorio en la ruta.

La url /Home/Index/eiximenis nos invoca la acción Index() con id=”eiximenis” pero que hace la url /Home/Index? Pues devuelve un 404. ¿Por que? Bien, cuando una URL no satisface ninguna de las entradas de la tabla de rutas, el framework devuelve un 404 para indicar que la URL no se corresponde a ningún recurso. En nuestro caso la tabla de rutas tiene una sola entrada que obliga que las urls tengan la forma /controlador/accion/id. Y dado que id no se ha declarado opcional ni se le ha dado ningún valor por defecto, debe aparecer explícitamente en la URL. Como en nuestro caso no aparece, la URL /Home/Index no cumple esta entrada en la tabla de rutas y por eso recibimos el 404.

4. Rutas con más de un parámetro

La pregunta explícita que me hicieron en el Webcast fue “Y se puede pasar más de un parámetro”? Mi respuesta en aquel momento fue simplemente que sí, añadiendolos a la tabla de rutas:

routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}/{desc}", // URL with parameters
new { controller = "Home", action = "Index",
id = UrlParameter.Optional, desc = UrlParameter.Optional } // Parameter defaults
);

Ahora la ruta tiene dos parámetros (ambos opcionales) id y desc.

En el controlador definimos la acción con los dos parámetros:

public ActionResult Index(string id, string desc)
{
return View();
}

Tal y como lo tenemos:

  • /Home/Index –> Invoca a Index con id=null y desc=null
  • /Home/Index/eiximenis –> Invoca a Index con id=”eiximenis” y desc=null
  • /Home/Index/eiximenis/edu –> Invoca a Index con id=”eiximenis” y desc=”edu”

De esta manera podemos pasar más de un parámetro a los controladores.

5. Otros parámetros (querystrings,…)

Bueno… vamos a liarla un poco más! Tal y como lo tenemos que ocurre con la url /Home/Index/eiximenis?desc=edu ?

Pues veamos: La tabla de rutas no usa la querystring, así que para la tabla de rutas es como si hubiesemos entrado /Home/Index/eiximenis, lo que se mapea a la acción Index. Luego cuando el framework va a invocar dicha acción:

  • Pone el valor de id a “eiximenis” (primer valor de la url)
  • El valor de la ruta para desc és null (recordad: la tabla de rutas no entiende de querystring). Entonces se analiza la querystring y el framework ve que el controlador acepta otro parámetro llamado desc y que en la querystring aparece dicho parámetro, así que le asigna valor.

Así por lo tanto, la url /Home/Index/eiximenis?desc=edu llama a l’acción Index con el valor de id a “eiximenis” y el valor de desc a “edu”.

Fijaos pues que las urls /Home/Index/eiximenis/edu y /Home/Index/eiximenis?desc=edu son equivalentes, puesto que ambas terminan llamando a la misma acción con los dos parámetros… pero que sean equivalentes no quiere decir que sean indistinguibles. Desde el controlador podemos saber si los parámetros nos vienen via ruta o bien via querystring:

public ActionResult Index(string id, string desc)
{
var routedesc = this.RouteData.Values["desc"];
return View();
}

El valor de routdesc será “edu” si venimos por la url /Home/Index/eiximenis/edu y null si venimos por la url /Home/Index/eiximenis?desc=edu

Nota: Todo lo que hemos dicho sobre la querystring aplica también a parámetros POST.

6. Bindings

Y vamos ya con la última… Imagina que tenemos una clase así:

public class FooModel
{
public string id { get; set; }
public string desc { get; set; }
}

Y declaramos nuestra acción para que reciba, no dos strings sinó un objeto FooModel:

public ActionResult Index(FooModel m)
{
return View();
}

Ahora en este caso la url:

  • /Home/Index –> Invoca a Index() con un objeto FooModel con id=null y desc=null
  • /Home/Index/eiximenis –> Invoca a Index() con un objeto FooModel con id=”eiximenis” y desc=null
  • /Home/Index/eiximenis/edu –> Invoca a Index() con un objeto FooModel con id=”eiximenis” y desc=”edu”
  • /Home/Index/eiximenis?desc=edu –> Invoca a Index() con un objeto FooModel con id=”eiximenis” y desc=”edu”

Que te parece? El framework es capaz de convertir los parámetros (vengan de la ruta o del querystring) en el objeto que espera la acción, instanciando sus propiedades!

Y bueno… vamos a dejarlo aquí por el momento 🙂 Espero que ahora tengáis un poco más claro como funciona el “paso de parámetros” a los controladores.

Un saludo a todos! 😉

Una postdata para quien quiera profundizar

Si quieres profundizar en este tema, antes que nada ten presente los siguientes puntos:

  • A través de la tabla de rutas el framework decide que acción implementa la URL
  • Antes de invocar una acción, los Value Providers entran en acción, procesando los valores de la request. Los valores de la request pueden estar en:
    • RouteData (puestos por la tabla de rutas)
    • QueryString
    • POST
    • Cualquier otro sitio (p.ej. cookies). Se pueden crear value providers propios.
  • Antes de invocar la acción, el ModelBinder entra en acción: coge los valores que han procesado los Value Providers y los intenta mapear a los parámetros de la acción.
  • Se invoca la acción del controlador.

Ahora algunos links: