【发布时间】:2016-09-06 12:03:20
【问题描述】:
我有以下视图模型结构: ItemViewModel 包含类型为 BaseViewModel 的属性 Model。
在这个具体示例中,Model 的类型是 WeekManagerWorkScheduleViewModel。该视图模型依次由 ManagerWorkScheduleViewModel 类型的 IEnumerable 组成。
在 ItemView.cshtml 中使用了以下内容:
<form class="form-horizontal" id="@Model.FormName" name="@Model.FormName" onsubmit="@Model.OnSubmitFunction">
<div class="form-group form-group-sm">
@Html.EditorFor(model => model.Model, Model.ItemViewName + "View")
</div>
<button class="btn pull-left" type="submit" style="margin: 2px;">
<span class="glyphicon glyphicon-save"></span>
@Model.ViewConfiguration.SaveText @Model.ItemTitle
</button>
</form>
我省略了一些细节(基本上是一堆 if 确定是否添加 CRUD 按钮。Model.ItemViewName 是类型名(在本例中为 WeekManagerWorkScheduleViewModel)。
public class WeekManagerWorkScheduleViewModel : BaseViewModel
{
[HiddenInput]
public int RegionId { get; set; }
[HiddenInput]
public IEnumerable<DateTime> Dates { get; set; }
public IEnumerable<ManagerWorkScheduleViewModel> WorkSchedules { get; set; }
}
WeekManagerWorkScheduleView.cshtml 如下所示:
@using DRSTransportPortal.ViewModels
@model WeekManagerWorkScheduleViewModel
@{
ViewBag.Title = "Ugentlig arbejdsplan - ledere";
}
@Html.HiddenFor(m => m.RegionId)
<table class="table">
<thead>
<tr>
<th><b>Leder</b></th>
<th><b>Uge</b></th>
<th><b>Mandag</b></th>
<th><b>Tirsdag</b></th>
<th><b>Onsdag</b></th>
<th><b>Torsdag</b></th>
<th><b>Fredag</b></th>
<th><b>Lørdag</b></th>
<th><b>Søndag</b></th>
</tr>
<tr>
<th></th>
<th></th>
@foreach (var date in Model.Dates)
{
<th><i><small>@date.Date.ToString("dd-MM-yyyy")</small></i></th>
}
</tr>
</thead>
<tbody>
@Html.EditorFor(m => m.WorkSchedules)
</tbody>
</table>
我在 Views/imalazysod/EditorTemplates 中有一个名为 ManagerWorkScheduleViewModel.cshtml 的视图(MVC 知道这个位置,因为我使用的是从 Razor 派生的自定义视图引擎):
@using DRSTransportPortal.ViewModels
@model ManagerWorkScheduleViewModel
<tr id="@Model.Id">
<td>@Html.HiddenFor(m => m.Id)</td>
<td>@Html.DisplayFor(m => m.ManagerName, new { htmlAttributes = new { @class = "form-control editoritem" } })</td>
<td>@Html.DisplayFor(m => m.DateWeekText, new { htmlAttributes = new { @class = "form-control editoritem" } })</td>
<td>@Html.EditorFor(m => m.MondayCodeChoice, new { @class = "form-control editoritem" })</td>
<td>@Html.EditorFor(m => m.TuesdayCodeChoice, new { @class = "form-control editoritem" })</td>
<td>@Html.EditorFor(m => m.WednesdayCodeChoice, new { @class = "form-control editoritem" })</td>
<td>@Html.EditorFor(m => m.ThursdayCodeChoice, new { @class = "form-control editoritem" })</td>
<td>@Html.EditorFor(m => m.FridayCodeChoice, new { @class = "form-control editoritem" })</td>
<td>@Html.EditorFor(m => m.SaturdayCodeChoice, new { @class = "form-control editoritem" })</td>
<td>@Html.EditorFor(m => m.SundayCodeChoice, new { @class = "form-control editoritem" })</td>
</tr>
“Choice”属性都是使用 ChoiceViewModel.cshtml 的 ChoiceViewModel 类型。
现在,一切正常: Screenshot (names omitted). Red box indicates 1 (one) nested viewmodel
生成的 HTML 如下所示(此处仅显示第一行和前几个单元格):
<tr id="134">
<td><input name="Model.WorkSchedules[0].Id" id="Model_WorkSchedules_0__Id" type="hidden" value="134" data-val-required="Feltet Id skal udfyldes." data-val="true" data-val-number="The field Id must be a number."></td>
<td>OMITTED</td>
<td>2016 - 36</td>
<td>
<select name="Model.WorkSchedules[0].MondayCodeChoice.SelectedValue" class="form-control editoritem dropdown" id="SelectChoices" onchange="">
<option value="1">06:00 - 14:00</option>
<option value="19">06:00 - 18:00</option>
<option value="31">08:00 - 16:00</option>
<option value="2">10:00 - 18:00</option>
<option value="32">10:00 - 18:00</option>
<option value="23">Bagvagt</option>
<option value="22">Ferie</option>
<option value="8">Fri</option>
<option value="3">Kontor</option>
<option value="15">Kussus</option>
<option value="16">Syg</option>
</select>
</td>
REST OF HTML IS OMMITTED (CONTINUES FOR 12 ROWS WITH 10 CELLS EACH)
</tr>
但是,当我回帖(使用 jQuery、ajax 顺便说一句)时,我得到的是: Controller breakpoint, after modelbinding
我尝试在 BaseViewModel 上放置一个自定义模型绑定器,并进行调试,但没有发现任何看起来不正常的东西。它解析正确的类型(WeekManagerWorkScheduleViewModel 等)。 使用它,我的来自 ControllerContext->...->Request 的 Forms 元素是: {Model.Id=141&ModelType=DRSTransportPortal.ViewModels.WeekManagerWorkScheduleViewModel%2c+DRSTransportPortal%2c+Version%3d1.0.0.0%2c+Culture%3dneutral%2c+PublicKeyToken%3dnull&Model.RegionId=1&Model.WorkSchedules%5b0%5d.Id =134&Model.WorkSchedules%5b0%5d.MondayCodeChoice.SelectedValue=22&Model.WorkSchedules%5b0%5d.TuesdayCodeChoice.SelectedValue=1&Model.WorkSchedules%5b0%5d.WednesdayCodeChoice.SelectedValue=1&Model.WorkSchedules%5b0%5d.ThursdayCodeChoice.SelectedValue=1&Model .WorkSchedules%5b0%5d.FridayCodeChoice.SelectedValue=1&Model.WorkSchedules%5b0%5d.SaturdayCodeChoice.SelectedValue=1&Model.WorkSchedules%5b0%5d.SundayCodeChoice.SelectedValue=1&.....所有12个嵌套视图模型继续}
我已确定以下内容属实: 所有绑定都对属性进行(默认即 {get;set;}) 包括 BaseViewModel 在内的所有视图模型都没有构造函数(即应创建默认构造函数) 所有属性和类都是公开的
所以...我的问题是,为什么模型绑定器无法创建列表的视图模型?我在其他地方使用模板没有问题,编辑 pr。人,即一次一行)。 是的,我知道有很多与 MVC 模型绑定相关的问题和答案,但似乎没有一个完全属于这一类(书籍也是如此)。真正让我感到困惑的是,它认识到需要一个包含 12 个项目的列表,但它只是没有填充。
编辑:
public class BaseViewModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var typeValue = bindingContext.ValueProvider.GetValue("ModelType");
var type = Type.GetType((string) typeValue.ConvertTo(typeof (string)), true);
if(!typeof(BaseViewModel).IsAssignableFrom(type))
throw new InvalidOperationException("NOT A BASEVIEWMODEL");
var model = Activator.CreateInstance(type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
}
}
[ModelBinder(typeof(BaseViewModelBinder))]
public class BaseViewModel
{
[HiddenInput]
public int Id { get; set; }
[HiddenInput]
public DateTime Created { get; set; }
[HiddenInput]
public DateTime Edited { get; set; }
public virtual string SelectionName { get; set; }
}
编辑 2:
[HttpPost]
public override async Task<JsonResult> Save(ItemViewModel item, string parentName, int? parentId)
{
if (item?.Model == null)
{
const int result = 0;
return Json(new { result });
}
var model = item.Model as WeekManagerWorkScheduleViewModel;
if (model == null)
{
const int result = 0;
return Json(new { result });
}
foreach (var inner in model.WorkSchedules)
{
}
return await base.Save(item, parentName, parentId);
}
public class ManagerWorkScheduleViewModel : BaseViewModel
{
[HiddenInput]
public int? ManagerId { get; set; }
[DisplayName("Leder")]
public string ManagerName { get; set; }
[HiddenInput]
public int? DateWeekId { get; set; }
[DisplayName("År / Uge")]
public string DateWeekText { get; set; }
[HiddenInput]
public int? MondayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Mandag")]
public ChoiceViewModel MondayCodeChoice { get; set; }
[HiddenInput]
public int? TuesdayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Tirsdag")]
public ChoiceViewModel TuesdayCodeChoice { get; set; }
[HiddenInput]
public int? WednesdayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Onsdag")]
public ChoiceViewModel WednesdayCodeChoice { get; set; }
[HiddenInput]
public int? ThursdayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Torsdag")]
public ChoiceViewModel ThursdayCodeChoice { get; set; }
[HiddenInput]
public int? FridayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Fredag")]
public ChoiceViewModel FridayCodeChoice { get; set; }
[HiddenInput]
public int? SaturdayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Lørdag")]
public ChoiceViewModel SaturdayCodeChoice { get; set; }
[HiddenInput]
public int? SundayCodeId { get; set; }
[UIHint("ChoiceViewModel")]
[DisplayName("Søndag")]
public ChoiceViewModel SundayCodeChoice { get; set; }
}
【问题讨论】:
-
哦,顺便说一句,使用 ASP.NET MVC 5(我认为 ..Core 之前的那个 :))
-
您不能使用派生类。您还没有显示相关代码(您的模型或控制器方法,但如果属性
Model是BaseViewModel的类型,那么DefaultModelBinder将初始化BaseViewModel,而不是WeekManagerWorkScheduleViewModel和WeekManagerWorkScheduleViewModel的任何属性不在BaseViewModel中的将被忽略 -
如果我在模型绑定器中放置一个断点,它确实使用了正确的模型,而且,顶层模型上的所有属性(具有隐藏输入)都正确创建并给出了正确的值。此外,对于从 BaseViewModel 派生的所有视图模型,它就像一个魅力,这些视图模型没有帖子中需要的嵌套视图模型。
标签: asp.net asp.net-mvc model-binding asp.net-mvc-viewmodel