2011-03-23 48 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

在更新数据库中,我面临同样的问题,一个几天前。在我的情况下,几乎没有添加新的非空列可用于维护,而这些列在导致异常的代码中未提供。我找出这些字段并为它们提供了值并解决了它们。

相关问题