【问题标题】:Can you remove the HTML Field Prefix from strongly typed models in MVC 3?您可以从 MVC 3 中的强类型模型中删除 HTML 字段前缀吗?
【发布时间】:2012-02-20 01:25:24
【问题描述】:

我有一个这样的视图模型:

public class EditVM
{
  public Media.Domain.Entities.Movie Movie       { get; set; }
  public IEnumerable<Genre>          Genres      { get; set; }
}

电影是我想要编辑的真实实体。流派只是用来填充下拉列表。当我打电话时,我更愿意这样做:

@Html.TextBoxFor(m => m.Movie.Title)

在我的强类型视图中,输入控件有一个名称 = "Title" 而不是 "Movie.Title"

我不希望通过使用ViewData 等将我的视图拆分为部分视图或丢失我的强类型视图。

有没有办法向视图表达我不希望有Movie. 前缀?我注意到您可以设置:

ViewData.TemplateInfo.HtmlFieldPrefix = "x";

在控制器中,但不幸的是,它似乎只允许添加 additional 前缀。将其设置为 "" 没有任何作用。

有什么解决办法吗?或者,如果我希望保留强类型视图和 lambda,我是否坚持使用在这种情况下实际上并不需要的不幸前缀?

感谢您的帮助。

更新:

这里的控制器操作可能会让事情变得更清晰。

public ActionResult Edit(int? id)
{
  var vm = new EditVM
  {
    Movie  = id.HasValue ? _movieSvc.Find(id.Value) : new Movie(),
    Genres = AppData.ListGenres()     
  };
  return View(vm);
}

[HttpPost]
public void Edit([Bind(Prefix = "Movie")]Movie m)
{
   _movieSvc.AddOrUpdateMovie(m); //Exceptions handled elsewhere
}

【问题讨论】:

    标签: asp.net-mvc asp.net-mvc-3 model-view-controller view


    【解决方案1】:

    不,为了做你想做的事,你必须重写 Html 助手,然后你必须编写自己的模型绑定器。似乎需要做很多工作才能获得微薄的收益。

    唯一的选择是在其中将 Movie 对象作为模型传递的 Partial 视图。但是,这需要您编写自己的模型绑定器才能使其被识别。

    您必须执行 m.Movie.Title 的原因是 ID 具有正确的名称,因此模型绑定程序可以将其识别为模型的成员。

    根据您的更新:

    您的选择是:

    1. 使用非强类型助手。
    2. 使用局部视图。
    3. 重写 stronly 类型的帮助器
    4. 根本不使用帮助器,将值写入 HTML

    就我个人而言,我只会使用 1 或 2,可能是 2。

    编辑:

    根据您上面的更新。将您的代码更改为此(注意,Genres 不会被回发到服务器,因此 m.Genres 在回发时只会为空):

    [HttpPost] 
    public void Edit(EditVM m) 
    { 
       _movieSvc.AddOrUpdateMovie(m.Movie); //Exceptions handled elsewhere 
    } 
    

    编辑:

    我确实只是想到了一个替代方案。你可以这样做:

    @{ var Movie = Model.Movie; }
    @Html.TextBoxFor(m => Movie.Title)
    

    但是,如果出现验证错误,则必须重新创建 EditVM。

    【讨论】:

    • 我认为我不需要编写模型活页夹。我的编辑操作的GET 版本使用EditVM,因为它需要所有这些数据。但是POST 版本只希望接收一个 Movie 对象。如果名称属性只是“标题”等,它会很好地绑定。
    • 因此,您在帖子中使用的模型与在 get 和 view 中使用的模型不同。这可能会令人困惑,以及您遇到问题的原因。这不是“幸福的道路”。你将不得不在某些事情上妥协。查看我的更新。
    • 是的,这是我希望避免的妥协。顺便说一句,我认为您的意思是说它是名称属性,而不是模型绑定器使用的 id。我得到的印象是,就我将如何创建编辑操作而言,我在保留这里做一些事情。我认为我正在做的事情相当普遍。您没有将下拉菜单作为您编辑的实体的一部分吗?你如何以不同的方式处理问题?您通常只为下拉控件创建部分视图吗?您如何填充下拉列表而不让 GET/POST 操作具有差异参数?
    • @C.J. - 我一般使用 ID,而不是专门指 Html ID 字段,但是是的.. 名称更正确。我真的不明白下拉菜单与这个问题有什么关系。为什么不直接将 EditVM 类作为 Post 方法的参数?这是执行此操作的首选方式,因为如果您的模型无法验证,则无论如何您都需要将原始模型再次传回以保持 ModelState 正确。
    • 是的,没错。但我认为该操作期待它不会获得的数据看起来很奇怪。
    【解决方案2】:

    我有一个这样的视图模型

    我认为您可能对什么是视图模型有一些误解。视图模型不应包含对您的域模型的任何引用,这就是 MovieGenre 类的样子。我的意思是创建一个以VM 为后缀的新类,并且在其中将所有域模型作为属性填充,这并不是真正的视图模型。视图模型是专门为满足您的视图要求而设计的类。

    更正确的视图模型如下所示:

    public class EditVM
    {
        public string MovieTitle { get; set; }
        public IEnumerable<GenreViewModel> Genres { get; set; }
    }
    

    在你看来,你会:

    @Html.EditorFor(x => x.MovieTitle)
    @Html.EditorFor(x => x.Genres)
    

    【讨论】:

    • 我不确定我是否同意您对视图模型的看法。也许我使用这个术语比你更宽松,但我选择将视图所需的数据称为“视图模型”。不过,无论您选择如何命名它,如果可以从我的域中满足该需求的现有类形成一个类,我不认为使用它们是“不正确的”。
    • @C.J. - 在您的视图中使用它们并不是“不正确的”(好吧,不建议这样做,但您当然可以这样做),将它们称为视图模型是“不正确的”。 Darin 是绝对正确的,View Model 是为视图定制的模型,而不是简单地将你的 Domain 模型打包在一起。充其量,我将其称为伪视图模型。没有真正的同意或不同意,视图模型的定义非常明确。
    • 您认为 Microsoft ASP.NET 团队的高级项目经理 Steven Walther 在这篇文章中是否也有错误? stephenwalther.com/blog/archive/2009/04/13/…
    • @C.J. - 我不明白参考文章与您认为使用域模型有什么关系。事实上,在那篇文章中没有任何内容引用域模型。事实上,他的模型显然是他的 UI 模型,而不是他的领域模型。如果它们是领域模型,它们将存在于不同的层或项目中。
    • 那些类是持久化的类,所以我认为它们是域,尽管解决方案没有将它们分割到一个单独的项目中。
    【解决方案3】:

    另一种选择是使用TextBox(string name, object value) 重载而不是TextBoxFor

    @Html.TextBox("Title", Model.Movie.Title)
    

    您也可以指定输入标签 HTML 而不是使用帮助器。

    另一种选择是将 EditVM 作为回发参数。这就是我会做的。我的 post action 参数始终是 .cshtml 模型的同一类型。是的,会有一些属性,比如为空的列表,但你只是忽略了这些。它还允许您优雅地处理发布错误,因为如果出现错误,您无论如何都需要返回该视图模型的实例,并包含他们提交的值。我通常有私有方法或 DB 层来处理检索进入 ViewModel 的各种列表,因为这些列表在回发时将是空的并且需要重新填充,同时不会触及帖子中的属性。

    使用您现在的 post 方法,如果您需要返回相同的视图,您必须创建一个新的 EditVM,然后将所有已发布的值复制到其中,并仍然填充列表。使用我的方法,您可以消除其中一个映射步骤。如果您发布的内容不止一件,您的发布操作是否会有无数不同的参数?让它们自然而然地变成一个输入到视图的 EditVM 的参数。虽然在回发期间在 VM 中拥有这些空属性可能会让人感到恶心,但您会在 View 和回发 IMO 之间获得很好的可预测一致性。您不必花费大量时间考虑您的 post 方法中的哪些参数组合可以从表单中获取所有数据。

    【讨论】:

      猜你喜欢
      • 2014-12-20
      • 2011-04-11
      • 2023-03-29
      • 1970-01-01
      • 2014-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-22
      相关资源
      最近更新 更多