Custom Model Binder for decimals and integer: How

2020-04-12 08:39发布

问题:

I want to extend the default model binding to be more smart when dealing with numbers. The default works very bad when are commas and decimals points in the game.

I was trying the do a new binder

Public Class SmartModelBinder
    Inherits DefaultModelBinder
    Protected Overrides Sub SetProperty(controllerContext As ControllerContext, bindingContext As ModelBindingContext, propertyDescriptor As System.ComponentModel.PropertyDescriptor, value As Object)
        If propertyDescriptor.PropertyType Is GetType(Decimal) Or propertyDescriptor.PropertyType Is GetType(Decimal?) Then
            If value Is Nothing Then
                value = 0
            End If
        End If

        MyBase.SetProperty(controllerContext, bindingContext, propertyDescriptor, value)
    End Sub
End Class

But the value is already converted at this point

How can I extend the binder to get the string value from the form and perform a different transformation?

回答1:

What about this?

ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder())

And a custom binder. I guess I don't know if you can override decimal binding in this way, but it works for my own types.

public class DecimalModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (valueProviderResult == null)
        {
            return base.BindModel(controllerContext, bindingContext);
        }
        // to-do: your parsing (get the text value from valueProviderResult.AttemptedValue)
        return parsedDecimal;
    }
}


回答2:

The method you are probably looking for is BindModel. Here's a high level overview of how the default model binder works, suppose you have the following class:

public class MyModel
{
  public int Id;
  public string Name;
}

When MVC tries to bind data to MyModel, it calls BindModel on the default model binder. That binder determines that MyModel is not a "simple" data type (i.e. int, decimal, string etc). It then pulls out the possible members that it can bind to, then finds the correct model binder for each of those types and calls that model binder's BindModel method against the field/property, so model binding of a complex type is really a recursive call.

Normally I would suggest writing a model binder just for decimal and setting that as the model binder for that data type, but I've heard others have had issue with that (I haven't tried it myself). So I would try that first, and if that doesn't work, then just check for that model type in the BindModel method of your default model binder and handle that special case.

This is an extremely high level overview of model binding and wouldn't even begin to suggest that it's everything you need to know about how that area works.



回答3:

I'm adding an additional answer because Phil Haack blogged recently on exactly how to do this. He warns that it is untested, but he makes use of the ModelState and adds an error if needed, which is something I was never aware of when/how to do, so it was helpful to me.

Here's a link to his post: http://haacked.com/archive/2011/03/19/fixing-binding-to-decimals.aspx