表单验证不断要求在 MVC 上提供所需的 ID
Validation on Form keeps asking for a required ID on MVC
我正在尝试验证表单。出于某种原因,当我提交表单时,表单验证显示 "The ID field is required." 然而,尽管它这么说,但我有一个 HiddenFor 输入应该处理它...
这是我的观点UserReportForm.cshtml(为了便于阅读而缩短):
@model BugTracker.ViewModels.UserReportViewModel
@{
ViewBag.Title = "Issue Form";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Report Bug/Request Change</h1>
@Html.ValidationSummary(false, "Please fix the following errors.")
@using (Html.BeginForm("Save", "Report", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="form-group">
<label>Are you reporting a bug, or requesting a change to a page?</label>
@Html.DropDownListFor(m => m.Report.RequestTypeID, new SelectList(Model.RequestType, "ID", "RequestBC"), "Select Issue", new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Report.RequestTypeID)
</div>
<div class="form-group">
<label>Name of Issue</label>
@Html.TextBoxFor(m => m.Report.Name, new { @class = "form-control"})
@Html.ValidationMessageFor(m => m.Report.Name)
</div>
<div class="form-group">
<label>Detailed Description of Issue</label>
@Html.TextAreaFor(m => m.Report.Description, new { @class = "form-control", @rows = "10"})
@Html.ValidationMessageFor(m => m.Report.Description)
</div>
@Html.HiddenFor(m => m.Report.ID)
<button type="submit" class="btn btn-primary">Save</button>
}
这是视图模型UserReportViewModel.c:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using BugTracker.Models;
using System.Web.Mvc;
namespace BugTracker.ViewModels
{
public class UserReportViewModel
{
public Report Report { get; set; }
public IEnumerable<RequestType> RequestType { get; set; }
}
}
和Report.cs模型:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace BugTracker.Models
{
public class Report
{
//For User
public int ID { get; set; }
[Required]
public String Name { get; set; }
[Required]
public String Description { get; set; }
public RequestType RequestType { get; set; }
[Display (Name="Request Type")]
public byte RequestTypeID { get; set; }
}
}
在我添加验证之前,这些表单在数据库中获取 ID 没有问题(每次递增 1)。为什么这是关于 ID 的抱怨,我该怎么做才能阻止它说它是必需的?
编辑:我不认为这与它有多大关系,但经过进一步审查,我认为我的控制器可能导致了这个问题:
新建表格
public ActionResult UserReportForm()
{
var requestType = _context.RequestType.ToList();
var viewModel = new UserReportViewModel
{
RequestType = requestType,
};
return View(viewModel);
}
保存表格
[HttpPost]
public ActionResult Save(UserReportViewModel viewModel)
{
if (!ModelState.IsValid)
{
var viewM = new UserReportViewModel
{
RequestType = _context.RequestType.ToList(),
};
return View("UserReportForm", viewM);
}
if (viewModel.Report.ID == 0)
{
_context.Reports.Add(viewModel.Report);
}
else
{
var reportInDb = _context.Reports.Single(c => c.ID == viewModel.Report.ID);
reportInDb.Name = viewModel.Report.Name;
reportInDb.Description = viewModel.Report.Description;
reportInDb.RequestTypeID = viewModel.Report.RequestTypeID;
}
_context.SaveChanges();
return RedirectToAction("Report", "Report", new { stat = 1 });
}
请更新模型并重建项目
public class Report
{
//For User-> here add the [Key]
[Key]
public int ID { get; set; }
[Required]
public String Name { get; set; }
[Required]
public String Description { get; set; }
public RequestType RequestType { get; set; }
[Display (Name="Request Type")]
public byte RequestTypeID { get; set; }
}
如果 ID
列是自动生成的主键(即标识列),您应该用 KeyAttribute
和 DatabaseGeneratedAttribute
标记它,如下例:
public class Report
{
// add 2 attributes below
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Required]
public String Name { get; set; }
[Required]
public String Description { get; set; }
public RequestType RequestType { get; set; }
[Display(Name="Request Type")]
public byte RequestTypeID { get; set; }
}
通过使用这些属性,无需在 ID
属性 上使用 RequiredAttribute
。
关于 DropDownListFor
的第二个问题,在尝试 return View(viewModel)
时出现问题,因为 UserReportViewModel.RequestType
在表单提交后仍未分配,导致 UserReportViewModel.RequestType
collection contains null值并在构建 SelectList
时抛出 ArgumentNullException
。
只需将 List<RequestType>
分配给现有的视图模型以防验证错误就足够了:
[HttpPost]
public ActionResult Save(UserReportViewModel viewModel)
{
if (!ModelState.IsValid)
{
viewModel.RequestType = _context.RequestType.ToList(); // use this line instead
return View("UserReportForm", viewModel); // change to return existing viewmodel
}
// other stuff - skipped for brevity
return RedirectToAction("Report", "Report", new { stat = 1 });
}
感谢您的回复,SAR 和 Tetsuya Yamamoto - 我感谢您的建议!在做了更多研究之后,我能够解决我的问题。我发现的解决方案是我如何使用视图模型。
首先,@Html.ValidationSummary 似乎会检查每个字段以确定是否需要模型中定义的信息。在我的例子中,我的报告模型 中定义的 ID 是 所必需的。鉴于这种情况,我可以通过简单地从我的视图中删除这一行来解决我的问题:
@Html.HiddenFor(m => m.Report.ID)
但是,我想将此视图用于创建表单和编辑表单。在那种情况下,我们需要让这个 ID 可以为空。解决方案不是编辑报表模型,而是编辑视图模型 UserReportViewModel.cs。我打开这个文件,并添加了这行代码:
public int? Id {get; set;}
然后,我回到我的视图并将其更改为:
@Html.HiddenFor(m => m.Id)
快完成了。回到控制器中,我需要稍微调整一下。我的新控制器中没有任何变化,但在保存控制器中,我将使用 viewModel.Report.ID 的实例更改为 viewModel.Id。我还在 "if (!ModelState.Valid) branch" 中添加了一行代码。更新后的代码如下所示:
[HttpPost]
public ActionResult Save(UserReportViewModel viewModel)
{
if (!ModelState.IsValid)
{
var viewM = new UserReportViewModel
{
RequestType = _context.RequestType.ToList(),
};
viewM.Id = viewModel.Report.ID;
return View("UserReportForm", viewM);
}
if (viewModel.Id == 0)
{
_context.Reports.Add(viewModel.Report);
}
else
{
var reportInDb = _context.Reports.Single(c => c.ID == viewModel.Id);
reportInDb.Name = viewModel.Report.Name;
reportInDb.Description = viewModel.Report.Description;
reportInDb.RequestTypeID = viewModel.Report.RequestTypeID;
}
_context.SaveChanges();
return RedirectToAction("Report", "Report", new { stat = 1 });
}
进行这些更改后,我能够通过验证成功创建和编辑我的表单!总之,解决此问题的方法是在视图模型中使用可为空的 ID,而不是特定模型中所需的 ID。
我正在尝试验证表单。出于某种原因,当我提交表单时,表单验证显示 "The ID field is required." 然而,尽管它这么说,但我有一个 HiddenFor 输入应该处理它...
这是我的观点UserReportForm.cshtml(为了便于阅读而缩短):
@model BugTracker.ViewModels.UserReportViewModel
@{
ViewBag.Title = "Issue Form";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Report Bug/Request Change</h1>
@Html.ValidationSummary(false, "Please fix the following errors.")
@using (Html.BeginForm("Save", "Report", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="form-group">
<label>Are you reporting a bug, or requesting a change to a page?</label>
@Html.DropDownListFor(m => m.Report.RequestTypeID, new SelectList(Model.RequestType, "ID", "RequestBC"), "Select Issue", new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Report.RequestTypeID)
</div>
<div class="form-group">
<label>Name of Issue</label>
@Html.TextBoxFor(m => m.Report.Name, new { @class = "form-control"})
@Html.ValidationMessageFor(m => m.Report.Name)
</div>
<div class="form-group">
<label>Detailed Description of Issue</label>
@Html.TextAreaFor(m => m.Report.Description, new { @class = "form-control", @rows = "10"})
@Html.ValidationMessageFor(m => m.Report.Description)
</div>
@Html.HiddenFor(m => m.Report.ID)
<button type="submit" class="btn btn-primary">Save</button>
}
这是视图模型UserReportViewModel.c:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using BugTracker.Models;
using System.Web.Mvc;
namespace BugTracker.ViewModels
{
public class UserReportViewModel
{
public Report Report { get; set; }
public IEnumerable<RequestType> RequestType { get; set; }
}
}
和Report.cs模型:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace BugTracker.Models
{
public class Report
{
//For User
public int ID { get; set; }
[Required]
public String Name { get; set; }
[Required]
public String Description { get; set; }
public RequestType RequestType { get; set; }
[Display (Name="Request Type")]
public byte RequestTypeID { get; set; }
}
}
在我添加验证之前,这些表单在数据库中获取 ID 没有问题(每次递增 1)。为什么这是关于 ID 的抱怨,我该怎么做才能阻止它说它是必需的?
编辑:我不认为这与它有多大关系,但经过进一步审查,我认为我的控制器可能导致了这个问题:
新建表格
public ActionResult UserReportForm()
{
var requestType = _context.RequestType.ToList();
var viewModel = new UserReportViewModel
{
RequestType = requestType,
};
return View(viewModel);
}
保存表格
[HttpPost]
public ActionResult Save(UserReportViewModel viewModel)
{
if (!ModelState.IsValid)
{
var viewM = new UserReportViewModel
{
RequestType = _context.RequestType.ToList(),
};
return View("UserReportForm", viewM);
}
if (viewModel.Report.ID == 0)
{
_context.Reports.Add(viewModel.Report);
}
else
{
var reportInDb = _context.Reports.Single(c => c.ID == viewModel.Report.ID);
reportInDb.Name = viewModel.Report.Name;
reportInDb.Description = viewModel.Report.Description;
reportInDb.RequestTypeID = viewModel.Report.RequestTypeID;
}
_context.SaveChanges();
return RedirectToAction("Report", "Report", new { stat = 1 });
}
请更新模型并重建项目
public class Report
{
//For User-> here add the [Key]
[Key]
public int ID { get; set; }
[Required]
public String Name { get; set; }
[Required]
public String Description { get; set; }
public RequestType RequestType { get; set; }
[Display (Name="Request Type")]
public byte RequestTypeID { get; set; }
}
如果 ID
列是自动生成的主键(即标识列),您应该用 KeyAttribute
和 DatabaseGeneratedAttribute
标记它,如下例:
public class Report
{
// add 2 attributes below
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Required]
public String Name { get; set; }
[Required]
public String Description { get; set; }
public RequestType RequestType { get; set; }
[Display(Name="Request Type")]
public byte RequestTypeID { get; set; }
}
通过使用这些属性,无需在 ID
属性 上使用 RequiredAttribute
。
关于 DropDownListFor
的第二个问题,在尝试 return View(viewModel)
时出现问题,因为 UserReportViewModel.RequestType
在表单提交后仍未分配,导致 UserReportViewModel.RequestType
collection contains null值并在构建 SelectList
时抛出 ArgumentNullException
。
只需将 List<RequestType>
分配给现有的视图模型以防验证错误就足够了:
[HttpPost]
public ActionResult Save(UserReportViewModel viewModel)
{
if (!ModelState.IsValid)
{
viewModel.RequestType = _context.RequestType.ToList(); // use this line instead
return View("UserReportForm", viewModel); // change to return existing viewmodel
}
// other stuff - skipped for brevity
return RedirectToAction("Report", "Report", new { stat = 1 });
}
感谢您的回复,SAR 和 Tetsuya Yamamoto - 我感谢您的建议!在做了更多研究之后,我能够解决我的问题。我发现的解决方案是我如何使用视图模型。
首先,@Html.ValidationSummary 似乎会检查每个字段以确定是否需要模型中定义的信息。在我的例子中,我的报告模型 中定义的 ID 是 所必需的。鉴于这种情况,我可以通过简单地从我的视图中删除这一行来解决我的问题:
@Html.HiddenFor(m => m.Report.ID)
但是,我想将此视图用于创建表单和编辑表单。在那种情况下,我们需要让这个 ID 可以为空。解决方案不是编辑报表模型,而是编辑视图模型 UserReportViewModel.cs。我打开这个文件,并添加了这行代码:
public int? Id {get; set;}
然后,我回到我的视图并将其更改为:
@Html.HiddenFor(m => m.Id)
快完成了。回到控制器中,我需要稍微调整一下。我的新控制器中没有任何变化,但在保存控制器中,我将使用 viewModel.Report.ID 的实例更改为 viewModel.Id。我还在 "if (!ModelState.Valid) branch" 中添加了一行代码。更新后的代码如下所示:
[HttpPost]
public ActionResult Save(UserReportViewModel viewModel)
{
if (!ModelState.IsValid)
{
var viewM = new UserReportViewModel
{
RequestType = _context.RequestType.ToList(),
};
viewM.Id = viewModel.Report.ID;
return View("UserReportForm", viewM);
}
if (viewModel.Id == 0)
{
_context.Reports.Add(viewModel.Report);
}
else
{
var reportInDb = _context.Reports.Single(c => c.ID == viewModel.Id);
reportInDb.Name = viewModel.Report.Name;
reportInDb.Description = viewModel.Report.Description;
reportInDb.RequestTypeID = viewModel.Report.RequestTypeID;
}
_context.SaveChanges();
return RedirectToAction("Report", "Report", new { stat = 1 });
}
进行这些更改后,我能够通过验证成功创建和编辑我的表单!总之,解决此问题的方法是在视图模型中使用可为空的 ID,而不是特定模型中所需的 ID。