我的帖子调用未返回正确的模型类型。它始终使用baseObject而不是我从Get
传入的正确派生对象RestaurantViewModel.cs
public class RestaurantViewModel{
public Food BaseFoodObject{get;set;}
}
Food.cs
public class Food{
public string Price{get;set;)
}
Bread.cs - 从食物继承
public class Bread:Food{
public int Unit{get;set;}
}
Milk.cs - 从食物中继承
public class Milk:Food{
public string Brand{get;set}
}
面包模板的编辑器。显示单位并允许用户编辑
的index.html
@Model RestaurantViewModel
@using(Html.BeginForm("SaveFood", "Food"))
{
@Html.EditorFor(m=>m.BaseFoodObject)
<input type="submit" value="Process"/>
}
Bread.cshtml
@Model Bread
<div>
@Html.TextboxFor(bread=>bread.Unit)
</div>
FoodController.cs
public ActionResult Index(){
Bread bread = new Bread(){
Price = "$10",
Unit = 1
}
RestaurantViewModel viewModel = new RestaurantViewModel(){
BaseFoodObject = bread
}
return View(viewModel);
}
public ActionResult Post(RestaurantViewModel viewModelPost)
{
// When I inspect the viewModelPost, there is no attribute for unit
}
最终结果: 1.显示器看起来正确。 EditorFor足够聪明,可以选择正确的编辑器模板并正确显示值 2.保存不有效。 面包对象的单元属性不会通过 RestaurantViewModel 传递。原因是RestaurantViewModel使用食物对象而不是面包
我希望修改EditorFor并告诉它使用视图中的模型或我在显示时传入的对象类型。< / p>
由于
更新1:我通过使用自定义绑定器并使用工厂来决定我真正想要的对象来解决这个问题。这有助于构建我想要的正确模型
答案 0 :(得分:2)
问题来自帖子。发布后,您拥有的是一组发布的数据和类型RestaurantViewModel
的参数。 modelbinder在Food
上设置了所有相应的字段,因为它只知道它们。其他一切都被丢弃了。关于这一点,我们无能为力。如果您需要发布与Bread
相关的字段,那么您的媒体资源类型必须为Bread
。这是唯一可行的方式。
答案 1 :(得分:2)
MVC是无国籍的。 couple的references。
你的问题中有几个与此冲突的语句,以及MVC绑定如何工作,例如:
我的帖子调用未返回正确的模型类型。
可能只是术语,但你的Post调用不会“返回模型类型” - 它会进入后期操作中定义的模型,在本例中为RestaurantViewModel
。
而不是我从Get
传入的正确派生对象
因为它是无状态的,所以它对你从get
传递的模型一无所知......绝对没有。
通过getaction + view.cshtml +模型呈现的最终html不是链接到postaction。您可以轻松地获取渲染的HTML,保存它,重新启动您的PC,重新加载渲染的HTML,它将以完全相同的方式工作。
一种修改EditorFor的方法,并告诉它使用View中的Model或我在显示时传入的Object Type
当您使用EditorFor
时,它会根据绑定的模型设置ID
和name
属性,因此它已经执行此操作,但您可能没有绑定到模型您想要绑定以获取正确的id
。
那么,对于这个问题,如果在'普通'C#代码中你要实例化RestaurantViewModel
的新实例,你会期望 BaseFoodObject的类型是什么?
这就是ModelBinder正在做的事情 - 它正在创建一个新的RestaurantViewModel
。
由于您的帖子操作方法的签名不包含与Bread
的任何内容 - 所有面包属性都会被忽略。
一些选项:
检查装订后的食物属性并手动阅读(可能是最快+最简单但不是很“mvc-ish”)
public ActionResult Post(RestaurantViewModel viewModelPost)
{
if (!string.IsNullOrEmpty(Request.Form["Unit"]))
// it's a bread form
为了使这更容易,您可以提供类型为
的隐藏字段 if (Request.Form["Type"] == typeof(Bread).Name)
{
var bread = new Bread { Unit = Request.Form["Unit"] }
为动作添加面包,使其受到约束
public ActionResult Post(RestaurantViewModel viewModelPost, Bread bread)
然而,显然,它不适用于牛奶。
因此可以使用ActionNameSelector
扩展此选项以选择正确的操作
public ActionResult PostBread(RestaurantViewModel viewModelPost, Bread bread)
public ActionResult PostMilk(RestaurantViewModel viewModelPost, Milk milk)
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class FoodSelectorAttribute : ActionNameSelectorAttribute
{
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
{
... check if provided parameters contains bread/milk
(related link但不是这个具体案例的解决方案)
另一种选择可能是将餐厅类型更改为通用,但需要更多更改(最好使用界面)和更多详细信息(此处提供的是一个想法,而不是解决方案)
基础知识将是:
public class RestaurantViewModel<T>
where T: Food
{
}
public ActionResult Post(RestaurantViewModel<Bread> viewModelPost)
public ActionResult Post(RestaurantViewModel<Milk> viewModelPost)
但是我没有确认默认的ModelBinder是否适用于这种情况。