ASP.NET MVC - Mixing Custom and Default Model Binding
Asked Answered
P

3

38

I have a type:

public class IssueForm
{
    Order Order {get; set;}
    Item Item {get; set;}
    Range Range {get; set;}
}

I created a custom model binder due to requirements on Order and Item, but Range could still use the Default Model Binder.

Is there a way from within my custom model binder to call the default model binder to return a Range object? I think I just have to just setup ModelBindingContext correctly, but I don't know how.


EDIT

Looking at the first comment and answer -- it seems like inheriting from the default model binder could be useful.

To add more specifics for my setup so far I have:

public IssueFormModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        Order = //code to pull the OrderNumber from the context and create an Order
        Item = //code to pull the ItemNumber from the context and create an Item

        IssueForm form = IssueFormFactory.Create(Order, Item);

        form.Range = // ** I'd like to replace my code with a call to the default binder **

        return form
    }
}

This might be a stupid way of doing it... this is my first model binder. Just pointing out my current implementation.


EDIT #2

So the answers to override BindProperty will work if I can hook into like a "I'm all done binding" method and call the Factory method with the properties.

I guess I really should look at the DefaultModelBinder implementation and quit being stupid.

Perpetuate answered 9/6, 2009 at 14:17 Comment(3)
I'm not sure if it is the most elegant solution, but could you derive your custom model binder from DefaultModelBinder rather than implementing IModelBinder? Then when you override BindModel, pass the responsibility through to the DefaultModelBinder where appropriate. Otherwise, use custom binding.Condyloid
Yah, I'm seeing some model binders go off of IModelBinder and DefaultModelBinder. I guess the DefaultModelBinder has a few extra methods that I assume it calls from the BindModel method, so you can focus your needs more?Perpetuate
It only confirms my decision to ignore ModelBinder and code input processing and validation manually. Now I have it consistent, transparent and adjustable to present and imaginable future needs.Hendren
G
25

Try something like this:

public class CustomModelBinder :  DefaultModelBinder {
    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) {
        if(propertyDescriptor.Name == "Order") {
            ...
            return;
        }

        if(propertyDescriptor.Name == "Item") {
            ...
            return;
        }

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

}
Gillmore answered 9/6, 2009 at 14:30 Comment(1)
I'm likely being stupid here but how do you implement the CustomModelBinder class, surely that has to be instantiated somewhere for this to work? Maybe as an attribute in the ActionResult params?Ameline
V
54

override the BindProperty from the DefaultModelBinder:

public class CustomModelBinder:DefaultModelBinder
        {
            protected override void BindProperty( ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor )
            {
                if (propertyDescriptor.PropertyType == typeof(Range))
                {
                    base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
                }
                // bind the other properties here
            }
        }
Vapid answered 9/6, 2009 at 14:36 Comment(1)
+1 for inspedcting property type instead of the name. Blame magic strings!Ferryboat
G
25

Try something like this:

public class CustomModelBinder :  DefaultModelBinder {
    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) {
        if(propertyDescriptor.Name == "Order") {
            ...
            return;
        }

        if(propertyDescriptor.Name == "Item") {
            ...
            return;
        }

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

}
Gillmore answered 9/6, 2009 at 14:30 Comment(1)
I'm likely being stupid here but how do you implement the CustomModelBinder class, surely that has to be instantiated somewhere for this to work? Maybe as an attribute in the ActionResult params?Ameline
W
6

I think I would have registered two different custom model binders, one for Order and one for Item, and let the default model binder handle the Range and the IssueForm.

Wylde answered 3/3, 2010 at 19:48 Comment(1)
Yes, obviously its been awhile since I asked this question. I ended up looking at the DefaultModelBinder code and realizing the recursive nature of model binding. I didn't realize with a complex type containing other complex types I could just define a model binder for each one individually. I instead thought the complex parent would have to know the details of each complex child.Perpetuate

© 2022 - 2024 — McMap. All rights reserved.