为什么ModelState.IsValid不能正常用于外键约束?

Mor*_*ort 1 asp.net-mvc entity-framework ef-code-first entity-framework-5

我有一个MeterPeak实体,这个实体有一个MeterReading实体作为外键(包含在问题的底部).MeterReading实体具有由MeterSiteId和DateTime组成的复合主键.

所以我的理解是,在我的MeterPeak实体中,我必须输入一个MeterSiteId和一个与现有MeterReading实体匹配的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);
    }
Run Code Online (Sandbox Code Playgroud)

当我输入与现有仪表读数不匹配的MeterSiteId和DateTime并尝试保存时,我希望ModelState.IsValid检查返回false,但它不会.它只有在到达unitOfWork.save()行时才会失败,并且在将此更改保存到dbcontext时出错

The UPDATE statement conflicted with the FOREIGN KEY constraint "FK_dbo.MeterPeaks_dbo.MeterReadings_MeterSiteId_DateTime"
Run Code Online (Sandbox Code Playgroud)

为什么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; }

}
Run Code Online (Sandbox Code Playgroud)

Ben*_*ale 6

为什么ModelState.IsValid在到达dbcontext.save之前没有找到这个问题?

ModelState是一个ASP.NET MVC概念,不了解您的数据库.该ModelState属性表示传递给HTTP帖子中的action方法的信息的状态.它工作正常.

您还没有表现出你的形式,但看到你的Edit操作方法接受MeterPeak我假设以下值的HTTP POST被发送:Id,MeterSiteId,DateTime,Rating,NotePeakReading(你应该能够通过使用调试工具来验证这个你浏览器).

ASP.NET MVC检索这些值并使用模型绑定器MeterPeak使用您提交的值构造对象.

然后,使用您提供的值,对象对象上存在的任何验证属性,或者Validate如果对象实现IValidatableObject接口,则通过调用方法验证对象.

请注意,您的MeterPeak对象不会使用验证属性修饰任何属性或实现IValidatableObject接口,因此没有任何内容可以验证,这解释了ModelState.IsValid属性为何的真实性.(Entity Framework使用您应用的属性来了解如何将类映射到数据库表).

总结一下:

  1. 你发了一篇HTTP帖子
  2. MVC检索发布的值并使用模型绑定器构造MeterPeak对象.
  3. 使用上述规则验证对象并将其传递给您的操作方法.

还没有进行任何数据库调用,因此MVC无法知道此时是否违反了外键约束.

另外,我建议不要将您的实体用作ASP.NET MVC应用程序控制器中的"模型".你真正应该拥有的是视图特定模型a(viewModel),然后将其转换为控制器中的实体(手动或使用AutoMapper等框架).这可能听起来像很多工作,但最终会得到回报.在我对如何避免每个模型都需要viewModel的问题的答案中提供了一些好处.有关TryUpdateModel的实例,ASP .NET MVC 3的问题可以进一步讨论.