我有一个MeterPeak实体,这个实体有一个MeterReading实体作为外键(包含在问题的底部)。 MeterReading实体具有由MeterSiteId和DateTime组成的复合主键。
所以我的理解是,在我的MeterPeak实体中,我必须输入一个与现有MeterReading实体匹配的MeterSiteId和DateTime,以满足外键约束。我不应该被允许链接到不存在的外键。
但是,在MeterPeakController的编辑操作中,我有以下代码
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(MeterPeak meterpeak)
{
if (ModelState.IsValid)
{
unitOfWork.MeterPeakRepository.Update(meterpeak);
unitOfWork.Save();
return RedirectToAction("Index");
}
ViewBag.MeterSiteId = new SelectList(unitOfWork.MeterSiteRepository.Get(b => true), "Id", "Location", meterpeak.MeterSiteId);
return View(meterpeak);
}
当我输入与现有仪表读数不匹配的MeterSiteId和DateTime并尝试保存时,我希望ModelState.IsValid检查返回false,但事实并非如此。它只有在到达unitOfWork.save()行时才会失败,并且在使用此错误保存对dbcontext的更改时出错
The UPDATE statement conflicted with the FOREIGN KEY constraint "FK_dbo.MeterPeaks_dbo.MeterReadings_MeterSiteId_DateTime"
为什么ModelState.IsValid在到达dbcontext.save之前没有找到这个问题?
public class MeterPeak
{
[Key]
public int Id { get; set; }
[ForeignKey("PeakReading"), Column(Order = 1)]
public int MeterSiteId { get; set; }
[ForeignKey("PeakReading"), Column(Order = 2)]
public DateTime DateTime { get; set; }
public int? Rating { get; set; }
public String Note { get; set; }
public virtual MeterReading PeakReading { get; set; }
}
public class MeterReading
{
[Key, Column(Order = 1)]
[Required(ErrorMessage = "Please Select a Meter Site")]
public int MeterSiteId { get; set; }
[Key, Column(Order = 2)]
[Required]
public DateTime DateTime { get; set; }
//This is not the Primary key but I need one unique value to assist in getting records, especially from Javascript
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid SingleKey { get; set; }
public virtual MeterSite MeterSite { get; set; }
[Required]
[DisplayFormat(DataFormatString = "{0:#,##0.00000#}", ApplyFormatInEditMode = true)]
public Double RawLevel { get; set; }
[Required]
[DisplayFormat(DataFormatString = "{0:#,##0.00000#}", ApplyFormatInEditMode = true)]
public Double RawVelocity { get; set; }
[Required]
[DisplayFormat(DataFormatString = "{0:#,##0.00000#}", ApplyFormatInEditMode = true)]
public Double RawFlow { get; set; }
[DisplayFormat(DataFormatString = "{0:#,##0.00000#}", ApplyFormatInEditMode = true)]
public Double ValidLevel { get; set; }
[DisplayFormat(DataFormatString = "{0:#,##0.00000#}", ApplyFormatInEditMode = true)]
public Double ValidVelocity { get; set; }
[DisplayFormat(DataFormatString = "{0:#,##0.00000#}", ApplyFormatInEditMode = true)]
public Double ValidFlow { get; set; }
[Range(-1, 3, ErrorMessage = "Rating must be one of the following -1,0,1,2,3")]
public int Rating { get; set; }
public bool? Valid { get; set; }
public virtual ICollection<Note> Notes { get; set; }
}
答案 0 :(得分:6)
为什么ModelState.IsValid在到达dbcontext.save之前没有找到这个问题?
ModelState
是ASP.NET MVC概念,不了解您的数据库。 ModelState
属性表示传递给HTTP帖子中的操作方法的信息的状态。它工作正常。
您尚未显示自己的表单,但看到Edit
操作方法接受MeterPeak
我假设您的HTTP帖子中发送了以下值:Id
,{ {1}},MeterSiteId
,DateTime
,Rating
和Note
(您应该可以使用浏览器中的调试工具对此进行验证)。
ASP.NET MVC检索这些值并使用模型绑定器使用您提交的值构造PeakReading
对象。
然后,使用您提供的值,对象对象上存在的任何验证属性,或者如果对象实现IValidatableObject
接口,则调用MeterPeak
方法验证对象。
请注意,您的Validate
对象不会使用验证属性修饰任何属性或实现MeterPeak
接口,因此没有任何内容可以验证,这解释了IValidatableObject
属性为何成立的原因。 (Entity Framework使用您应用的属性来了解如何将类映射到数据库表。)
总结:
ModelState.IsValid
对象。尚未进行任何数据库调用,因此MVC无法知道此时违反了外键约束。
另外,我建议不要将您的实体用作ASP.NET MVC应用程序控制器中的“模型”。你真正应该拥有的是视图特定模型a(viewModel),然后将其转换为控制器中的实体(手动或使用AutoMapper等框架)。这可能听起来像很多工作,但最终会得到回报。我对问题how to avoid needing a viewModel for every model的回答中提供了一些好处。问题Real example of TryUpdateModel, ASP .NET MVC 3中提供了进一步的讨论。