继承模型MVC的编辑器

本文关键字:编辑器 MVC 模型 继承 | 更新日期: 2023-09-27 18:21:52

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

RestaurantViewModel.cs

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

Food.cs

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

面包制品——从食品继承

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 ObjectUnit属性不会与RestaurantViewModel一起传递。原因是RestaurantViewModel使用了Food对象,而不是Bread

我希望可以修改EditorFor,并告诉它使用View中的Model或我显示它时传入的Object Type。

感谢

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

继承模型MVC的编辑器

MVC是无状态的。一些参考资料。

在你的问题中有几个语句与此相冲突,以及MVC绑定是如何工作的,例如:

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

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

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

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

通过getaction+view.cshtml+模型呈现的最终html没有链接到postaction。你可以很容易地获取渲染后的html,保存它,重新启动你的电脑,重新加载渲染后的html,它将以完全相同的方式工作。

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

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


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

这就是ModelBinder正在做的事情——它正在创建一个新的RestaurantViewModel

由于您的post-action方法的签名不包含任何与Bread有关的内容,因此将忽略所有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

(相关链接,但不是该特定案例的解决方案)


另一种选择可能是将Restaurant类型更改为泛型,但需要更多的更改(理想情况下使用接口)和更多的细节(此处提供的是一个想法,而不是解决方案)

基础是:

public class RestaurantViewModel<T> 
     where T: Food
{
}
public ActionResult Post(RestaurantViewModel<Bread> viewModelPost)
public ActionResult Post(RestaurantViewModel<Milk> viewModelPost)

但我还没有确认默认的ModelBinder是否能在这种情况下工作。

这篇文章出现了问题。发布后,您所拥有的只是一组发布的数据和一个类型为RestaurantViewModel的参数。modelbinder在Food上设置所有适当的字段,因为它只知道这些。其他的东西都被丢弃了。对此我们无能为力。如果需要发布与Bread相关的字段,则属性的类型必须为Bread。这是唯一可行的方法。