简体   繁体   English

EditorFor 继承模型MVC

[英]EditorFor Inheritance Model MVC

My Post call does not return the correct Model type.我的 Post 调用没有返回正确的模型类型。 It always use the baseObject instead of the correct derived object that I passed in from the Get它总是使用 baseObject 而不是我从 Get 传入的正确派生对象

RestaurantViewModel.cs RestaurantViewModel.cs

public class RestaurantViewModel{
   public Food BaseFoodObject{get;set;}
}

Food.cs食品.cs

public class Food{
  public string Price{get;set;)
}

Bread.cs -- Inherit from Food Bread.cs——从食物继承

public class Bread:Food{
    public int Unit{get;set;} 
}

Milk.cs -- Inherit from Food Milk.cs——从食物中继承

public class Milk:Food{
   public string Brand{get;set}
}

Editor For Template for Bread.面包模板的编辑器。 Display the unit and allow user to edit显示单位并允许用户编辑

Index.html索引.html

@Model RestaurantViewModel

@using(Html.BeginForm("SaveFood", "Food"))
{
  @Html.EditorFor(m=>m.BaseFoodObject)
 <input type="submit" value="Process"/>
}

Bread.cshtml面包.cshtml

@Model Bread
<div> 
   @Html.TextboxFor(bread=>bread.Unit)
</div>

FoodController.cs 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
}

Final Result: 1. The display looks correct.最终结果: 1. 显示正确。 EditorFor is smart enough to pick the correct editor template and display the value correctly 2. The Save does not work. EditorFor 足够聪明,可以选择正确的编辑器模板并正确显示值 2. 保存不起作用 The Unit attribute of Bread Object does not get passed in with the RestaurantViewModel . Bread ObjectUnit属性不会与RestaurantViewModel一起传入。 The reason for that is the RestaurantViewModel used the Food object instead of Bread原因是 RestaurantViewModel 使用Food对象而不是Bread

I hope there is away 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 并告诉它使用视图中模型或我在显示时传入的对象类型。

Thanks谢谢

Update 1: I solved this problem by using the custom binder and using a factory to decide which object I really want.更新 1:我通过使用自定义绑定器并使用工厂来决定我真正想要哪个对象来解决这个问题。 This helps construct the correct Model which I want这有助于构建我想要的正确模型

The problem comes with the post.问题出在帖子上。 Once you post, all you have is a set of posted data and a parameter of type, RestaurantViewModel .发布后,您所拥有的只是一组发布的数据和一个类型为RestaurantViewModel的参数。 The modelbinder sets all the appropriate fields on Food because that's all it knows.模型绑定器在Food上设置所有适当的字段,因为它只知道这些。 Everything else is discarded.其他一切都被丢弃。 There's nothing that can be done about this.对此没有什么可以做的。 If you need to post fields related to Bread then the type of your property must be Bread .如果您需要发布与Bread相关的字段,那么您的财产类型必须是Bread That's the only way it will work.这是唯一可行的方法。

MVC is stateless. MVC 是无状态的。 A couple of references . 几个参考

There's a couple of statements in your question that conflict with this, and how MVC binding works eg:您的问题中有几个陈述与此冲突,以及 MVC 绑定的工作原理,例如:

My Post call does not return the correct Model type.我的 Post 调用没有返回正确的模型类型。

Possibly just terminology, but your Post call does not 'return a model type' - it goes into the model that's defined in the post action, in this case RestaurantViewModel .可能只是术语,但您的 Post 调用不会“返回模型类型” - 它进入 post 操作中定义的模型,在本例中为RestaurantViewModel

instead of the correct derived object that I passed in from the Get而不是我从 Get 传入的正确派生对象

because it is stateless, it knows nothing about the model you passed in from the get ... absolutely nothing.因为它是无状态的,它对你从get传入的模型一无所知......绝对没有。

The final html rendered via the getaction+view.cshtml+model is not linked to the postaction.通过 getaction+view.cshtml+model 呈现的最终 html 未链接到 postaction。 You could just as easily take the rendered html, save it, reboot your PC, reload the rendered html and it will work exactly the same way.您可以轻松地获取渲染的 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 并告诉它使用视图中的模型或我在显示时传入的对象类型的方法

When you use EditorFor it sets an ID and name attribute based on the model it was bound to, so it already does this, but perhaps you are not binding to the model you want to bind to to get the correct id .当您使用EditorFor它会根据绑定到的模型设置IDname属性,因此它已经这样做了,但也许您没有绑定到要绑定到的模型以获取正确的id


So, to the question, if, in 'normal' C# code you were to instantiate a new instance of RestaurantViewModel , what would you expect the type of BaseFoodObject to be?因此,对于这个问题,如果在“普通”C# 代码中您要实例化RestaurantViewModel的新实例,您希望BaseFoodObject 的类型是什么?

This is what the ModelBinder is doing - it's creating a new RestaurantViewModel .这就是 ModelBinder 正在做的 - 它正在创建一个新的RestaurantViewModel

As your post action method's signature does not include anything to do with Bread - all the bread properties are ignored.由于您的 post action 方法的签名不包含与Bread任何内容 - 所有面包属性都将被忽略。

Some options:一些选项:


Check for the food properties after binding and read them manually (probably the quickest+easiest but not very "mvc-ish")绑定后检查食物属性并手动读取它们(可能是最快+最简单但不是很“mvc-ish”)

public ActionResult Post(RestaurantViewModel viewModelPost) 
{
    if (!string.IsNullOrEmpty(Request.Form["Unit"]))
        // it's a bread form

to make this easier, you could provide a hidden field with the type为了使这更容易,您可以提供一个带有类型的隐藏字段

    if (Request.Form["Type"] == typeof(Bread).Name) 
    {
        var bread = new Bread { Unit = Request.Form["Unit"] }

Add bread to the action so it's bound将面包添加到动作中,使其绑定

public ActionResult Post(RestaurantViewModel viewModelPost, Bread bread)

but then, obviously, it won't work for milk.但是,显然,它不适用于牛奶。

So could extend this using an ActionNameSelector to select the correct action所以可以使用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 but not a solution to this specific case) 相关链接,但不是此特定案例的解决方案)


Another option might be to change the Restaurant type to a generic, but would require a few more changes (and ideally use of interfaces), and more details (provided here as an idea, rather than a solution)另一种选择可能是将 Restaurant 类型更改为泛型,但需要进行更多更改(最好使用接口)和更多详细信息(此处作为想法而不是解决方案提供)

The basics would be:基本知识是:

public class RestaurantViewModel<T> 
     where T: Food
{
}

public ActionResult Post(RestaurantViewModel<Bread> viewModelPost)

public ActionResult Post(RestaurantViewModel<Milk> viewModelPost)

but I've not confirmed if the default ModelBinder would work in this case.但我还没有确认默认的 ModelBinder 在这种情况下是否可以工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM