C# 继承模型MVC的编辑器

C# 继承模型MVC的编辑器,c#,asp.net,asp.net-mvc,asp.net-mvc-4,razor,C#,Asp.net,Asp.net Mvc,Asp.net Mvc 4,Razor,我的Post调用未返回正确的型号类型。它总是使用baseObject,而不是我从Get传入的正确的派生对象 RestaurantViewModel.cs餐厅 public class RestaurantViewModel{ public Food BaseFoodObject{get;set;} } 食品公司 public class Food{ public string Price{get;set;) } Bread.cs——从食物中继承 public class Bread

我的Post调用未返回正确的型号类型。它总是使用baseObject,而不是我从Get传入的正确的派生对象

RestaurantViewModel.cs餐厅

public class RestaurantViewModel{
   public Food BaseFoodObject{get;set;}
}
食品公司

public class Food{
  public string Price{get;set;)
}
Bread.cs——从食物中继承

public class Bread:Food{
    public int Unit{get;set;} 
}
public class Milk:Food{
   public string Brand{get;set}
}
Milk.cs——从食物中继承

public class Bread:Food{
    public int Unit{get;set;} 
}
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"/>
}
最终结果: 1.显示器看起来正确。EditorFor足够智能,可以选择正确的编辑器模板并正确显示值 2.保存不起作用。面包对象的单位属性不会与餐馆视图模型一起传递。原因是RestaurantViewModel使用了食物对象而不是面包

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

谢谢


更新1:我通过使用自定义活页夹和工厂来决定我真正想要的对象,从而解决了这个问题。这有助于构建我想要的正确模型。这篇文章带来了问题。发布后,您所拥有的只是一组发布的数据和一个参数类型,
RestaurantViewModel
。modelbinder在
Food
上设置所有适当的字段,因为它只知道这些。其他一切都被丢弃了。对此无能为力。如果需要发布与
Bread
相关的字段,则属性的类型必须是
Bread
。这是它工作的唯一方式。

MVC是无状态的。一堆

在您的问题中有两种说法与此相冲突,以及MVC绑定是如何工作的,例如:

我的Post调用未返回正确的型号类型

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

而不是我从Get中传入的正确的派生对象

因为它是无状态的,所以它对您从
get
传递的模型一无所知。。。绝对没有

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

一种修改编辑器for并告诉它在视图中使用模型或显示时传入的对象类型的方法

当您使用
EditorFor
时,它会根据绑定到的模型设置
ID
name
属性,因此它已经这样做了,但您可能没有绑定到要绑定到的模型以获得正确的
ID


因此,对于这个问题,如果在“普通”C#代码中,您要实例化
RestaurantViewModel
的新实例,那么您希望BaseFoodObject的类型是什么

这就是ModelBinder所做的-它正在创建一个新的
RestaurantViewModel

由于您的post-action方法的签名不包括与
面包有关的任何内容
——因此将忽略所有面包属性

一些选择:


绑定后检查食物属性并手动读取(可能是最快+最简单但不是很“mvc-ish”)

为了使这更容易,您可以提供一个类型为的隐藏字段

    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<T> 
     where T: Food
{
}

public ActionResult Post(RestaurantViewModel<Bread> viewModelPost)

public ActionResult Post(RestaurantViewModel<Milk> viewModelPost)
public class RestaurantViewModel
T:食物
{
}
公共行动结果公告(RestaurantViewModel viewModelPost)
公共行动结果公告(RestaurantViewModel viewModelPost)

但我还没有确认默认的ModelBinder在这种情况下是否有效

您认为可以在Bread.cshtml中隐藏(模型类型)吗?当页面呈现时,模型类型应该是Bread,我们可以扩展ModelBinder来查找该字段。您可能可以这样做,但我通常不赞成使用ModelBinder,尤其是对于特定和狭窄的场景。我几乎认为只使用一个<代码> FuffCube 并手动更新并将这些值绑定到您的模型是比较合适的。我已经解决了它使用自定义活页夹和工厂。你的建议很好。
public class RestaurantViewModel<T> 
     where T: Food
{
}

public ActionResult Post(RestaurantViewModel<Bread> viewModelPost)

public ActionResult Post(RestaurantViewModel<Milk> viewModelPost)