0

One of my classes has some properties that I want model binder to always ignore them.

Currently I'm using [Bind(Exclude ="")] in the action methods, like this:

public ActionResult Edit([Bind(Exclude = "prop1, prop2, prop3")] BusinessModel model)

This class has been used in several action methods. Do I have to manually exclude them, or there is a better way?

I should note in this particular class, they're navigation properties.

Akbari
  • 2,369
  • 7
  • 45
  • 85
  • 4
    Use a view model rather than your data model - [What is ViewModel in MVC?](http://stackoverflow.com/questions/11064316/what-is-viewmodel-in-mvc) –  Jun 06 '15 at 06:23

2 Answers2

2

ramiramilu's answer is great, but I would like to add a minor detail to it.

It's probably better to return base.BindModel with a new BindingContext which excludes the properties to be ignored instead of returning a BusinessModel object out straight.

This lets the DefaultModelBinder's ModelState validation mechanism to kick in.

public class CustomDataBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType == typeof(BusinessModel))
        {
            BindAttribute bindAttribute = new BindAttribute
            {
                Exclude = "prop1, prop2, prop3"
            };

            // create new property filter where only properties which are not exluded in the above bindAttribute are bound
            Predicate<string> newPropertyFilter = propertyName => bindAttribute.IsPropertyAllowed(propertyName) && bindingContext.PropertyFilter(propertyName);         

            ModelBindingContext newBindingContext = new ModelBindingContext()
            {
                //bind default ModelMetaData
                ModelMetadata = bindingContext.ModelMetadata,

                //set to new property filter which exludes certain properties
                PropertyFilter = newPropertyFilter,

                //bind default ModelState
                ModelState = bindingContext.ModelState,

                //bind default ValueProvider
                ValueProvider = bindingContext.ValueProvider
            };
            return base.BindModel(controllerContext, newBindingContext);
        }
        else
        {
            return base.BindModel(controllerContext, bindingContext);
        }
    }
}
Community
  • 1
  • 1
Chid
  • 21
  • 4
1

One way to solve this problem using centralized code is to override BindModel of DefaultModelBinder and exclude the properties which you don't want to bind.

public class CustomDataBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType == typeof(BusinessModel))
        {
            HttpRequestBase request = controllerContext.HttpContext.Request;
            string name = request.Form.Get("Name");
            return new BusinessModel
            {
                Name = name
            };                
        }
        else
        {
            return base.BindModel(controllerContext, bindingContext);
        }
    }
}

And then register it at Global.asax in Application_Start().

ModelBinders.Binders.Add(typeof(BusinessModel), new CustomDataBinder());

In above case I have used BusinessModel as described as below -

public class BusinessModel
{
    public string prop1 { get; set; }
    public string Name { get; set; }
}

To test, I have created a simple view -

@model WebApplication1.Controllers.BusinessModel

@using (Html.BeginForm("PostData", "Home"))
{
    @Html.EditorFor(model => model.prop1, new { htmlAttributes = new { @class = "form-control" } })
    @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })

    <input type="submit" value="Create" class="btn btn-default" />
}

When view renders, I entered some data in both the editors - enter image description here

When I hit create button, the entered value in Prop1 editor was not binded -

enter image description here

ramiramilu
  • 17,044
  • 6
  • 49
  • 66