Extendiendo ASP.NET MVC 3

Buenas!

En esta serie de post vamos a estar revisando con ejemplos los principales puntos de extensibilidad de ASP.NET MVC 3.

La lista a revisar es la siguiente:

•Model:

–Value Providers

–Model Binders

•View:

–View Engines

–Html Helpers

–Razor Helpers

•Controller:

–Action Name Selectors

–Action Methods Selectors

–Action Filters

–Exceptions Filters

–Custom Results

Así que empecemos!!

Value Providers:

Model Binding es el proceso de transformar los datos que se obtienen al momento del Request (como por ejemplo valores de Forms, Query Strings, etc.) en Modelos.

Para esto durante el Model Binding es necesario contar con un mecanismo que nos permita entender de donde provienen los datos para poder acceder a estos y utilizarlos para crear o actualizar objetos del Modelo. Este mecanismo son los Value Providers.

El propósito de un Value Provider es simple, proveer acceso a la información que se puede utilizar para el proceso de Model Binding. MVC posee varios Value Providers que el Default Model Binder utiliza, entre estos FormValueProvider, QueryStringValueProvider y RouteDataValueProvider.

En nuestro ejemplo crearemos un Value Provider que accede a las Cookies para obtener valores.

Para esto, vamos a agregar una nueva clase llamada CookieValueProvider que implementa la interfaz IValueProvider.
Esta interfaz posee dos métodos:
- ContainsPrefix: Se utiliza para determinar si el Value Provider puede obtener los valores solicitados bajo ese prefijo / clave. Si puede, MVC lo va a utilizar para obtener el valor utilizando el método GetValue. Devuelve un boolean.
- GetValue: Obtiene le valor a través de la clave solicitada. Devuelve una instancia de ValueProviderResult la cual es básicamente un Wrapper del valor, que se encarga de resolver algunas cuestiones como conversiones o manejo de cultura.

La siguiente seria nuestra implementación de CookieValueProvider. Al construirse utiliza la colección de HttpCookies para crear un diccionario donde se almacenan los valores de las cookies indexadas por sus claves. Con esta información, el método ContainsPrefix verifica si en la colección existe la clave. Si existe devuelve True y entonces MVC va a utilizar este Value Provider para obtener el valor a través el método GetValue, el cual va a acceder al diccionario de valores de cookies para obtener el valor y devolverlo como una instancia de ValueProviderResult

public class CookieValueProvider : IValueProvider
    <br></font></font><font style="font-size: 10pt;"><font face="Courier New"><font color="#000000">{
      <br>&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">private</font></span><font color="#000000"> </font><span><font style="background-color: rgb(243, 243, 243);" color="#2b91af">Dictionary</font></span><font color="#000000">&lt;</font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">string</font></span><font color="#000000">, </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">string</font></span></font><font face="Courier New"><font color="#000000">&gt; _cookiesKeys = 
      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">new</font></span><font color="#000000"> </font><span><font style="background-color: rgb(243, 243, 243);" color="#2b91af">Dictionary</font></span><font color="#000000">&lt;</font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">string</font></span><font color="#000000">, </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">string</font></span></font><font face="Courier New"><font color="#000000">&gt;();
      <br>

      <br>&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">public</font></span><font color="#000000"> CookieValueProvider(</font><span><font style="background-color: rgb(243, 243, 243);" color="#2b91af">HttpCookieCollection</font></span></font><font face="Courier New"><font color="#000000"> cookieCollection)
      <br>&nbsp;&nbsp;&nbsp; {

      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">foreach</font></span><font color="#000000"> (</font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">string</font></span><font color="#000000"> cookieName </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">in</font></span></font><font face="Courier New"><font color="#000000"> cookieCollection)
      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {

      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">var</font></span></font><font face="Courier New"><font color="#000000"> cookie = cookieCollection.Get(cookieName);
      <br>

      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">foreach</font></span><font color="#000000"> (</font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">var</font></span><font color="#000000"> key </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">in</font></span></font><font face="Courier New"><font color="#000000"> cookie.Values.AllKeys)
      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {

      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">if</font></span><font color="#000000"> (key != </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">null</font></span></font><font face="Courier New"><font color="#000000"> &amp;&amp; !_cookiesKeys.ContainsKey(key))
      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _cookiesKeys.Add(key, cookie.Values.Get(key));

      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }

      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }

      <br>&nbsp;&nbsp;&nbsp; }

      <br>

      <br>&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">public</font></span><font color="#000000"> </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">bool</font></span><font color="#000000"> ContainsPrefix(</font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">string</font></span></font><font face="Courier New"><font color="#000000"> prefix)
      <br>&nbsp;&nbsp;&nbsp; {

      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">var</font></span></font><font face="Courier New"><font color="#000000"> containsPrefix = _cookiesKeys.Keys.Any(x =&gt; x == prefix);
      <br>

      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">return</font></span></font><font face="Courier New"><font color="#000000"> containsPrefix;
      <br>

      <br>&nbsp;&nbsp;&nbsp; }

      <br>

      <br>&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">public</font></span><font color="#000000"> </font><span><font style="background-color: rgb(243, 243, 243);" color="#2b91af">ValueProviderResult</font></span><font color="#000000"> GetValue(</font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">string</font></span></font><font face="Courier New"><font color="#000000"> key)
      <br>&nbsp;&nbsp;&nbsp; {

      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">var</font></span></font><font face="Courier New"><font color="#000000"> value = _cookiesKeys[key];
      <br>

      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">return</font></span><font color="#000000"> value != </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">null</font></span></font><font face="Courier New"><font color="#000000"> ?
      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">new</font></span><font color="#000000"> </font><span><font style="background-color: rgb(243, 243, 243);" color="#2b91af">ValueProviderResult</font></span><font color="#000000">(value, value, </font><span><font style="background-color: rgb(243, 243, 243);" color="#2b91af">CultureInfo</font></span></font></font><font face="Courier New"><font style="font-size: 10pt;"><font color="#000000">.CurrentUICulture) 
      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">null</font></span><font color="#000000">;
      <br>&nbsp;&nbsp;&nbsp; }

      <br>}</font></font></font></div>

Con esto, tenemos completo nuestro Custom Value Provider para acceder a las Cookies y obtener valores desde allí. Ahora el siguiente paso es “decirle” a MVC que existe un nuevo Value Provider y que tiene que ser considerado para ser utilizado al momento de obtener valores durante el proceso de Model Binding.

<br>Para esto necesitamos implementar una clase que derive de la clase abstracta <strong>ValueProviderFactory </strong>e implemente el método <strong>GetValueProvider</strong>. Esta fábrica se va encargar de devolver nuestro nuevo Value Provider construido basándose en la información de contexto. 

<br>La siguiente seria nuestra implementación de la CookieValueProviderFactory, la cual en el método <strong>GetValueProvider</strong> se encarga de devolver una nueva instancia de CookieValueProvider, pasándole por parámetro de constructor la colección de Cookies que se están utilizando en el Request.</font></p>
public class CookieValueProviderFactory : ValueProviderFactory
    <br></font></font><font style="font-size: 10pt;"><font face="Courier New"><font color="#000000">{
      <br>&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">public</font></span><font color="#000000"> </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">override</font></span><font color="#000000"> </font><span><font style="background-color: rgb(243, 243, 243);" color="#2b91af">IValueProvider</font></span><font color="#000000"> GetValueProvider(</font><span><font style="background-color: rgb(243, 243, 243);" color="#2b91af">ControllerContext</font></span></font></font><font face="Courier New"><font style="font-size: 10pt;"><font color="#000000"> controllerContext)
      <br>&nbsp;&nbsp;&nbsp; {

      <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">return</font></span><font color="#000000"> </font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">new</font></span><font color="#000000"> </font><span><font style="background-color: rgb(243, 243, 243);" color="#2b91af">CookieValueProvider</font></span><font color="#000000">(controllerContext.HttpContext.Request.Cookies);
      <br>&nbsp;&nbsp;&nbsp; }

      <br>}</font></font></font></div>

Una vez creada nuestra fabrica, el ultimo punto es registrarla dentro de la coleción de fábricas de Value Providers que posee MVC. Esto lo podemos hacer dentro del archivo Global.asax, en el método Application_Start, como se ve a continuación:

protected void Application_Start()
{
      <br>&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#008000">//....&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span>

    <br><font color="#000000">&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#2b91af">ValueProviderFactories</font></span></font><font face="Courier New"><font color="#000000">.Factories.Add(</font><span><font style="background-color: rgb(243, 243, 243);" color="#0000ff">new</font></span><font color="#000000"> </font><span><font style="background-color: rgb(243, 243, 243);" color="#2b91af">CookieValueProviderFactory</font></span></font></font><font face="Courier New"><font style="font-size: 10pt;"><font color="#000000">());&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; </font><span><font style="background-color: rgb(243, 243, 243);" color="#008000">//....</font></span>

    <br><font color="#000000">}</font></font></font></div>

Con este último punto, nuestra aplicación esta lista para utilizar nuestro nuevo ValueProvider!

En la próxima semana continuaremos con el siguiente punto de extensibilidad: Model Binders.

<br>Nos leemos!!<span style="font-family: ;"><font face="Consolas"><font color="#000000"></font></font></span></font></p>

No Comments