简体   繁体   中英

How to chain binders in asp.net MVC 4?

I just wrote my first model binder in asp.net MVC. I started with a pure HTML form that had all the inputs with names prefixed by "fm_".

public class HuggiesModel
{
    public string fm_firstname { get; set; }
    public DateTime fm_duedate { get; set; }
}

The default binder worked fine which I saw as a nice time saver.

Then I decided I wanted cleaner property names so I changed to:

[ModelBinder(typeof(HuggiesModelBinder))]
public class HuggiesModel
{
    public string FirstName { get; set; }
    public DateTime? DueDate { get; set; }
}

And the model binder:

public class HuggiesModelBinder : IModelBinder
{
    private HttpRequestBase request;

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
            throw new ArgumentNullException("bindingContext");

        this.request = controllerContext.HttpContext.Request;

        var model = new HuggiesModel
        {
            FirstName = GetQueryStringValue("fm_firstname"),
        };

        string dateTimeString = GetQueryStringValue("fm_duedate");
        model.DueDate = string.IsNullOrWhiteSpace(dateTimeString) 
                                    ? default(DateTime?) 
                                    : DateTime.Parse(dateTimeString);

        return model;
    }

    private string GetQueryStringValue(string key)
    {
        if (this.request.HttpMethod.ToUpper() == "POST")
            return this.request.Form[key];
        return this.request.QueryString[key];
    }
}

Is there a way I could have implemented this such that I avoid having to parse the DateTime and get the default binder to do that for me?

Notes :

I realize I could just change the form input names to match the model names I desire but I purposely didn't do that to gain experience writing a model binder and that led me to this question.

The title of the question is based on the conceptual idea of what I'm trying to do - create a chain that looks like:

request
  -> default model binder binds get/post data to first view model
         -> my model binder binds first view model to second view model
               -> controller action method is called with second view model

You can extend the DefaultModelBinder rather than implementing IModelBinder.

Here's an example for

    public class HuggiesModelBinder:DefaultModelBinder
    {
        protected override void BindProperty( ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor )
        {
            if (propertyDescriptor.PropertyType == typeof(DateTime))
            {

                base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
            }
            // bind the rest of the properties here
        }
    }

However if we consider a more realistic scenario where your HuggiesModel is comprised of complex types plus simple types (like what you have now), you'd use the Default Model binding with the simple types (Together with naming conventions - ie having FirstName property instead of fm_firstname). For complex types you'd implement a custom model binder for each type. You don't necessarily need to have 1 large custom model binder for 'HuggiesModel'.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM