继承模型MVC

我的帖子呼叫未返回正确的模型类型.它始终使用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.保存不起作用. Bread对象的Unit属性不会与RestaurantViewModel一起传递.原因是RestaurantViewModel使用Food对象而不是Bread

我希望可以修改EditorFor并告诉它使用视图中的模型或显示它时传入的对象类型.

谢谢

更新1:我通过使用自定义绑定程序并使用工厂来决定我真正想要的对象,解决了这个问题.这有助于构建我想要的正确模型

解决方法:

MVC是无状态的. referencescouple.

您的问题中有一些与此矛盾的内容,以及MVC绑定的工作原理,例如:

My Post call does not return the correct Model type.

可能只是术语,但您的Post调用不会“返回模型类型”-它进入在post操作中定义的模型中,在本例中为RestaurantViewModel.

instead of the correct derived object that I passed in from the Get

因为它是无状态的,所以它对您从get传入的模型一无所知…绝对一无所获.

通过getaction view.cshtml模型呈现的最终html未链接到后期操作.您可以轻松地获取渲染的html,保存它,重新启动PC,重新加载渲染的html,它的工作方式完全相同.

a way to modify the EditorFor and tell it to use the Model in the View or the Object Type that I passed in when I display it

当您使用EditorFor时,它根据绑定到的模型设置ID和名称属性,因此它已经做到了,但是也许您未绑定到要绑定以获取正确ID的模型.

因此,问题在于,如果您要在“正常” C#代码中实例化RestaurantViewModel的新实例,那么您期望BaseFoodObject的类型是什么?

这就是ModelBinder所做的-创建一个新的RestaurantViewModel.

由于您的后操作方法签名不包括与Bread有关的所有内容-所有面包属性都将被忽略.

一些选项:

结合后检查食物特性并手动阅读(可能是最快,最简单的方法,但不是很“笨拙”)

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在这种情况下是否可以使用.

上一篇:在重载返回视图的Controller时,应如何基于ViewBag属性加载其他内容?


下一篇:c#-MVC发布页面部分-模型绑定失败