2009-12-16 15 views
9

Tengo un montón de formularios donde se ingresan los valores de moneda y quiero que puedan ingresar "$ 1,234.56". Por defecto, los archivadores modelo no analizarán eso en un decimal.Cómo filtrar datos con el clasificador de modelo personalizado

Lo que estoy pensando hacer es crear un encuadernador de modelo personalizado hereda DefaultModelBinder, anula el método BindProperty, comprueba si el tipo de descriptor de propiedad es decimal y si lo está, simplemente quita $ y, de los valores.

¿Es este el mejor enfoque?

Código:

public class CustomModelBinder : DefaultModelBinder 
{ 
protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor) 
{ 
    if(propertyDescriptor.PropertyType == typeof(decimal) || propertyDescriptor.PropertyType == typeof(decimal?)) 
    { 
    var newValue = Regex.Replace(bindingContext.ValueProvider[propertyDescriptor.Name].AttemptedValue, @"[$,]", "", RegexOptions.Compiled); 
    bindingContext.ValueProvider[propertyDescriptor.Name] = new ValueProviderResult(newValue, newValue, bindingContext.ValueProvider[propertyDescriptor.Name].Culture); 
    } 

    base.BindProperty(controllerContext, bindingContext, propertyDescriptor); 
} 
} 

actualización

Esto es lo que terminé haciendo:

public class CustomModelBinder : DataAnnotationsModelBinder 
{ 
    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor) 
    { 
     if(propertyDescriptor.PropertyType == typeof(decimal) || propertyDescriptor.PropertyType == typeof(decimal?)) 
     { 
      decimal newValue; 
      decimal.TryParse(bindingContext.ValueProvider[propertyDescriptor.Name].AttemptedValue, NumberStyles.Currency, null, out newValue); 
      bindingContext.ValueProvider[propertyDescriptor.Name] = new ValueProviderResult(newValue, newValue.ToString(), bindingContext.ValueProvider[propertyDescriptor.Name].Culture); 
     } 
     base.BindProperty(controllerContext, bindingContext, propertyDescriptor); 
    } 
} 
+1

echar un vistazo a este post de Haacked: http://haacked.com/archive/2011/03/ 19/fixing-binding-to-decimales.aspx – VinnyG

Respuesta

5

Es razonable hacerlo en el ligante. Sin embargo, creo que Decimal.Parse con el proveedor de formato de moneda o estilo de número (see the docs) sería más confiable que quitar el "$" y llamar al base. Para empezar, manejaría monedas extranjeras, lo que podría ser un problema para usted algún día.

+1

Wow. No sabía Decimal.Parse tenía un formato que lo aceptará de manera predeterminada. Pensarías que habría una forma para que los encuadernadores modelo lo acepten de manera predeterminada. De hecho, en este ejemplo http://www.asp.net/%28S%28ywiyuluxr3qb2dfva1z5lgeg%29%29/learn/mvc/tutorial-39-cs.aspx incluso tienen un tipo decimal con un validador de expresiones regulares que dice $ está bien ... Si tengo un tipo decimal, un valor de $ 1,234 o $ 1234 aparece como 0 y el estado del modelo no es válido. –

+0

Miré a través de la fuente MVC y la fuente DataAnnotationsModelBinder, y creo que sería mejor llamar a base. Están sucediendo muchas cosas, incluido el manejo de los errores y el estado del modelo. Creo que hacer lo que estaba haciendo antes, pero usar la función decimal.Parse en su lugar funcionará muy bien. –

1

Puede crear su propio ValidationAttribute que verifique si el valor tiene el formato correcto. Luego, podría ver si la propiedad está decorada con este atributo y vincularla de la manera adecuada. El atributo no necesita ser ValidationAttibute, pero parece una buena idea.

5

En MVC3 puede simplemente registrar un encuadernador personalizado que implemente la interfaz IModelBinder específicamente para tipos decimales y luego decirle que maneje la moneda o el decimal utilizando la propiedad ModelMetaData.DataTypeName en el bindingContext.

He modificado la muestra proporcionada por Phil Haack in his article para demostrar cómo se podía hacer:

public class DecimalModelBinder : IModelBinder 
    { 

     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
     { 
      var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 
      var modelState = new ModelState { Value = valueResult }; 

      decimal actualValue = 0; 
      try 
      { 

       if(bindingContext.ModelMetadata.DataTypeName == DataType.Currency.ToString()) 
        decimal.TryParse(valueResult.AttemptedValue, NumberStyles.Currency, null, out actualValue); 
       else 
        actualValue = Convert.ToDecimal(valueResult.AttemptedValue,CultureInfo.CurrentCulture); 


      } 
      catch (FormatException e) 
      { 
       modelState.Errors.Add(e); 
      } 

      bindingContext.ModelState.Add(bindingContext.ModelName, modelState); 
      return actualValue; 
     } 
    } 
Cuestiones relacionadas