首页 文章

使用Entity Framework保存对SQL Server数据库的更改时,一个或多个实体的验证失败

提问于
浏览
328

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

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”属性 .

16 回答

  • 2

    如果数据库行中有nvarchar(50),请确保不要在其中插入超过50个字符 . 愚蠢的错误,但花了我3个小时来搞清楚 .

  • 3

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

  • 4

    此实现将实体异常包装到具有详细文本的异常 . 它处理 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);
        }
    }
    
  • 4

    当我遇到我的Entity VAlidation Erros问题时,这段代码帮助我找到了问题 . 它告诉我我的实体定义的确切问题 . 尝试使用以下代码来覆盖storeDB.SaveChanges();在下面尝试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;
    }
    
  • 22

    如果你有具有相同属性名称的类,这里是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);
           }
        }
     }
    
  • 1

    为你的答案提供帮助,它帮助了我很多 . 我在Vb.Net中编码,这是Vb.Net的Bolt代码

    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
    
  • 3

    它可能由未被模型填充的Property引起..而不是它由Controller ...填充,这可能会导致此错误 . 解决方法是在应用ModelState验证之前分配属性 . 而这第二个假设是 . 您可能已经在数据库中拥有数据并尝试更新它但现在正在获取它 .

  • 0

    几天前我在更新数据库时遇到了同样的问题 . 在我的例子中,为维护添加了几个新的非可空列,这些列未在导致异常的代码中提供 . 我找出了这些字段并为它们提供了值并将其解析 .

  • 6

    我认为为每个 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() 方法,您将获得一个包含所有实体验证错误的逗号分隔列表 .

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

    希望这是有帮助的 .

  • 831

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

    对于Entity Framework 4.x,如果要获取关键字段的名称和值,以便了解哪个实体实例(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;
    
  • 5

    No code change required:

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

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

    要么:

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

    如果您不在try / catch中,或者无法访问异常对象 .

    这将允许您深入查看 ValidationErrors 树 . 它已被发现可以立即了解这些错误 .

  • 0

    作为对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...
            }
        }
    }
    
  • 1

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

  • 2

    我不喜欢我登记OnSaveChanges的例外情况

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

    我今天得到了这个错误,暂时无法解决,但我意识到这是在添加了一些 RequireAttribute s之后我的模型和一些开发种子数据没有填充所有必填字段 .
    所以请注意,如果您在通过某种初始策略(如 DropCreateDatabaseIfModelChanges )更新数据库时遇到此错误,那么您必须确保 seed data fulfils and satisfies any model data validation attributes .

    我知道这与问题中的问题略有不同,但是对于其他与我自己有同样问题的人来说,它会增加一些答案 .
    希望这有助于其他人:)

  • 36

    您可以使用以下代码从 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);
            }
        }
    }
    

相关问题