2013-04-04 40 views
194

我有一個使用實體框架的項目。雖然我DbContext調用SaveChanges,我得到以下異常:DbEntityValidationException - 如何輕鬆分辨造成錯誤的原因?

System.Data.Entity.Validation.DbEntityValidationException:驗證 失敗的一個或多個實體。有關更多詳細信息,請參閱「EntityValidationErrors」屬性 。

這是一切都很好,但我不希望每次發生此異常時都附加一個調試器。更重要的是,在生產環境中,我不能輕鬆附加調試器,因此我必須竭盡全力來重現這些錯誤。

如何查看隱藏在DbEntityValidationException內的詳細信息?

回答

388

最簡單的解決方案是在您的實體類上覆蓋SaveChanges。您可以捕獲DbEntityValidationException,打開實際的錯誤並使用改進的消息創建新的DbEntityValidationException

  1. 在SomethingSomething.Context.cs文件旁邊創建一個部分類。
  2. 使用此帖子底部的代碼。
  3. 就是這樣。您的實現將自動使用重寫SaveChanges而不需要任何重構工作。

你的異常消息現在看起來像這樣:

System.Data.Entity.Validation.DbEntityValidationException:驗證 失敗的一個或多個實體。有關更多詳細信息,請參閱「EntityValidationErrors」屬性 。驗證錯誤爲:字段PhoneNumber 必須是最大長度爲'12'的字符串或數組類型; 姓氏字段是必需的。

您可以從DbContext繼承任何類刪除重寫的SaveChanges:

public partial class SomethingSomethingEntities 
{ 
    public override int SaveChanges() 
    { 
     try 
     { 
      return base.SaveChanges(); 
     } 
     catch (DbEntityValidationException ex) 
     { 
      // Retrieve the error messages as a list of strings. 
      var errorMessages = ex.EntityValidationErrors 
        .SelectMany(x => x.ValidationErrors) 
        .Select(x => x.ErrorMessage); 
     
      // Join the list to a single string. 
      var fullErrorMessage = string.Join("; ", errorMessages); 
     
      // Combine the original exception message with the new one. 
      var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage); 
     
      // Throw a new DbEntityValidationException with the improved exception message. 
      throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors); 
     } 
    } 
} 

DbEntityValidationException還包含導致驗證錯誤的實體。所以如果你需要更多的信息,你可以改變上面的代碼來輸出關於這些實體的信息。

參見:http://devillers.nl/improving-dbentityvalidationexception/

+6

生成的實體類已經從DbContext繼承,因此您不必在分部類上再次添加它。通過將其添加到部分類中,您不會破壞或更改任何內容。事實上,如果你從DbContext添加繼承,Resharper會建議你刪除它:「在其他部分已經指定了」Base type'DbContext'「。 – 2013-10-10 05:49:06

+10

爲什麼這不是SaveChanges的默認行爲? – 2015-06-17 18:59:36

+3

「爲什麼這不是SaveChanges的默認行爲?」 - 這是一個非常好的問題。這是一個驚人的解決方案,它節省了我幾個小時!我不得不拋出使用System.Linq;' – 2015-09-15 11:53:46

40

正如馬丁指出,沒有在DbEntityValidationResult的更多信息。我發現在每條消息中同時獲得POCO類名稱和屬性名稱非常有用,並且希望避免必須爲我的所有[Required]標記編寫自定義ErrorMessage屬性。

以下的調整,以馬丁的代碼照顧這些細節對我來說:

// Retrieve the error messages as a list of strings. 
List<string> errorMessages = new List<string>(); 
foreach (DbEntityValidationResult validationResult in ex.EntityValidationErrors) 
{ 
    string entityName = validationResult.Entry.Entity.GetType().Name; 
    foreach (DbValidationError error in validationResult.ValidationErrors) 
    { 
     errorMessages.Add(entityName + "." + error.PropertyName + ": " + error.ErrorMessage); 
    } 
} 
+1

使用[github]中的[SelectMany和Aggregate'(https://github.com/DaringCoders/DaringCore/blob/14a3e6e694890da128c94e635c2eb3e41e587bd9/DaringCore.EF/EntityFrameworkExceptionHelper.cs)_Daring Coders_ – Kiquenet 2017-03-29 15:02:34

-2

使用try塊在代碼中像

try 
{ 
    // Your code... 
    // Could also be before try if you know the exception occurs in SaveChanges 

    context.SaveChanges(); 
} 
catch (DbEntityValidationException e) 
{ 
    foreach (var eve in e.EntityValidationErrors) 
    { 
     Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:", 
      eve.Entry.Entity.GetType().Name, eve.Entry.State); 
     foreach (var ve in eve.ValidationErrors) 
     { 
      Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"", 
       ve.PropertyName, ve.ErrorMessage); 
     } 
    } 
    throw; 
} 

你可以在這裏查看詳細信息以及

  1. http://mattrandle.me/viewing-entityvalidationerrors-in-visual-studio/

  2. Validation failed for one or more entities. See 'EntityValidationErrors' property for more details

  3. http://blogs.infosupport.com/improving-dbentityvalidationexception/

+0

第三條鏈接是接受答案的博客的副本,但在不同的網站上。第二個鏈接是一個已經引用你的第一個鏈接的堆棧溢出問題。 – Eris 2015-01-30 08:47:20

+0

因此,試圖幫助有適當參考的人在這裏有任何問題? – 2015-04-15 12:37:18

+0

是的,你的答案不應該只包含鏈接。做出回答問題的總結,然後在末尾發佈鏈接以供進一步閱讀。 – ChrisO 2015-04-24 11:10:25

38

要查看EntityValidationErrors集合,添加以下觀看錶達到監視窗口。

((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors 

我使用的Visual Studio 2013

+0

$異常輝煌!這意味着在即時窗口中,我可以做$ exception.EntityValidationErrors.SelectMany(x => x.ValidationErrors).Select(x => x.ErrorMessage); – chrispepper1989 2017-12-05 10:20:37

2

我認爲「實際的驗證錯誤」可能包含敏感信息,這可能是爲什麼微軟選擇了把他們在另一個地方(性能)的原因。這裏標出的解決方案很實用,但應謹慎對待。

我寧願創建一個擴展方法。更多的原因是:

  • 保留原始的堆棧跟蹤
  • 按照開/常閉原則(即:我可以用不同的訊息,不同類型的日誌)
  • 在生產環境中可能有其他地方(即:其他dbcontext)其中可能會引發DbEntityValidationException。
12

當你在catch {...}塊開闢 「快速監視」 窗口(CTRL + ALT + q)內調試模式並粘貼在那裏:

((System.Data.Entity.Validation.DbEntityValidationException)ex).EntityValidationErrors 

這將允許您深入查看ValidationErrors樹。這是我發現能夠即時洞察這些錯誤的最簡單方法。

對於誰只關心第一個錯誤,可能不會有catch塊視覺2012+的用戶,你甚至可以這樣做:

((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors.First().ValidationErrors.First().ErrorMessage 
7

要通過調試期間檢查錯誤迅速找到有意義的錯誤信息:

  • 添加一個快速手錶:

    ((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors 
    
  • 深入查看像這樣的EntityValidationErrors:

    (收集項目例如[0])> ValidationErrors>(收集項目例如[0])>的ErrorMessage

4

實際上,這僅僅是驗證問題,EF將首先使對數據庫的任何改變之前驗證實體屬性。 因此,EF會檢查屬性值是否超出範圍,例如設計表格時。 Table_Column_UserName是varchar(20)。但是,在EF中,輸入的值大於20. 或者在其他情況下,如果列不允許爲空值。 因此,在驗證過程中,無論您是否要對其進行更改,您都必須將值設置爲非空列。 我個人喜歡Leniel Macaferi的回答。它可以向您顯示驗證問題的詳細信息

相關問題