当表单字段留空时,NerdDinner 表单验证 MVC2 中的 DataAnnotations 错误
平台:Windows 7 Ultimate
IDE:Visual Studio 2010 Ultimate
Web 环境: ASP.NET MVC 2
数据库: SQL Server 2008 R2 Express
数据访问:实体框架 4
表单验证:数据注释
示例应用:来自 Wrox Pro ASP.NET MVC 2 的 NerdDinner
书籍: Wrox Professional MVC 2
第 1 章 - 部分的问题: “将验证和业务规则逻辑与模型类集成”(第 33 至 35 页)
错误概要: NerdDinner 表单数据注释和数据库空值验证错误。
当数据库字段设置为不允许空值时,示例代码中的 DataAnnotations 不起作用。
书中的代码和从 codeplex 下载的示例代码出现错误。
救命!我对此真的很沮丧!我不敢相信这么简单的东西竟然不起作用???
重现错误的步骤:
- 将数据库字段设置为不允许 NULL (见图)
- 设置 NerdDinnerEntityModel 晚餐类字段' 可空属性为 false (参见图片)
- 为 Dining_Validation 类添加 DataAnnotations (代码 A)
- 创建 Dining 存储库类 (代码 B)
- 添加 CREATE 操作DiningController (CODE C)
- 这是发布前的空白表单(参见图片)
- 发布时出现此空错误 空白表格应该是 由Dinner_Validation类拦截 数据注释。 注意错误消息 说“这个财产不能 设置为空值。 WTH??? (见图)
- 编辑过程中出现下一个错误 过程。这是编辑控制器 操作(代码D)
- 这是故意错误的“编辑”形式 输入测试晚餐验证 DataAnnotations (见图)
- 发布时再次出现错误 编辑具有空白表单字段的表单。发帖请求 应该被Dinner_Validation 类DataAnnotations 拦截。相同的 空输入错误。啥??? (参见图片)
查看屏幕截图:
http://www.intermedia4web.com/temp/nerdDinner/StackOverflowNerdDinnerQuestionshort.png
代码 A:
[MetadataType(typeof(Dinner_Validation))]
public partial class Dinner { }
[Bind(Include = "Title, EventDate, Description, Address, Country, ContactPhone, Latitude, Longitude")]
public class Dinner_Validation
{
[Required(ErrorMessage = "Title is required")]
[StringLength(50, ErrorMessage = "Title may not be longer than 50 characters")]
public string Title { get; set; }
[Required(ErrorMessage = "Description is required")]
[StringLength(265, ErrorMessage = "Description must be 256 characters or less")]
public string Description { get; set; }
[Required(ErrorMessage="Event date is required")]
public DateTime EventDate { get; set; }
[Required(ErrorMessage = "Address is required")]
public string Address { get; set; }
[Required(ErrorMessage = "Country is required")]
public string Country { get; set; }
[Required(ErrorMessage = "Contact phone is required")]
public string ContactPhone { get; set; }
[Required(ErrorMessage = "Latitude is required")]
public double Latitude { get; set; }
[Required(ErrorMessage = "Longitude is required")]
public double Longitude { get; set; }
}
代码 B:
public class DinnerRepository
{
private NerdDinnerEntities _NerdDinnerEntity = new NerdDinnerEntities();
// Query Method
public IQueryable<Dinner> FindAllDinners()
{
return _NerdDinnerEntity.Dinners;
}
// Query Method
public IQueryable<Dinner> FindUpcomingDinners()
{
return from dinner in _NerdDinnerEntity.Dinners
where dinner.EventDate > DateTime.Now
orderby dinner.EventDate
select dinner;
}
// Query Method
public Dinner GetDinner(int id)
{
return _NerdDinnerEntity.Dinners.FirstOrDefault(d => d.DinnerID == id);
}
// Insert Method
public void Add(Dinner dinner)
{
_NerdDinnerEntity.Dinners.AddObject(dinner);
}
// Delete Method
public void Delete(Dinner dinner)
{
foreach (var rsvp in dinner.RSVPs)
{
_NerdDinnerEntity.RSVPs.DeleteObject(rsvp);
}
_NerdDinnerEntity.Dinners.DeleteObject(dinner);
}
// Persistence Method
public void Save()
{
_NerdDinnerEntity.SaveChanges();
}
}
代码 C:
// **************************************
// GET: /Dinners/Create/
// **************************************
public ActionResult Create()
{
Dinner dinner = new Dinner() { EventDate = DateTime.Now.AddDays(7) };
return View(dinner);
}
// **************************************
// POST: /Dinners/Create/
// **************************************
[HttpPost]
public ActionResult Create(Dinner dinner) {
if (ModelState.IsValid)
{
dinner.HostedBy = "The Code Dude";
_dinnerRepository.Add(dinner);
_dinnerRepository.Save();
return RedirectToAction("Details", new { id = dinner.DinnerID });
}
else
{
return View(dinner);
}
}
代码 D:
// **************************************
// GET: /Dinners/Edit/{id}
// **************************************
public ActionResult Edit(int id)
{
Dinner dinner = _dinnerRepository.GetDinner(id);
return View(dinner);
}
// **************************************
// POST: /Dinners/Edit/{id}
// **************************************
[HttpPost]
public ActionResult Edit(int id, FormCollection formValues)
{
Dinner dinner = _dinnerRepository.GetDinner(id);
if (TryUpdateModel(dinner)){
_dinnerRepository.Save();
return RedirectToAction("Details", new { id=dinner.DinnerID });
}
return View(dinner);
}
I已向 Wrox 和其中一位作者发送帮助请求,但尚未收到任何人的回复。由于这些错误,本书的读者甚至无法继续完成第一章的其余部分。即使我从 Codeplex 下载最新版本,它仍然存在相同的错误。有人可以帮助我并告诉我需要修复什么吗?谢谢-艾德。
Platform: Windows 7 Ultimate
IDE: Visual Studio 2010 Ultimate
Web Environment: ASP.NET MVC 2
Database: SQL Server 2008 R2 Express
Data Access: Entity Framework 4
Form Validation: DataAnnotations
Sample App: NerdDinner from Wrox Pro ASP.NET MVC 2
Book: Wrox Professional MVC 2
Problem with Chapter 1 - Section: "Integrating Validation and Business Rule Logic with Model Classes" (pages 33 to 35)
ERROR Synopsis: NerdDinner form validation ERROR with DataAnnotations and db nulls.
DataAnnotations in sample code does not work when the database fields are set to not allow nulls.
ERROR occurs with the code from the book and with the sample code downloaded from codeplex.
Help! I'm really frustrated by this!! I can't believe something so simple just doesn't work???
Steps to reproduce ERROR:
- Set Database fields to not allow NULLs (See Picture)
- Set NerdDinnerEntityModel Dinner class fields'
Nullable property to false (See Picture) - Add DataAnnotations for Dinner_Validation class (CODE A)
- Create Dinner repository class (CODE B)
- Add CREATE action to DinnerController (CODE C)
- This is blank form before posting (See Picture)
- This null ERROR occurs when posting a
blank form which should be
intercepted by the Dinner_Validation class
DataAnnotations. Note ERROR message
says that "This property cannot be
set to a null value. WTH??? (See Picture) - The next ERROR occurs during the edit
process. Here is the Edit controller
action (CODE D) - This is the "Edit" form with intentionally wrong
input to test Dinner Validation
DataAnnotations (See Picture) - The ERROR occurs again when posting the
edit form with blank form fields. The post request
should be intercepted by the Dinner_Validation class DataAnnotations. Same
null entry error. WTH??? (See Picture)
See screen shots at:
http://www.intermedia4web.com/temp/nerdDinner/StackOverflowNerdDinnerQuestionshort.png
CODE A:
[MetadataType(typeof(Dinner_Validation))]
public partial class Dinner { }
[Bind(Include = "Title, EventDate, Description, Address, Country, ContactPhone, Latitude, Longitude")]
public class Dinner_Validation
{
[Required(ErrorMessage = "Title is required")]
[StringLength(50, ErrorMessage = "Title may not be longer than 50 characters")]
public string Title { get; set; }
[Required(ErrorMessage = "Description is required")]
[StringLength(265, ErrorMessage = "Description must be 256 characters or less")]
public string Description { get; set; }
[Required(ErrorMessage="Event date is required")]
public DateTime EventDate { get; set; }
[Required(ErrorMessage = "Address is required")]
public string Address { get; set; }
[Required(ErrorMessage = "Country is required")]
public string Country { get; set; }
[Required(ErrorMessage = "Contact phone is required")]
public string ContactPhone { get; set; }
[Required(ErrorMessage = "Latitude is required")]
public double Latitude { get; set; }
[Required(ErrorMessage = "Longitude is required")]
public double Longitude { get; set; }
}
CODE B:
public class DinnerRepository
{
private NerdDinnerEntities _NerdDinnerEntity = new NerdDinnerEntities();
// Query Method
public IQueryable<Dinner> FindAllDinners()
{
return _NerdDinnerEntity.Dinners;
}
// Query Method
public IQueryable<Dinner> FindUpcomingDinners()
{
return from dinner in _NerdDinnerEntity.Dinners
where dinner.EventDate > DateTime.Now
orderby dinner.EventDate
select dinner;
}
// Query Method
public Dinner GetDinner(int id)
{
return _NerdDinnerEntity.Dinners.FirstOrDefault(d => d.DinnerID == id);
}
// Insert Method
public void Add(Dinner dinner)
{
_NerdDinnerEntity.Dinners.AddObject(dinner);
}
// Delete Method
public void Delete(Dinner dinner)
{
foreach (var rsvp in dinner.RSVPs)
{
_NerdDinnerEntity.RSVPs.DeleteObject(rsvp);
}
_NerdDinnerEntity.Dinners.DeleteObject(dinner);
}
// Persistence Method
public void Save()
{
_NerdDinnerEntity.SaveChanges();
}
}
CODE C:
// **************************************
// GET: /Dinners/Create/
// **************************************
public ActionResult Create()
{
Dinner dinner = new Dinner() { EventDate = DateTime.Now.AddDays(7) };
return View(dinner);
}
// **************************************
// POST: /Dinners/Create/
// **************************************
[HttpPost]
public ActionResult Create(Dinner dinner) {
if (ModelState.IsValid)
{
dinner.HostedBy = "The Code Dude";
_dinnerRepository.Add(dinner);
_dinnerRepository.Save();
return RedirectToAction("Details", new { id = dinner.DinnerID });
}
else
{
return View(dinner);
}
}
CODE D:
// **************************************
// GET: /Dinners/Edit/{id}
// **************************************
public ActionResult Edit(int id)
{
Dinner dinner = _dinnerRepository.GetDinner(id);
return View(dinner);
}
// **************************************
// POST: /Dinners/Edit/{id}
// **************************************
[HttpPost]
public ActionResult Edit(int id, FormCollection formValues)
{
Dinner dinner = _dinnerRepository.GetDinner(id);
if (TryUpdateModel(dinner)){
_dinnerRepository.Save();
return RedirectToAction("Details", new { id=dinner.DinnerID });
}
return View(dinner);
}
I have sent Wrox and one of the authors a request for help but have not heard back from anyone. Readers of the book cannot even continue to finish the rest of chapter 1 because of these errors. Even if I download the latest build from Codeplex, it still has the same errors. Can someone please help me and tell me what needs to be fixed? Thanks - Ed.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
终于找到这本书的Wrox论坛有答案了。答案其实是令人惊讶的。它与 Visual Studio 2010 中的调试器有关。
基本上只需按 F5 即可继续,一切正常。
以下是包含更多答案的论坛主题的链接:
http: //p2p.wrox.com/book-professional-asp-net-mvc-2/79788-constraintexception-unhandled-user-code.html#post259245
Finally found the Wrox forum for this book that has the answer. The answer is actually surprising. It has something to do with the debugger in Visual Studio 2010.
Essentially just hit F5 to continue and everything works fine.
Here is the link to the forum thread with more answers:
http://p2p.wrox.com/book-professional-asp-net-mvc-2/79788-constraintexception-unhandled-user-code.html#post259245
工具->选项-> (展开)调试-> (常规)启用异常助手。
Visual Studio 只是想提供帮助! :) 一开始也吓到了我......以为代码有一些令人震惊的错误。是的,只要恢复执行,一切都会好起来的。验证内部将捕获异常(以及其他异常,例如转换、范围等)并将其记录在 ModelState.Errors 集合中。
Tools -> Options -> (expand) Debugging -> (General) Enable the exception assistant.
It's just Visual Studio trying to be helpful! :) Scared me too at first ... thought there was something show-stoppingly wrong with the code. Yeap, just resume execution and all will be fine. The validation internals will catch the Exception (this and others as well, such as conversion, range, etc) and log it in the ModelState.Errors collection.