【问题标题】:Validation failed for one or more entities while saving changes to SQL Server Database using Entity Framework使用实体框架保存对 SQL Server 数据库的更改时验证一个或多个实体失败
【发布时间】:2011-07-21 00:11:51
【问题描述】:

我想将我的编辑保存到数据库,并且我在 ASP.NET MVC 3 / C# 中使用实体框架代码优先,但我遇到了错误。在我的 Event 类中,我有 DateTime 和 TimeSpan 数据类型,但在我的数据库中,我分别有 Date 和 time。这可能是原因吗?在将更改保存到数据库之前,如何在代码中转换为适当的数据类型。

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:\sep\MvcEventCalendar\MvcEventCalendar\Controllers\EventManagerController.cs 行:77

堆栈跟踪:

[DbEntityValidationException:一个或多个实体的验证失败。有关详细信息,请参阅“EntityValidationErrors”属性。]

【问题讨论】:

  • 您的必填字段之一可能为空值。比如 EventDate 、 StartTime 、 Price、 Category 等
  • 您是否检查过要发布的表单变量以确保每个变量都与数据库定义的类型匹配?或者就像 Daveo 所说的那样,缺少所需的表单值之一......
  • 并非所有发布的表单变量都与数据库定义的类型匹配。我在数据库中使用了日期和时间,但在 .NET 中没有等效的直接数据类型。因此,我使用了 DateTime 和 TimeSpan。现在我需要将两者分别转换为日期和时间。
  • 我的错误来源是一个超过 30 个字符的字符串字段,我在数据库中的字段大小为 30。

标签: c# sql asp.net-mvc entity-framework code-first


【解决方案1】:

您可以使用以下代码从DbEntityValidationException 中提取所有信息(您需要将命名空间:System.Data.Entity.ValidationSystem.Diagnostics 添加到您的using 列表中):

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

【讨论】:

  • 如果任何种子数据不完全满足模型属性规则(如Required),也会出现此错误。我添加了一个包含更多信息的答案。
  • 可以通过解释实际可以看到跟踪输出的位置来改进答案。
  • 我发现这个 msdn article 关于跟踪非常有用
  • 你先生,赢得了互联网。
  • 可以在输出窗口中看到跟踪输出。点击顶部的调试 -> 窗口 -> 输出
【解决方案2】:

无需更改代码:

当您在catch {...} 块内处于调试模式时,打开“QuickWatch”窗口(Ctrl+Alt+Q)并粘贴在那里:

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

或:

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

如果您不在 try/catch 中或无权访问异常对象。

这将允许您深入了解ValidationErrors 树。这是我发现即时了解这些错误的最简单方法。

【讨论】:

  • 你我的朋友是个天才,你帮助我追查了我收到的 ET 错误,谢谢!
  • 没问题 :) 但不是天才,只是喜欢 QuickWatch :)
  • 刚刚为那些无权访问异常对象/变量的人更新了答案。
  • 每次遇到此异常时,我都会搜索错误,然后我来到这里(Google 的第二个结果),我找到了这篇文章,并在“调试”>“观察”面板中使用了第二个解决方案。做了几十次。谢谢GONeale。
  • 我是唯一一个在运行第二个查询时得到“当前上下文中不存在名称 '$exception'”的人吗?
【解决方案3】:

如果您有具有相同属性名称的类,这里是对 Praveen 答案的一个小扩展:

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

【讨论】:

    【解决方案4】:

    作为对 Praveen 和 Tony 的改进,我使用了覆盖:

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

    【讨论】:

      【解决方案5】:

      此实现将实体异常包装到带有详细文本的异常。 它处理DbEntityValidationExceptionDbUpdateExceptiondatetime2 范围错误(MS SQL),并在消息中包含无效实体的键(在一次SaveChanges 调用中保存多个实体时很有用)。

      首先,在 DbContext 类中覆盖 SaveChanges

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

      【讨论】:

      • 如果你想处理异常,你应该等待 SaveChangesAsync 调用。
      • 谢谢,Arnab Chakraborty!答案固定
      【解决方案6】:

      当我遇到实体验证错误时,此代码帮助我找到了问题。它告诉我实体定义的确切问题。 在需要覆盖 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;
      }
      

      【讨论】:

        【解决方案7】:

        我今天遇到了这个错误,有一段时间无法解决,但我意识到这是在我的模型中添加了一些 RequireAttributes 之后,并且一些开发种子数据没有填充所有必需的字段。
        所以请注意,如果您在通过某种初始化策略(如DropCreateDatabaseIfModelChanges)更新数据库时遇到此错误,那么您必须确保您的种子数据满足并满足任何模型数据验证属性.

        我知道这与问题中的问题略有不同,但这是一个受欢迎的问题,所以我想我会为与我有相同问题的其他人添加更多答案。
        希望这对其他人有帮助:)

        【讨论】:

          【解决方案8】:

          我认为为每个 SaveChanges() 操作添加 try/catch 不是一个好习惯,最好集中处理:

          将这个类添加到主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() 方法,您将获得一个逗号分隔的列表,其中包含所有实体验证错误。

          这也可以改进,在生产环境中记录错误,而不是仅仅抛出错误。

          希望这有帮助。

          【讨论】:

          • 在哪里可以找到“DbContext 类”?我对所有这些 MVC 东西都很陌生。我正在使用 MVC 5 和 Entity Framework 6。只是不知道我的解决方案资源管理器中的名称会是什么样的。
          • @JustJohn 它在您的数据库模型(类文件)中,如果您不知道它在哪里,您可以针对您的项目对关键字 DbContext 进行完整搜索。
          • 我最喜欢这种方法,因为它是应用程序中的通用解决方案,可以轻松揭示表面之下的内容,而无需对系统中的任何其他模块进行太多更改
          【解决方案9】:

          这是对 Tony 的扩展的扩展...:-)

          对于Entity Framework 4.x,如果要获取key字段的名称和值,以便知道哪个实体实例(DB记录)有问题,可以添加以下内容。这提供了对 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;
          

          【讨论】:

            【解决方案10】:

            我不喜欢例外 我注册了 OnSaveChanges 并拥有了这个

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

            【讨论】:

            • 你说“我注册了 OnSaveChanges”是什么意思?
            • 我和其他人一样连接到 Context 上的 OnSaveChanges,我只是没有达到异常阶段。\
            【解决方案11】:

            当您尝试保存存在验证错误的实体时,也会发生此错误。造成这种情况的一个好方法是在保存到数据库之前忘记检查 ModelState.IsValid。

            【讨论】:

              【解决方案12】:

              确保如果 DB 行中有 nvarchar(50),则不要尝试在其中插入超过 50 个字符。愚蠢的错误,但我花了 3 个小时才弄明白。

              【讨论】:

                【解决方案13】:

                感谢您的回答,对我帮助很大。 正如我在 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
                

                【讨论】:

                  【解决方案14】:

                  它可能是由模型未填充的属性引起的。而是由控制器填充的。这可能会导致此错误。解决此问题的方法是在应用模型状态验证之前分配属性。 而这第二个假设是 。您的数据库中可能已经有数据 并尝试对其进行更新,但现在正在获取它。

                  【讨论】:

                    【解决方案15】:

                    这可能是由于特定列允许的最大字符数,例如在 sql 中,一个字段可能具有以下数据类型 nvarchar(5) 但用户输入的字符数超过了指定的字符数,因此出现错误出现。

                    【讨论】:

                      【解决方案16】:

                      几天前我在更新数据库时遇到了同样的问题。在我的情况下,为维护添加了一些新的不可为空的列,这些列未在导致异常的代码中提供。我找出了这些字段并为它们提供了值并解决了它。

                      【讨论】:

                        【解决方案17】:

                        在我的情况下,我有一个表列名称路径,我设置的数据类型是 varchar(200)。在将其更新为 nvarchar(max) 后,我从 edmx 中删除了该表,然后再次添加了该表,它正确地工作了我。

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 2018-05-09
                          • 1970-01-01
                          • 2019-01-15
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2019-07-24
                          相关资源
                          最近更新 更多