1

我有一個MeterPeak實體,這個實體有一個MeterReading實體作爲外鍵(包含在問題的底部)。 MeterReading實體具有由MeterSiteId和DateTime組成的複合主鍵。爲什麼ModelState.IsValid不能正確地處理外鍵約束?

所以我的理解是,在我的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); 
    } 

當我進入一個MeterSiteId和不匹配現有抄表,並試圖挽救我期望的ModelState一個DateTime .IsValid檢查返回false,但它不。它只有當它保存與此錯誤更改的DbContext當獲取到unitOfWork.save()行,它的錯誤

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; } 

} 

回答

5

爲什麼ModelState.IsValid找不到讓到dbcontext.save之前這個問題?

ModelState是一個ASP.NET MVC的概念,不知道你的數據庫。 ModelState屬性表示傳遞給HTTP帖子中操作方法的信息的狀態。它工作正常。

您還沒有表現出你的形式,但看到你的Edit操作方法接受一個MeterPeak我假定了以下值的HTTP POST發送:IdMeterSiteIdDateTimeRatingNotePeakReading(你應該能夠通過使用瀏覽器中的調試工具來驗證這一點)。

ASP.NET MVC檢索這些值並使用模型聯編程序使用您提交的值構造一個MeterPeak對象。

然後使用您提供的值對對象上存在的任何驗證屬性進行驗證,或者如果您的對象實現了IValidatableObject接口,則通過調用Validate方法對其進行驗證。

注意與屬性驗證您的MeterPeak對象沒有任何裝飾性或實現IValidatableObject接口,因此沒有來驗證這解釋了爲什麼ModelState.IsValid屬性爲true。 (您應用的屬性被Entity Framework用來了解如何將您的類映射到數據庫表)。

總結:

  1. 你讓一個HTTP POST
  2. MVC檢索公佈值和使用模型綁定構建MeterPeak對象。
  3. 該對象使用上述規則進行驗證並傳遞給您的操作方法。

沒有數據庫已籲請尚未所以沒有辦法對MVC知道外鍵約束已在這一點上受到侵犯。

在旁註中,我建議不要在ASP.NET MVC應用程序控制器中將實體用作「模型」。你應該真正擁有的是一個特定於視圖的模型(viewModel),然後將其轉換爲控制器中的實體(手動或使用諸如AutoMapper之類的框架)。這可能聽起來像很多工作,但最終會帶來收益。我在回答問題how to avoid needing a viewModel for every model時提供了一些好處。在Real example of TryUpdateModel, ASP .NET MVC 3這個問題上可以進一步討論。

+0

感謝這:-)非常全面的答案 – Mort 2013-05-10 03:24:35

+0

問題 - 我讀的這種方式,似乎'ModelState.IsValid'有一個檢查,以確定模型是否是一個'IValidatableObject',如果是這樣會調用除了其他裝飾器之外,還有'Validate'方法。這是一個準確的陳述,還是我誤解? – 2013-10-25 20:26:01

+0

驗證系統查找數據註釋和「IValidatableObject」接口,並作爲模型綁定過程的一部分執行。任何違反約束條件的都會添加到'ModelState'中。如果錯誤詞典中沒有錯誤,則「ModelState.IsValid」只返回true,否則返回false。 – 2013-10-25 20:50:57