45

我继承了一个用 ASP.Net MVC 4 编写的代码库。每个 post 方法都需要一个FormCollection. 除了不得不通过带引号的字符串访问值的烦恼之外,它还会导致一些缺点,例如无法在我的 ViewModel 属性上使用ModelState.IsValid, 或属性之类的东西。[AllowHtml]他们实际上确实为每个视图创建了 ViewModel 类(尽管它们几乎只是实际实体框架模型类的直接包装器),但它们仅用于 GET 方法。

关于 FormCollection 有什么我遗漏的东西可以解释为什么这实际上是一个好主意吗?它似乎只有缺点。我想通过使用 ViewModels 来完成并“修复”它。这需要大量的工作,因为 ViewModel 的属性是接口而不是具体的类,这意味着要么编写自定义绑定器,要么更改 ViewModel。

但也许我在使用 FormCollection 有意义的地方遗漏了什么?

4

11 回答 11

52

是否有充分的理由使用 FormCollection 而不是 ViewModel?

不,我有以下问题。

问题 - 1

如果FormCollection正在使用...这将是不必要的,因为在获取特定索引的条目时,返回Type Cast的值是类型的。这种情况不会出现在 Strongly Typed 的情况下。Primitive TypeSystem.Collections.Specialized.NameValueCollectionStringView-Models

问题 - 2

当您提交表单并转到PostAction Method时,View-Model由于 Action 方法中存在 Parameter,您可以将 Posted Values 发回给您View。否则,再次编写代码以发送回TempData/ViewData/ViewBag

在此处输入图像描述

View-Models 是普通的类,用于将数据绑定到视图

问题 - 3

我们有可以在View Model或中实现的数据注释Custom Validations

在此处输入图像描述

ASP.Net MVC 使用数据注释简化模型验证。数据注释是应用于属性的属性。我们可以通过继承内置的验证属性类来创建自定义验证属性。



问题 - 4

示例您有以下HTML

<input type="text" name="textBox1" value="harsha" customAttr1 = "MyValue" />

问题:我们如何从上面访问 customAttr1 的值,例如从控制器内部

:当一个表单被发布时,只有元素的名称和值被发布回服务器。

替代方案:使用一些 jQuery 获取自定义属性值,并将其与表单值一起发布到操作方法

另一种选择是将您在自定义属性中获得的内容放在隐藏控件中




这就是原因,我总是更喜欢使用View-Models

于 2013-06-19T23:54:37.087 回答
7

我能想到的唯一优点是,如果您想使用在未指定要强类型化的 EF 模型时提供的自动生成的控制器。在这种情况下,您的 Create 和 Edit 操作将使用 FormCollection 对象,因为它是一个可靠的、预先存在的框架工件,可用于此目的。也许以前的开发人员在创建他的控制器时选择了这个选项,并坚持使用它,因为 Visual Studio 必须知道它在做什么 :)

但是,实际上,我绝不会推荐这种几秒钟的领先优势。构建视图模型总是更好,如果仅出于维护目的,我建议考虑朝该方向发展的努力。使用模型绑定和强类型视图和 html 帮助程序,您更有可能减少运行时错误的数量,因为您更改了一些魔术字符串并且直到您的页面爆炸时才意识到它。

于 2013-06-18T15:57:43.187 回答
6

好的,我看到这里的普遍共识是它不受欢迎。为了提供另一个视角,我一直喜欢在 POST 操作中使用传递给控制器​​的 formcollection。它提供了来自控制器的 TryUpdateModel 方法的使用,该方法将集合映射到您的强类型类。TryUpdateModel 还具有允许您将要允许更新的模型的属性列入白名单的重载。

if (TryUpdateModel(viewModel, new string[] { "Name" }))
{
    //Do something
}

它仍然允许您想要的所有模型绑定,但有助于防止更新我的视图模型上的“名称”属性以外的任何内容。

您可以在此处查看有关 TryUpdateModel 方法的更多信息:

http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.tryupdatemodel(v=vs.108).aspx

于 2013-06-20T20:47:38.310 回答
3

总有一些解决方法可以摆脱 FormCollection 哈哈。您可以将隐藏字段绑定到表单中的视图模型变量,以满足您的内心需求。

表单集合主要源于创建视图模型的懒惰,但最终仍然需要花时间试图弄清楚如何在控制器中从中获取值:P

我认为它是在 MVC 的一开始就创建的,作为在具有非常简单的表单时使用强类型视图的替代方案 - 回到每个人都使用 ViewBag 的时代 :) ......一旦他们在那里有了它,他们就不能就这么简单把它拿出来。

如果您绝对确定您的视图永远不会有多个表单输入,也许您可​​以使用它?不过可能还是个坏主意..

我找不到任何最近的文章谈论表单集合的任何优点......而强类型视图无处不在。

于 2013-06-20T14:24:40.577 回答
3

是的。有时,它可能很有用。这是一个例子:

假设我们的数据库中有“ date_and_time_field”。

在 Razor View 中,我们想要使用两个表单域。第一个“日期”(可能使用 jQuery UI Datepicker)。第二个“小时”。

在 Controller Action 中,我们通过和组成“date_and_time_field” 。Request.Form["Date"]Request.Form["Hour"]

还有其他一些有用的场景:

  • 交叉表(在 Razor 视图中带有复选框)

  • 集合Request.Unvalidated().Form(也许这不是你问题的一部分:我不想跑题)

于 2013-06-20T15:56:46.083 回答
3

默认模型绑定器几乎可以完成您需要它做的所有事情。我只使用了FormCollection一次,后来才弄清楚如何将元素数组绑定到ViewModel上的集合中。

就去ViewModel。由于列举的各种原因,周围都更好。

于 2013-06-20T23:58:14.213 回答
3

有了form collection您将能够获取表单内的所有值。在某些情况下,您可能需要从表单中传递一些可能不属于您的view model.

以从表单中传递 10 个隐藏值为例。表单集合很有意义。

您可能面临的唯一困难是类型转换。您获得的所有表单集合项都将是字符串;您可能需要type cast根据您的要求。

模型状态验证也是您可能面临挑战的另一个领域。

于 2013-06-24T08:03:33.100 回答
2

使用Forms Collection,您将找到一种快速获取表单值的方法。否则,您必须创建一个模仿表单字段的类,人们有时会懒于为不太重要/很少使用的表单创建自定义类。

不,与自定义类作为操作参数相比,表单集合没有额外的好处(实际上是有限的),应尽可能避免。

于 2013-06-18T10:27:43.110 回答
2

您始终可以将表单集合属性添加到您的方法签名中。它们将自动由具有相应键的表单值填充。

于 2013-06-20T08:27:29.900 回答
2

回答标题问题:是的。

有一些情况FormCollection需要使用。例如,假设 aViewModel具有实现 1 到 N 关系的属性(在具体情况下,a TimesheetViewModelwith ICollection<TimesheetEntryViewModel>),并且控制器必须在时间条目之间执行验证,以免在条目的结束时间之间发生时间冲突以及以下条目的开始时间。要将相关条目标记为验证错误,如何检索行索引?

好吧,使用默认模型绑定,索引值在 Controller 逻辑中丢失。幸运的是,FormController存储了您在视图中使用的索引,并且可以进行更具体的验证。

于 2015-12-27T22:59:18.800 回答
0

有些类型的 SPA 应用程序您不知道您的模型(根本没有 ViewModel 并且视图是动态创建的(简称;))),因此 FormCollection 是您实现具有整个页面输入的自定义后期验证的唯一选择值...如果您的视图了解模型,那么当然,您可以使用具体的 ViewModel 对象。这很容易 ;)

于 2020-08-28T06:34:12.123 回答