Introducción

Una de las herramientas más valiosas que nos ofrece ASP.NET MVC es el enlace de datos o Model Binding que nos permite traducir los datos recibidos de los formularios HTML a datos tipados que recibe nuestro controlador.

El responsable de realizar esta tarea es el DefaultModelBinder, un ModelBinder por defecto que traduce automáticamente los datos recibidos desde el navegador a los tipos de datos de los argumentos del controlador. Este ModelBinder por defecto funciona con tipos básicos y con algunos objetos complejos (clases) que cumplen una ciertas características.

Para otros tipos que el ModelBinder por defecto no es capaz de "traducir", tenemos la posibilidad de crear ModelBinders personalizados. Una vez creado nuestro ModelBinder no tenemos más que registrarlo en el vento Application_Start del Global.asax añadiéndolo a la conlección Binders de la clase estática ModelBinders indicándole el tipo de datos para el que debe utilizarse.

Sin embargo el tipo de datos que le indicamos a ASP.NET MVC debe ser un tipo específico, no podemos indicarle un tipo genérico. Entonces ¿cómo podemos crear y registrar un ModelBinder para un tipo genérico como podría ser KeyValuePair<TKey, TValue>?

Una opción sería registrar cada implementación específica que utilizamos en nuestra aplicación en la colección ModelBinders:

C#Visual Basic
Editar script|Remove
ModelBinders.Binders.Add(typeof(KeyValuePair<stringstring>), new KeyValuePairModelBinder()); 
ModelBinders.Binders.Add(typeof(KeyValuePair<stringdouble>), new KeyValuePairModelBinder());

Esto puede resultar bastante poco práctico, obligándonos a registrar de nuevo nuestro ModelBinder cada vez que queramos utilizar en la aplicación una implementación diferente del tipo genérico.

Por suerte ASP.NET MVC nos proporciona otro método mucho más práctico para registrar los ModelBinders: los proveedores de ModelBinders o ModelBinderProviders.

Creando el ModelBinder

Para crear y registrar el ModelBinderProvider para nestra clase genérica KeyValuePair<TKey, TValue>, lo primero que necesitaremos es crear el ModelBinder.

C#Visual Basic
Editar script|Remove
    public class KeyValuePairModelBinder: IModelBinder 
    { 
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
        { 
            var values = bindingContext.ValueProvider as ValueProviderCollection; 
            if (values == null) 
            { 
                return null; 
            } 
 
            var key = values.GetValue(bindingContext.ModelName + ".Key"); 
            var keyValue = Convert.ChangeType(key.AttemptedValue, bindingContext.ModelType.GetGenericArguments()[0]); 
            var value = values.GetValue(bindingContext.ModelName + ".Value"); 
            var valueValue = Convert.ChangeType(value.AttemptedValue, bindingContext.ModelType.GetGenericArguments()[1]); 
            return Activator.CreateInstance(bindingContext.ModelType, keyValue, valueValue); 
        }

 La implementación del ModelBinder es muy sencilla: recupera los valores de las propiedades Key y Value del modelo a través del ModelBindingContext y devuelve una nueva instancia  de KeyValuePair utilizando los valores de Key y Value en el constructor.

Creando el ModelBinderProvider

El ModelBinderProvider debe implementar la interfaz IModelBinderProvider que consta de un único método GetBinder. Este método recibe como argumento el tipo de datos del modelo que se desea enlazar y devuelve un ModelBinder para ese tipo (si el proveedor es capaz de crearlo) o null (si el proveedor no es capaz de crear un ModelBinder para ese tipo).

C#Visual Basic
Editar script|Remove
    public class KeyValuePairModelBinderProvider : IModelBinderProvider 
    { 
        public IModelBinder GetBinder(Type modelType) 
        { 
            if (modelType.IsGenericType && 
                modelType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) 
            { 
                return new KeyValuePairModelBinder(); 
            } 
 
            return null; 
        } 
    }
 Como se puede ver la implementación es muy sencilla: el método GetBinder comprueba que el tipo del modelo recibido como argumento se corresponde con el tipo genérico KeyValuePair<,> y, si es así, devuelve una instancia del ModelBinder KeyValuePairModelBinder. Si el tipo recibido es otro devuelve null.

Registrando el ModelBinderProvider

El registro del proveedor de ModelBinders lo realizaremos también en el evento Application_OnStart del Global.asax, añadiéndolo a la colección BinderProviders de la clase estática ModelBinderProviders.

C#Visual Basic
Editar script|Remove
ModelBinderProviders.BinderProviders.Add(new KeyValuePairModelBinderProvider());
 
Y una vez hecho esto no queda más que probar que funciona.