Mike Koder
Mike Koder

Reputation: 1928

ASP.NET MVC (4) - Bind properties in a certain order

Is there a way to force binding of properties A and B before C?

There's Order property in the System.ComponentModel.DataAnnotations.DisplayAttribute class, but does it affect binding order?

What i'm trying to achieve is

page.Path = page.Parent.Path + "/" + page.Slug

in a custom ModelBinder

Upvotes: 6

Views: 1474

Answers (2)

Dangerous
Dangerous

Reputation: 4909

I would have initially recommended Sams answer as it would have not involved any binding of the Path property at all. You mentioned that you could concatenate the values using a Path property as this would cause lazy loading to occur. I imagine therefore you are using your domain models to display information to the view. I would therefore recommend using view models to display only the information required in the view (then use Sams answer to retrieve the path) and then map the view model to the domain model using a tool (i.e. AutoMapper).

However, if you continue to use your existing model in the view and you cannot use the other values in the model, you can set the path property to the values provided by the form value provider in a custom model binder after the other binding has occurred (assuming no validation is to be performed on the path property).

So lets assume you have the following view:

@using (Html.BeginForm())
{
    <p>Parent Path: @Html.EditorFor(m => m.ParentPath)</p>
    <p>Slug: @Html.EditorFor(m => m.Slug)</p>
    <input type="submit" value="submit" />
}

And the following view model (or domain model as the case may be):

public class IndexViewModel { public string ParentPath { get; set; } public string Slug { get; set; } public string Path { get; set; } }

You can then specify the following model binder:

public class IndexViewModelBinder : DefaultModelBinder
    {
        protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            //Note: Model binding of the other values will have already occurred when this method is called.

            string parentPath = bindingContext.ValueProvider.GetValue("ParentPath").AttemptedValue;
            string slug = bindingContext.ValueProvider.GetValue("Slug").AttemptedValue;

            if (!string.IsNullOrEmpty(parentPath) && !string.IsNullOrEmpty(slug))
            {
                IndexViewModel model = (IndexViewModel)bindingContext.Model;
                model.Path = bindingContext.ValueProvider.GetValue("ParentPath").AttemptedValue + "/" + bindingContext.ValueProvider.GetValue("Slug").AttemptedValue;
            }
        }
    }

And finally specify that this model binder is to be used by using the following attribute on the view model:

[ModelBinder(typeof(IndexViewModelBinder))]

Upvotes: 0

Sam
Sam

Reputation: 1834

Why not implement the Page property as:

public string Path{
    get { return string.Format("{0}/{1}", Parent.Path, Slug); }
}

?

Upvotes: 1

Related Questions