【问题标题】:ASP.net MVC - Separate ViewModel for POST ActionASP.net MVC - 用于 POST 操作的单独 ViewModel
【发布时间】:2011-12-14 22:43:06
【问题描述】:

在我的 MVC 应用程序中,我有一个类似于此的视图模型:

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)

我实际上不需要 ComplexViewModel 中的任何东西,除了 Id 值,所以我决定创建另一个专门用于发布数据的视图模型:

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 帮助器?

【问题讨论】:

    标签: c# asp.net-mvc viewmodel


    【解决方案1】:

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

    它应该是这样工作的:

    您的帖子详细信息视图应针对特定视图模型进行强类型化。然后在您的控制器中,您有两个名为 Post 的操作结果,例如,一个用 [HTTPGET] 属性装饰,您要发布到的操作用 [HTTPPOST] 属性装饰。此外,您的 get 方法应采用 post id 等参数,而您的 post 方法应采用模型作为参数。

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

    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; }
        // ...
    }
    

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

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

    回答你原来的问题:

    视图只能强类型化到一个模型。您不能使用一个模型加载视图,然后将其与另一个模型一起发布(据我所知)。我认为,如果您尝试这样做,则问题出在您构建模型的方式上。

    【讨论】:

    • 我喜欢 POST 模型的简单性,因为它使我的验证器更易于使用。我没有复杂的模型,我只有两个可以轻松使用的整数。
    • @Dismissile 我将更新我的答案,为您提供一个示例,说明如何让您的验证器仅使用所需的 id。
    • 我让验证器使用我编写它的原始方式,但我不喜欢它们。我重写了模型以使其更清洁。我对模型的方式很满意,我只是想知道是否可以使用帮助器,以便他们可以“映射”到不同的模型属性。
    • 我没有使用 DataAnnotations。我正在使用 FluentValidation,但我不喜欢具有复杂属性时的验证方式。
    • asp.net mvc 在绑定时不关心模型是什么类,它只关心模型属性名称和表单上输入元素的名称。使它们在两个模型上都相同,没有问题。如果您为视图展平复杂对象,则很容易做到这一点。我经常这样做是为了使用 ajax 更新部分页面。如果您只需要其中的一部分,则无需发布完整的表单数据。
    【解决方案2】:

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

    <input type="hidden" id="Details1Id" value="@Model.Details1.Id"/>
    <input type="hidden" id="Details2Id" value="@Model.Details1.Id"/>
    

    更新

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

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

    【讨论】:

    • 我知道我可以做到这一点,但我希望有办法仍然使用帮助程序。不过,情况似乎并非如此。
    • 你可以使用@Html.Hidden 而不是@Html.HiddenFor() 来完成这个,对吧?
    • 是的,但我希望继续使用 HiddenFor,因为它会自动为嵌套模板添加前缀。
    【解决方案3】:

    模型需要更加相似。您的一个“视图”模型具有类属性,而您的“发布”模型没有。如果类属性的名称和属性不匹配,则它们不会被模型绑定器绑定。尝试类似:

    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 之间不使用相同模型的最大问题是验证。如果您想更改验证会发生什么(假设对客户端和服务器端使用内置的 MVC 验证)。现在你必须改变这两个模型!哦等等……不,你没有……

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

    然后只需将这些验证添加到您的类中: (MetadataType 告诉 MVC 使用什么进行验证)

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

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

    【讨论】:

      【解决方案4】:

      简单:只需让您的视图的视图模型从您的控制器后模型继承。然后您的 HiddenFor 方法可以正常工作,您只需要确保您期望发布的属性位于基类(发布模型)中。

      【讨论】:

        猜你喜欢
        • 2017-01-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多