MVC Complex model binding with inheritance, nested

2019-04-01 22:03发布

I don't manage to get the values of my nested models back to the controller, they are all nulls.

Here is the simplified architecture:

//The viewModel being passed to the view
public class RunnerIndexViewModel
{
    public RegisterViewModel User { get; set; }

    public TrainerViewModel TrainerVM { get; set; }

    public RunnerBase Runner { get; set; }

    [Display(Name = "AddContact", ResourceType = typeof(MyRessources))]
    public bool AddContact { get; set; }
}

public class RegisterViewModel
{
    // various simple type properties here
}

public class TrainerViewModel
{
    // various properties here
    public Unit unit { get; set; }

    public List<SelectListItem> ListStatut { get; set; }
}

public abstract partial class RunnerBase
{
    // entity framework class with associations
}

public class RedRunner : RunnerBase
{
    // entity framework class with associations
}

public class BlueRunner : RunnerBase
{
    // entity framework class with associations
}

Here is the main view that receives the model from the controller (simplified):

@model Web.Models.Fournisseurs.FournisseurIndexViewModel

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    @Html.Partial("PartialTrainer", Model.TrainerVM)
    @Html.Partial("PartialRunner", Model.Runner as RedRunner)
    @Html.Partial("PartialUser", Model.User)
}

The views PartialTrainer and PartialUser have nothing special, so here is the PartialRunner view that takes the base class from entity framework:

@model RunnerBase

@* Show Various fields from RunnerBase ... *@

@if (Model is RedRunner)
{
    @* show properties specific to RedRunner *@
}
else if (Model is BlueRunner)
{
    @* show properties specific to BlueRunner *@
}   

From the controller I either pass a RedRunner or a BlueRunner to the Runner property. All fields for all viewModels show up well but when submitting the form, I only manage to get the AddContact value...

How can I get the value of my other viewmodels and the one from the Runner class?

1条回答
一纸荒年 Trace。
2楼-- · 2019-04-01 22:44

The problem is with using partials. Each time you go into the a partial, the model context changes, so the inputs that Razor generates for you are not taking the full property path into account. For example, if you were to do the following in your main view:

@Html.EditorFor(m => m.TrainerVM.SomeProperty)

The resulting HTML would be akin to:

<input type="text" name="TrainerVM.SomeProperty" />

However, when using a partial, your helper would be called as:

@Html.EditorFor(m => m.SomeProperty)

And the resulting HTML would be akin to:

<input type="text" name="SomeProperty" />

Once this gets posted back, the modelbinder doesn't know that SomeProperty belongs to the TrainerVM instance so it can't determine where to bind the posted value, and you end up with nulls. To continue to use partials but side-step this problem, you'll need to pass a value for ViewData.TemplateInfo.HtmlFieldPrefix (which is used to properly prefix the names of all the fields in the partial):

@Html.Partial("PartialTrainer", Model.TrainerVM, new ViewDataDictionary(ViewData)
{
    TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "TrainerVM" }
})
查看更多
登录 后发表回答