【问题标题】:Why doesn't ModelState.IsValid work properly for foreign key constraint?为什么 ModelState.IsValid 不能正常用于外键约束?
【发布时间】:2013-05-07 01:20:29
【问题描述】:

我有一个 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; }

}

【问题讨论】:

    标签: asp.net-mvc entity-framework ef-code-first entity-framework-5


    【解决方案1】:

    为什么 ModelState.IsValid 在进入 dbcontext.save 之前没有发现这个问题?

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

    您尚未显示您的表单,但看到您的 Edit 操作方法接受 MeterPeak 我假设您的 HTTP 帖子中发送了以下值:IdMeterSiteIdDateTimeRatingNotePeakReading(您应该可以使用浏览器中的调试工具来验证这一点)。

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

    然后,使用您提供的值,针对您的对象上存在的任何验证属性或调用Validate 方法(如果您的对象实现IValidatableObject 接口)来验证该对象。

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

    总结一下:

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

    尚未进行任何数据库调用,因此 MVC 无法知道此时已违反外键约束

    附带说明,我建议不要在 ASP.NET MVC 应用程序控制器中使用您的实体作为“模型”。您真正应该拥有的是视图特定模型 a (viewModel),然后将其转换为您的控制器中的实体(手动或使用诸如 AutoMapper 之类的框架)。这听起来可能需要做很多工作,但最终会得到回报。我对问题how to avoid needing a viewModel for every model 的回答中提供了一些好处。问题Real example of TryUpdateModel, ASP .NET MVC 3 中提供了进一步的讨论。

    【讨论】:

    • 谢谢你 :-) 非常全面的回答
    • 问题 - 我正在阅读这篇文章的方式,似乎ModelState.IsValid 有一个检查以确定模型是否为IValidatableObject,如果是,则另外调用Validate 方法给其他装饰者。这是一个准确的说法,还是我误解了?
    • 验证系统查找数据注释和IValidatableObject 接口,并作为模型绑定过程的一部分执行。任何违反约束的行为都会添加到ModelStateModelState.IsValid 如果错误字典中没有错误,则返回 true,否则返回 false。
    猜你喜欢
    • 1970-01-01
    • 2020-08-14
    • 1970-01-01
    • 2020-05-31
    • 1970-01-01
    • 2011-03-26
    • 2019-03-08
    • 2017-01-30
    • 2011-04-22
    相关资源
    最近更新 更多