我继承了用ASP.Net MVC 4编写的代码库。每个post方法都需要FormCollection
。除了必须通过带引号的字符串访问值的烦恼之外,它还会导致诸如无法在我的ViewModel属性上使用ModelState.IsValid
或[AllowHtml]
属性之类的缺点。他们实际上为每个视图创建了ViewModel类(虽然它们几乎只是实际的Entity Framework Model类的直接包装器),但它们仅用于GET方法。
我对FormCollection有什么遗漏,这说明为什么这可能是一个好主意?它似乎只有缺点。我想通过使用ViewModels来“修复”它。这将需要很多工作,因为ViewModel具有接口而不是具体类的属性,这意味着要么编写自定义绑定器,要么更改ViewModel。
但也许在我使用FormCollection有意义的地方缺少一些东西?
答案 0 :(得分:44)
有没有充分的理由使用FormCollection而不是ViewModel?
没有。我有以下问题。
问题 - 1
如果正在使用FormCollection
...必须Type Cast
Primitive Type
值必不可少,因为在获取{{1}的特定索引条目时},返回的值是System.Collections.Specialized.NameValueCollection
类型。在强类型String
。
问题 - 2
当您提交表单并转到View-Models
操作方法,并且Post
作为参数存在于操作方法中时,您可以将发布的值发送给您View-Model
。否则,再次编写代码以通过View
View-Models是普通类,用于将数据绑定到Views
问题 - 3
我们有可以在TempData/ViewData/ViewBag
或View Model
中实施的数据注释。
ASP.Net MVC使用Data Annotation简化了模型验证。数据注释是在属性上应用thyat的属性。我们可以通过继承内置的Validation Attribute类来创建自定义验证属性。
问题 - 4
示例您有以下 HTML
Custom Validations
问题 :我们如何从上面例如从控制器内部访问customAttr1的值
回答:发布表单时,只会将元素的名称和值发回服务器。
替代方案:使用一些jQuery获取自定义属性值,并将其与表单值一起发布到操作方法
另一个选择是将您在自定义属性中获得的内容放入隐藏控件中
这就是原因,我总是喜欢使用<input type="text" name="textBox1" value="harsha" customAttr1 = "MyValue" />
答案 1 :(得分:7)
我能想到的唯一优势是,如果您想要使用自动生成的控制器,而不指定要强类型化的EF模型。在这种情况下,您的创建和编辑操作将使用FormCollection对象,因为它是一个可靠的,预先存在的框架工件,可用于此目的。也许之前的开发人员在创建控制器时选择了这个选项,并坚持使用它,因为Visual Studio必须知道它在做什么:)
但是,实际上,我绝不会推荐几秒钟的这个开端。构建视图模型总是更好,我建议只考虑维护目的,努力向这个方向移动。使用模型绑定和强类型视图以及html帮助程序,您更有可能减少因更改某些魔术字符串而在页面爆炸之前无法实现的运行时错误数。
答案 2 :(得分:6)
好的,我看到这里的普遍共识是不喜欢它。为了提供另一个视角,我总是喜欢在POST操作中使用传递给控制器的formcollection。它提供了控制器中TryUpdateModel方法的使用,该方法将集合映射到强类型类。 TryUpdateModel还具有重载功能,允许您列出要允许更新的模型属性。
if (TryUpdateModel(viewModel, new string[] { "Name" }))
{
//Do something
}
它仍然允许您想要的所有模型绑定,但有助于保持我的viewmodel上的“Name”属性以外的任何内容不被更新。
您可以在此处查看有关TryUpdateModel方法的更多信息:
http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.tryupdatemodel(v=vs.108).aspx
答案 3 :(得分:3)
总是有一些解决方法可以远离FormCollection大声笑..你可以将隐藏的字段绑定到表单中你的视图模型变量到你内心的内容。
表单集合主要来自于创建视图模型的懒惰,但最终还是花时间试图弄清楚如何从控制器中获取值:P
我认为它只是在MVC的最初创建,作为在使用非常简单的表单时使用强类型视图的替代方法 - 在每个人都使用ViewBag的时候:) ...并且一旦嘿它们在那里他们不能简单地把它拿出来。
如果您绝对确定您的视图永远不会有多个表单输入,也许您可以使用它?可能仍然是一个坏主意..
我找不到最近的文章谈论表单集合的任何优点......而强类型视图无处不在。
答案 4 :(得分:3)
是。有时,它可能是有用的。这是一个例子:
假设我们的数据库中包含“date_and_time_field
”。
在Razor View中,我们想要使用两个表单字段。第一个“日期”(可能与jQuery UI Datepicker)。第二个“小时”。
在Controller Action中,我们通过Request.Form["Date"]
和Request.Form["Hour"]
撰写“date_and_time_field”。
还有其他可能有用的场景:
一个交叉表(在Razor视图中使用checkBoxes)
收集Request.Unvalidated().Form
(也许这不是你问题的一部分:我不想偏离主题)
答案 5 :(得分:3)
默认的模型绑定器几乎可以完成您需要执行的所有操作。我使用FormCollection
一次 - 后来才弄清楚如何将元素数组绑定到 ViewModel 上的集合中。
只需转到 ViewModel 即可。出于各种原因列举的所有内容都更好。
答案 6 :(得分:3)
使用form collection
,您将能够获取表单中的所有值。在某些情况下,您可能需要从表单中传递一些可能不属于view model
的其他值。
举一个例子,从表单中传递10个隐藏值。表单集合很有意义。
你可能遇到的唯一困难是打字。您获得的所有表单集合项都将是字符串;根据您的要求,您可能需要type cast
。
模型状态验证也是您可能面临挑战的另一个领域。
答案 7 :(得分:2)
使用 Forms Collection ,您将找到一种快速获取表单值的方法。否则你必须创建一个模仿表单字段的类,有时人们懒得为不太重要/很少使用的表单创建自定义类。
表单集合上的表单集合没有额外的好处(实际上是有限的)作为操作参数,应该尽可能避免。
答案 8 :(得分:2)
您始终可以将表单集属性添加到方法签名中。它们将自动由具有相应键的表单值填充。
答案 9 :(得分:2)
回答标题问题:是的。
在某些情况下需要使用<template name="RecipeSingle">
<h1>{{recipe.name}}</h1>
<p>{{recipe.desc}}</p>
<p>{{recipe.ingredients}}</p>
</template>
。例如,假设FormCollection
具有实现1到N关系的属性(具体情况下,ViewModel
具有TimesheetViewModel
),并且Controller必须在时间条目不会在条目的结束时间和后续条目的开始时间之间产生时间冲突。要使用验证错误标记相关条目,如何检索行索引?
好吧,使用默认的模型绑定,索引值在Controller逻辑中丢失。幸运的是,ICollection<TimesheetEntryViewModel>
存储了您在 View 中使用的索引,可以进行更具体的验证。
答案 10 :(得分:0)
在某些SPA应用程序中,您对模型一无所知(根本没有ViewModel,并且视图是动态创建的(简称;))),因此,在实现具有以下内容的自定义验证后,FormCollection是您的唯一选择整页输入值... 如果您的视图了解模型,那么您当然可以使用具体的ViewModel对象。那很容易;)