Asp.net mvc 为什么ModelState.IsValid对于外键约束不能正常工作?

Asp.net mvc 为什么ModelState.IsValid对于外键约束不能正常工作?,asp.net-mvc,entity-framework,ef-code-first,entity-framework-5,Asp.net Mvc,Entity Framework,Ef Code First,Entity Framework 5,我有一个MeterPeak实体,这个实体有一个MeterReading实体作为问题底部的外键。MeterReading实体具有由MeterSteId和DateTime组成的复合主键 因此,我的理解是,在MeterPeak实体中,我必须输入一个MeterSteid和一个与现有MeterReading实体匹配的日期时间,以满足外键约束。我不应该被允许链接到一个不存在的外键 但是,在MeterPeakController的编辑操作中,我有以下代码 [HttpPost] [Validat

我有一个MeterPeak实体,这个实体有一个MeterReading实体作为问题底部的外键。MeterReading实体具有由MeterSteId和DateTime组成的复合主键

因此,我的理解是,在MeterPeak实体中,我必须输入一个MeterSteid和一个与现有MeterReading实体匹配的日期时间,以满足外键约束。我不应该被允许链接到一个不存在的外键

但是,在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);
    }
当我输入一个与现有电表读数不匹配的MeterSteId和DateTime并尝试保存时,我希望ModelState.IsValid检查返回false,但它不会返回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; }

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

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

您还没有显示表单,但是看到您的编辑操作方法接受MeterPeak,我假设以下值正在HTTP post中发送:Id、MeterSteID、DateTime、Rating、Note和PeakReading您应该能够使用浏览器中的调试工具验证这一点

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

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

请注意,MeterPeak对象没有使用验证属性修饰任何属性,也没有实现IValidatableObject接口,因此没有任何可验证的内容,这解释了ModelState.IsValid属性为true的原因。实体框架使用您应用的属性来理解如何将类映射到数据库表

总结如下:

你发了一篇HTTP帖子 MVC检索发布的值,并使用模型绑定器构造MeterPeak对象。 使用上述规则验证对象,并将其传递给操作方法。 尚未进行任何数据库调用,因此MVC无法知道此时是否违反了外键约束

另一方面,我建议不要在ASP.NET MVC应用程序控制器中使用实体作为“模型”。您真正应该拥有的是一个特定于视图的模型—一个viewModel,然后手动或使用框架(例如)将其转换为控制器中的实体。这听起来可能需要做很多工作,但最终会得到回报。我在回答这个问题时提供了一些好处。该问题可作进一步讨论

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

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

您还没有显示表单,但是看到您的编辑操作方法接受MeterPeak,我假设以下值正在HTTP post中发送:Id、MeterSteID、DateTime、Rating、Note和PeakReading您应该能够使用浏览器中的调试工具验证这一点

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

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

请注意,MeterPeak对象没有使用验证属性修饰任何属性,也没有实现IValidatableObject接口,因此没有任何可验证的内容,这解释了ModelState.IsValid属性为true的原因。实体框架使用您应用的属性来理解如何将类映射到数据库表

总结如下:

你发了一篇HTTP帖子 MVC检索发布的值,并使用模型绑定器构造MeterPeak对象。 使用上述规则验证对象,并将其传递给操作方法。 尚未进行任何数据库调用,因此MVC无法知道此时是否违反了外键约束

另一方面,我建议不要将实体用作ASP.NET MVC应用程序控件中的“模型”
呃。您真正应该拥有的是一个特定于视图的模型—一个viewModel,然后手动或使用框架(例如)将其转换为控制器中的实体。这听起来可能需要做很多工作,但最终会得到回报。我在回答这个问题时提供了一些好处。该问题可作进一步讨论

感谢:-非常全面的回答问题-在我阅读本文的方式中,ModelState.IsValid似乎有一个检查,以确定模型是否为IValidatableObject,如果是,将调用Validate方法以及其他decorator。这是一个准确的陈述,还是我误解了?验证系统寻找数据注释和IValidatableObject接口,并作为模型绑定过程的一部分执行。任何违反约束的行为都将添加到ModelState中。如果错误字典中没有错误,则ModelState.IsValid仅返回true,否则返回false。感谢:-非常全面的回答问题-我阅读此问题的方式,似乎ModelState.IsValid有一个检查来确定模型是否为IValidatableObject,如果是这样,则除了调用其他装饰器外,还将调用Validate方法。这是一个准确的陈述,还是我误解了?验证系统寻找数据注释和IValidatableObject接口,并作为模型绑定过程的一部分执行。任何违反约束的行为都将添加到ModelState中。如果错误字典中没有错误,ModelState.IsValid只返回true,否则返回false。