ASP.net MVC – 用于POST操作的单独ViewModel

在我的MVC应用程序中,我有一个看起来与此类似的View Model:

public class ComplexViewModel { public ComplexDetailsViewModel Details1 { get; set; } public ComplexDetailsViewModel Details2 { get; set; } } public class ComplexDetailsViewModel { public int Id { get; set; } public string DisplayValue1 { get; set; } public string DisplayValue2 { get; set; } // ... } 

我原本在以下视图中执行以下操作:

 @Html.HiddenFor(model => model.Details1.Id) @Html.HiddenFor(model => model.Details2.Id) @Html.DisplayFor(model => model.Details1.DisplayValue1) ... 

我会将完整的模型发布到控制器:

  public ActionResult Post(ComplexViewModel model) 

除了Id值之外,我实际上并不需要ComplexViewModel中的任何内容,因此我决定创建另一个专门用于POST数据的视图模型:

 public class PostViewModel { public int Details1Id { get; set; } public int Details2Id { get; set; } } public ActionResult Post(PostViewModel model) 

问题是现在我的@HiddenFor(model => model.Details1.Id)没有映射到我的POST模型,因此实际上没有任何内容被POST。

有没有办法为我的POST模型和我的GET模型提供单独的结构,同时仍然使用HiddenFor帮助器?

只需手动编写隐藏输入的HTML而不是使用Html Helpers。

   

更新

我做了类似的事情。 我最终在视图中展平了与表单相关的属性。 Automapper使得从其他对象映射到视图变得非常容易,并且可以使层次结构变得扁平化。 这样做,您的新视图可能最终看起来与此类似。

 public class ComplexViewModel { public long Details1Id { get; set; } public string Details1Name { get; set; } public long Details2Id { get; set; } public string Details2Name { get; set; } } 

仅仅因为你不使用POST版本中的所有数据并不意味着你必须制作另一个模型。 为什么不保持简单?

这是它应该如何工作:

您的post详细信息视图应该是针对特定视图模型的强类型。 然后在你的控制器中你有两个名为Post的动作结果,例如,一个用[HTTPPOST]属性修饰,你要发布的动作用[HTTPPOST]属性[HTTPPOST] 。 此外,您的get方法应该使用诸如post id之类的参数,并且post方法应该将模型作为参数。

要正确执行服务器端validation,您可以像这样装饰类属性:

 public class ComplexDetailsViewModel { [Required]//Works for just the Id property public int Id { get; set; } public string DisplayValue1 { get; set; } public string DisplayValue2 { get; set; } // ... } 

现在在你的控制器中你可以使用这个bool: ModelState.IsValid 。 基本上,如果他们关闭了JavaScript并且模型发布时没有Id那么该模型将无效。

这种模式非常强大,可以快速实现客户端和服务器端validation。 当然,客户端validation使用开箱即用的jQuery,因此我们可以轻松扩展validation器。 您甚至可以非常快速地进行AJAXvalidation。 当我构建表单时,在validation时我不会牺牲任何地方。因为它不需要时间来正确地完成它。

要回答您的原始问题:

视图只能强类型化为一个模型。 您无法使用一个模型加载视图,并将其与另一个模型一起发布(据我所知)。 我想如果你想要这样做,你的问题就在于你建立模型的方式。

模型需要更相似。 一个“视图”模型具有类属性,而“后”模型则没有。 如果类属性的名称和属性不匹配,则它们不受模型绑定器的约束。 尝试以下方法:

 public class ComplexViewModel { public ComplexDetailsViewModel Details1 { get; set; } public ComplexDetailsViewModel Details2 { get; set; } } public class ComplexDetailsViewModel : PostDetailsModel { public string DisplayValue1 { get; set; } public string DisplayValue2 { get; set; } } public class PostModel { public PostDetailsModel Details1 { get; set; } public PostDetailsModel Details2 { get; set; } } public class PostDetailsModel { public int Id { get; set; } } 

ViewPost之间不使用相同模型的大问题是validation。 如果要更改validation会发生什么(假设使用客户端和服务器端的内置MVCvalidation)。 现在你必须改变两个模型! 哦等等……不,你不…

 public interface MyValidation { [required] public int id { get; set; } } 

然后只需将这些validation添加到您的类中:( MetadataType告诉MVC用于validation的内容)

 [MetadataType(typeof(MyValidation))] public class ComplexDetailsViewModel .... [MetadataType(typeof(MyValidation))] public class PostDetailsModel .... 

这肯定会奏效,但我强烈建议任何人不要在视图和回发之间使用两种不同的模型。 在这种情况下,我完全没有理由认为它们应该是不同的。

简单:只需让您的视图的视图模型inheritance您的控制器后模型。 然后你的HiddenFor方法有了工作,你只需要确保你期望发布的属性在基类(post模型)中。