2011-03-23 27 views
317

我想將我的編輯保存到數據庫,並且我在ASP.NET MVC 3/C#中使用實體框架代碼優先,但我收到錯誤。在我的Event類中,我有DateTime和TimeSpan數據類型,但是在我的數據庫中,分別有Date和Time。這可能是原因嗎?在保存對數據庫的更改之前,如何在代碼中轉換爲適當的數據類型。使用實體框架保存對SQL Server數據庫的更改時,一個或多個實體的驗證失敗

public class Event 
{ 
    public int EventId { get; set; } 
    public int CategoryId { get; set; } 
    public int PlaceId { get; set; } 
    public string Title { get; set; } 
    public decimal Price { get; set; } 
    public DateTime EventDate { get; set; } 
    public TimeSpan StartTime { get; set; } 
    public TimeSpan EndTime { get; set; } 
    public string Description { get; set; } 
    public string EventPlaceUrl { get; set; } 
    public Category Category { get; set; } 
    public Place Place { get; set; } 
} 

控制器中的方法>>>>問題在storeDB.SaveChanges();在 '/' 應用

// POST: /EventManager/Edit/386   
[HttpPost] 
public ActionResult Edit(int id, FormCollection collection) 
{ 
    var theEvent = storeDB.Events.Find(id); 

    if (TryUpdateModel(theEvent)) 
    { 
     storeDB.SaveChanges(); 
     return RedirectToAction("Index"); 
    } 
    else 
    { 
     ViewBag.Categories = storeDB.Categories.OrderBy(g => g.Name).ToList(); 
     ViewBag.Places = storeDB.Places.OrderBy(a => a.Name).ToList(); 
     return View(theEvent); 
    } 
} 

public class EventCalendarEntities : DbContext 
{ 
    public DbSet<Event> Events { get; set; } 
    public DbSet<Category> Categories { get; set; } 
    public DbSet<Place> Places { get; set; } 
} 

的SQL Server 2008 R2數據庫/ T-SQL

EventDate (Datatype = date) 
StartTime (Datatype = time) 
EndTime (Datatype = time) 

HTTP表單

EventDate (Datatype = DateTime) e.g. 4/8/2011 12:00:00 AM 
StartTime (Datatype = Timespan/time not sure) e.g. 08:30:00 
EndTime (Datatype = Timespan/time not sure) e.g. 09:00:00 

服務器錯誤。

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

描述:執行當前Web請求期間發生未處理的異常。請查看堆棧跟蹤以獲取有關該錯誤的更多信息以及源代碼的位置。

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

源錯誤:

Line 75:    if (TryUpdateModel(theEvent)) 
Line 76:    { 
Line 77:     storeDB.SaveChanges(); 
Line 78:     return RedirectToAction("Index"); 
Line 79:    } 

源文件:C:\月\ MvcEventCalendar \ MvcEventCalendar \ \控制器線路EventManagerController.cs:77

堆棧跟蹤:

[DbEntityValidationException:驗證失敗對於一個或多個實體。有關更多詳細信息,請參閱「EntityValidationErrors」屬性。]

+7

可能您的一個必填字段爲空值。像EventDate,StartTime,價格,類別等 – Daveo 2011-03-23 04:00:26

+0

您是否檢查了發佈的表單變量以確保每個匹配數據庫定義的類型?或者像Daveo所說的那樣,缺少所需的表單值之一... – timothyclifford 2011-03-23 04:40:28

+0

並非所有發佈的表單變量都與數據庫定義的類型相匹配。我在數據庫中使用了日期和時間,但在.NET中沒有直接的數據類型。因此,我使用了DateTime和TimeSpan。現在我需要將這兩者分別轉換爲日期和時間。 – user522767 2011-03-23 04:51:17

回答

825

您可以提取所有從DbEntityValidationException用下面的代碼信息(您需要添加命名空間:System.Data.Entity.ValidationSystem.Diagnosticsusing列表):需要

catch (DbEntityValidationException dbEx) 
{ 
    foreach (var validationErrors in dbEx.EntityValidationErrors) 
    { 
     foreach (var validationError in validationErrors.ValidationErrors) 
     { 
      Trace.TraceInformation("Property: {0} Error: {1}", 
            validationError.PropertyName, 
            validationError.ErrorMessage); 
     } 
    } 
} 
+65

我希望我可以將其標記爲答案。 – quakkels 2012-02-03 14:33:42

+7

如果任何種子數據不完全滿足模型屬性規則(如'Required'),那麼您也可以得到此錯誤。我已經添加了更多信息的答案。 – 2012-12-13 10:52:52

+7

可以通過解釋實際可以看到跟蹤輸出的位置來改進答案。 – mkataja 2014-04-03 09:03:52

246

無需更改代碼:

當您處於catch {...}塊內的調試模式時,打開「QuickWatch」窗口(Ctrl + Alt + Q)並粘貼在那裏:

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

或:

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

如果你不是一個try/catch或不能訪問異常對象。

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

+6

你我的朋友是一個天才,你幫我追查了我收到的ET錯誤,謝謝你 – 2012-10-24 20:19:26

+4

沒問題:)但不是天才,只是愛快看:) – GONeale 2012-10-25 23:20:33

+4

只是你爲那些無權訪問異常對象/變量的人提供了答案。 – GONeale 2013-01-24 00:49:49

36

在你有相同的屬性名類的情況下,這裏是一個小擴展普利文的回答是:

catch (DbEntityValidationException dbEx) 
{ 
    foreach (var validationErrors in dbEx.EntityValidationErrors) 
    { 
     foreach (var validationError in validationErrors.ValidationErrors) 
     { 
      Trace.TraceInformation(
       "Class: {0}, Property: {1}, Error: {2}", 
       validationErrors.Entry.Entity.GetType().FullName, 
       validationError.PropertyName, 
       validationError.ErrorMessage); 
     } 
    } 
} 
3

這裏的一個擴展,託尼的擴展... :-)

對於實體框架4.x,如果您想獲取關鍵字段的名稱和值,以便知道哪個實體實例(數據庫記錄)存在問題,則可以添加以下內容。這提供了從您的DbContext對象訪問更強大的ObjectContext類成員的權限。

// Get the key field name & value. 
// This assumes your DbContext object is "_context", and that it is a single part key. 
var e = ((IObjectContextAdapter)_context).ObjectContext.ObjectStateManager.GetObjectStateEntry(validationErrors.Entry.Entity); 
string key = e.EntityKey.EntityKeyValues[0].Key; 
string val = e.EntityKey.EntityKeyValues[0].Value; 
3

我不喜歡例外 我註冊的OnSaveChanges和有此

var validationErrors = model.GetValidationErrors(); 

var h = validationErrors.SelectMany(x => x.ValidationErrors 
              .Select(f => "Entity: " 
                 +(x.Entry.Entity) 
                 + " : " + f.PropertyName 
                 + "->" + f.ErrorMessage)); 
+0

你說「我註冊了OnSaveChanges」是什麼意思? – 2014-01-24 20:09:15

+0

我連接到上下文的OnSaveChanges與其他人相同,我只是沒有達到異常階段。\ – 2014-01-27 21:01:10

22

作爲改進既普利文和託尼,我使用覆蓋:

public partial class MyDatabaseEntities : DbContext 
{ 
    public override int SaveChanges() 
    { 
     try 
     { 
      return base.SaveChanges(); 
     } 
     catch (DbEntityValidationException dbEx) 
     { 
      foreach (var validationErrors in dbEx.EntityValidationErrors) 
      { 
       foreach (var validationError in validationErrors.ValidationErrors) 
       { 
        Trace.TraceInformation("Class: {0}, Property: {1}, Error: {2}", 
         validationErrors.Entry.Entity.GetType().FullName, 
         validationError.PropertyName, 
         validationError.ErrorMessage); 
       } 
      } 

      throw; // You can also choose to handle the exception here... 
     } 
    } 
} 
4

我得到這個錯誤今天,並不能解決一段時間,但我意識到這是在我的模型中添加一些RequireAttribute s和一些開發種子數據w因爲沒有填充所有必填字段。
所以,只需要注意,如果在通過某種初始策略(如DropCreateDatabaseIfModelChanges)更新數據庫時發生此錯誤,那麼您必須確保您的種子數據符合並滿足任何模型數據驗證屬性

我知道這與問題中的問題略有不同,但這是一個受歡迎的問題,所以我想我會爲與我自己有同樣問題的其他人增加一點答案。
希望這可以幫助他人:)

0

Thnaks爲您的答案,它幫助我很多。當我在Vb.Net中編碼時,此Bolt代碼爲Vb。Net

Try 
    Return MyBase.SaveChanges() 
Catch dbEx As Validation.DbEntityValidationException 
    For Each [error] In From validationErrors In dbEx.EntityValidationErrors 
         From validationError In validationErrors.ValidationErrors 
         Select New With { .PropertyName = validationError.PropertyName, 
             .ErrorMessage = validationError.ErrorMessage, 
             .ClassFullName = validationErrors.Entry.Entity 
                    .GetType().FullName} 

     Diagnostics.Trace.TraceInformation("Class: {0}, Property: {1}, Error: {2}", 
              [error].ClassFullName, 
              [error].PropertyName, 
              [error].ErrorMessage) 
    Next 
    Throw 
End Try 
2

當您嘗試保存具有驗證錯誤的實體時,也會發生此錯誤。導致這種情況的一個好方法是在保存到數據庫之前忘記檢查ModelState.IsValid。

2

確保如果在DB行中有nvarchar(50),則不要在其中插入超過50個字符。愚蠢的錯誤,但花了我3個小時弄清楚。

4

我認爲加上try/catch語句,每SaveChanges()操作是不是一個很好的做法,這是更好地集中這一點:

這個類添加到主DbContext類:

public override int SaveChanges() 
{ 
    try 
    { 
     return base.SaveChanges(); 
    } 
    catch (DbEntityValidationException ex) 
    { 
     string errorMessages = string.Join("; ", ex.EntityValidationErrors.SelectMany(x => x.ValidationErrors).Select(x => x.ErrorMessage)); 
     throw new DbEntityValidationException(errorMessages); 
    } 
} 

這將覆蓋上下文的SaveChanges()方法,您將得到一個包含所有實體驗證錯誤的逗號分隔列表。

這也可以改善,記錄生產環境中的錯誤,而不是隻是拋出一個錯誤。

希望這會對您有所幫助。

+0

我在哪裏可以找到「DbContext類」?我有點新的MVC的東西。我正在使用MVC 5和實體框架6.只是不知道我的解決方案資源管理器中的名稱會是什麼樣子。 – JustJohn 2015-10-20 23:09:20

+0

@JustJohn它在你的數據庫模型(類文件)中,如果你不知道它在哪裏,你可以對你的項目進行關鍵字'DbContext'的完整搜索。 – 2015-10-21 15:00:10

+1

我喜歡這種方法,因爲它是應用程序中的通用解決方案,可輕鬆揭示表面下的內容,而不會對系統中的任何其他模塊進行太多改變 – Korayem 2016-08-27 18:41:01

5

當我遇到我的Entity VAlidation Erros問題時,此代碼有助於找到我的問題。它告訴我實體定義的確切問題。 請嘗試以下代碼,您需要覆蓋storeDB.SaveChanges();在下面的try catch塊中。

try 
{ 
     if (TryUpdateModel(theEvent)) 
     { 
      storeDB.SaveChanges(); 
      return RedirectToAction("Index"); 
     } 
} 
catch (System.Data.Entity.Validation.DbEntityValidationException dbEx) 
{ 
    Exception raise = dbEx; 
    foreach (var validationErrors in dbEx.EntityValidationErrors) 
    { 
     foreach (var validationError in validationErrors.ValidationErrors) 
     { 
      string message = string.Format("{0}:{1}", 
       validationErrors.Entry.Entity.ToString(), 
       validationError.ErrorMessage); 
      // raise a new exception nesting 
      // the current instance as InnerException 
      raise = new InvalidOperationException(message, raise); 
     } 
    } 
    throw raise; 
} 
0

其可以通過屬性其不受模型填充引起..代替它是由控制器填充..這可能會導致此錯誤..解決方案是施加的ModelState驗證之前分配屬性。 和這第二個假設是。您的數據庫中可能已有數據 並嘗試更新它,但現在將其提取。

1

這可能是由於特定列允許的字符的最大數量所致,例如在sql中,字段可能有以下數據類型nvarchar(5),但從用戶輸入的字符數超過了指定的字符數,因此出現錯誤。

3

此實現將實體包裝爲具有詳細文本的異常的異常。 它處理DbEntityValidationException,DbUpdateException,datetime2範圍錯誤(MS SQL),並在消息中包含無效實體的密鑰(在一個SaveChanges呼叫中提供許多實體時很有用)。

首先,覆蓋SaveChanges中的DbContext類:

public class AppDbContext : DbContext 
{ 
    public override int SaveChanges() 
    { 
     try 
     { 
      return base.SaveChanges(); 
     } 
     catch (DbEntityValidationException dbEntityValidationException) 
     { 
      throw ExceptionHelper.CreateFromEntityValidation(dbEntityValidationException); 
     } 
     catch (DbUpdateException dbUpdateException) 
     { 
      throw ExceptionHelper.CreateFromDbUpdateException(dbUpdateException); 
     } 
    } 

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken) 
    { 
     try 
     { 
      return await base.SaveChangesAsync(cancellationToken); 
     } 
     catch (DbEntityValidationException dbEntityValidationException) 
     { 
      throw ExceptionHelper.CreateFromEntityValidation(dbEntityValidationException); 
     } 
     catch (DbUpdateException dbUpdateException) 
     { 
      throw ExceptionHelper.CreateFromDbUpdateException(dbUpdateException); 
     } 
    } 

ExceptionHelper類:

public class ExceptionHelper 
{ 
    public static Exception CreateFromEntityValidation(DbEntityValidationException ex) 
    { 
     return new Exception(GetDbEntityValidationMessage(ex), ex); 
    } 

    public static string GetDbEntityValidationMessage(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); 
     return exceptionMessage; 
    } 

    public static IEnumerable<Exception> GetInners(Exception ex) 
    { 
     for (Exception e = ex; e != null; e = e.InnerException) 
      yield return e; 
    } 

    public static Exception CreateFromDbUpdateException(DbUpdateException dbUpdateException) 
    { 
     var inner = GetInners(dbUpdateException).Last(); 
     string message = ""; 
     int i = 1; 
     foreach (var entry in dbUpdateException.Entries) 
     { 
      var entry1 = entry; 
      var obj = entry1.CurrentValues.ToObject(); 
      var type = obj.GetType(); 
      var propertyNames = entry1.CurrentValues.PropertyNames.Where(x => inner.Message.Contains(x)).ToList(); 
      // check MS SQL datetime2 error 
      if (inner.Message.Contains("datetime2")) 
      { 
       var propertyNames2 = from x in type.GetProperties() 
             where x.PropertyType == typeof(DateTime) || 
              x.PropertyType == typeof(DateTime?) 
             select x.Name; 
       propertyNames.AddRange(propertyNames2); 
      } 

      message += "Entry " + i++ + " " + type.Name + ": " + string.Join("; ", propertyNames.Select(x => 
       string.Format("'{0}' = '{1}'", x, entry1.CurrentValues[x]))); 
     } 
     return new Exception(message, dbUpdateException); 
    } 
} 
+0

如果要處理異常,則應等待SaveChangesAsync調用。 – 2017-03-21 00:25:54

+0

謝謝,** Arnab Chakraborty **!答案固定 – Sel 2017-03-21 16:03:22

1

在更新數據庫中,我面臨同樣的問題,一個幾天前。在我的情況下,幾乎沒有添加新的非空列可用於維護,而這些列在導致異常的代碼中未提供。我找出這些字段併爲它們提供了值並解決了它們。

相關問題