Question

Based on user input, I want to nullify some properties of an entity before it gets to the controller's Action. In a crude example (the real model is a lot more complicated), let's say my entity have a BillingType property that defines if the client will be billed either monthly or fortnightly:

public class BillingMethod
{
    public int Id { get; set; }
    public int BillingTypeValue { get; set; }
    public BillingType BillingType
    {
        get
        {
            return (BillingType)BillingTypeValue;
        } 
        set
        {
            BillingTypeValue = (int)value;
        }
    }

    public int? DayOfMonth { get; set; }
    public int? DayOfFirstFortnight { get; set; }
    public int? DayOfSecondFortnight { get; set; }
}

public enum BillingType
{
    Monthly,
    Fortnightly
}

Now let's say the user chooses to charge monthly and then sets the DayOfMonth property to 15. Then he changes his mind and sets the Billing Type to fortnightly and sets the two fortnigth day properties and finnaly submits the form. What I need is a way to nullify the unused property (DayOfMonth, in this example) before it gets to the Controller's Action.

I know I can do this via javascript as the user changes from one billing type to another, or by intercepting the form's onSubmit event. Or even inside the Action, before saving it to the context, but I need a way to do it once and forget about it.

I think the best way to do this is by using a custom model binder but I don't have any experience at it. I tried creating a new ModelBindingContext but I couldn't realize how to get and parse form data inside a new object so I clearly need some directions.

Was it helpful?

Solution 2

I managed to modify the object using a custom binder. First I call base.BindModel and then I modify the properties before returning it.

public class BillingMethodModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext cContext, ModelBindingContext bContext)
    {
        var newBillingMethod = (BillingMethod)base.BindModel(cContext, bContext);
        var bType = newBillingMethod.BillingTypeValue;
        if (bType == (int)BillingType.Monthly)
        {
            newBillingMethod.DayOfFirstFortnight = null;
            newBillingMethod.DayOfSecondFortnight = null;
        }
        else
        {
            newBillingMethod.DayOfMonth = null;
        }

        return newBillingMethod;
    }
}

And, of course, add the custom binder in global.asax's Application_Start():

ModelBinders.Binders.Add(typeof(BillingMethod), new BillingMethodModelBinder());

That worked great for me, I'll wait for a day or so before accepting this answer just in case someone comes with a better solution or points me to any problems I may encounter while using this method

OTHER TIPS

I would be careful doing this in a custom model binder. One problem you'll run into is that each property is bound individually and you don't have a lot of control over the order. It sounds like you need to make some decisions based on the state of the object after binding has completed.

I'd probably use an action filter in this case. See: http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.onactionexecuting(v=vs.98).aspx

public class BillMethodFilterAttribute : ActionFilterAttribute {

     protected override void OnActionExecuting(ActionExecutingContext filterContext) {
        base.OnActionExecuting(filterContext);
           if (filterContext.Controller.ViewData.ModelMetadata.ModelType == typeof(BillingMethod)) {
               var method = filterContext.Controller.ViewData.Model as BillingMethod;
               if (method != null) {
                  //assign appropriately - binding is complete, you have full state of the object
               }
           }
        }        
}

[BillMethodFilter]
public abstract class ProjectController : Controller {


}

public class SomeController : ProjectController {

      public ActionResult SomeAction(BillingMethod method) {
            //before this action runs, the BillMethodFilter should execute and your billing method will be fully initialized correctly
      }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top