EditorFor Inheritance Model MVC
我的post调用没有返回正确的模型类型。 它始终使用baseObject而不是我从Get传入的正确派生对象
RestaurantViewModel.cs
public class RestaurantViewModel{ public Food BaseFoodObject{get;set;} }
Food.cs
public class Food{ public string Price{get;set;) }
Bread.cs – 从食物inheritance
public class Bread:Food{ public int Unit{get;set;} }
Milk.cs – 从食物中inheritance
public class Milk:Food{ public string Brand{get;set} }
面包模板的编辑。 显示单位并允许用户编辑
的index.html
@Model RestaurantViewModel @using(Html.BeginForm("SaveFood", "Food")) { @Html.EditorFor(m=>m.BaseFoodObject) }
Bread.cshtml
@Model Bread @Html.TextboxFor(bread=>bread.Unit)
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.保存不起作用 。 Bread Object的Unit属性不会通过RestaurantViewModel传入。 原因是RestaurantViewModel使用了Food对象而不是Bread
我希望能够修改EditorFor并告诉它使用View中的Model或我在显示时传入的Object Type。
谢谢
更新1:我通过使用自定义绑定器并使用工厂来决定我真正想要的对象来解决这个问题。 这有助于构建我想要的正确模型
MVC是无国籍的。 几个 参考 。
您的问题中有几个与此冲突的语句,以及MVC绑定如何工作,例如:
我的post调用没有返回正确的模型类型。
可能只是术语,但你的Post调用不会“返回模型类型” – 它会进入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
。
由于您的post action方法的签名不包含任何与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
( 相关链接,但不是这个具体案例的解决方案)
另一个选择可能是将餐厅类型更改为通用,但需要更多更改(并且理想情况下使用接口),以及更多详细信息(此处提供的是一个想法,而不是解决方案)
基础是:
public class RestaurantViewModel where T: Food { } public ActionResult Post(RestaurantViewModel viewModelPost) public ActionResult Post(RestaurantViewModel viewModelPost)
但是我没有确认默认的ModelBinder是否适用于这种情况。
问题来自post。 发布后,您所拥有的只是一组发布的数据和类型参数, RestaurantViewModel
。 modelbinder在Food
上设置了所有相应的字段,因为这就是它所知道的。 其他一切都被丢弃了。 关于这一点,没有什么可以做的。 如果您需要发布与Bread
相关的字段,那么您的属性类型必须是Bread
。 这是唯一可行的方式。