Validation failed for one or more entities while saving changes to SQL Server Database using Entity Framework
我想将我的编辑保存到数据库中,并且在ASP.NET MVC 3/C中首先使用实体框架代码,但我遇到了错误。在我的事件类中,我有日期时间和时间跨度数据类型,但在我的数据库中,我分别有日期和时间。这就是原因吗?在将更改保存到数据库之前,如何在代码中强制转换为适当的数据类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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()中出现问题;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // 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); } } |
具有
1 2 3 4 5 6 | 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
1 2 3 | EventDate (Datatype = date) StartTime (Datatype = time) EndTime (Datatype = time) |
HTTP格式
1 2 3 | 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"属性。
源错误:
1 2 3 4 5 | Line 75: if (TryUpdateModel(theEvent)) Line 76: { Line 77: storeDB.SaveChanges(); Line 78: return RedirectToAction("Index"); Line 79: } |
源文件:C:SEPMVCEventCalendarMVCEventCalendarControllersEventManagerController.cs行:77
堆栈跟踪:
[DBEntityValidationException:一个或多个实体的验证失败。有关详细信息,请参阅"EntityValidationErrors"属性。]
可以使用以下代码从
1 2 3 4 5 6 7 8 9 10 11 12 | 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); } } } |
无需更改代码:
当您在
1 | ((System.Data.Entity.Validation.DbEntityValidationException)ex).EntityValidationErrors |
或:
1 | ((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors |
如果您不在Try/Catch中或没有访问异常对象的权限。
这将允许您深入到
如果您的类具有相同的属性名,下面是praveen答案的一个小扩展:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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); } } } |
作为对Praveen和Tony的改进,我使用了一个覆盖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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... } } } |
此实现将实体异常包装为带有详细文本的异常。它处理
首先,在dbContext类中重写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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); } } |
异常帮助程序类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | 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); } } |
当我的实体验证错误出现问题时,这段代码帮助我找到了问题。它告诉我实体定义的确切问题。在下面的try catch块中,尝试在需要覆盖storedb.savechanges()的地方使用以下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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; } |
我认为,为每个
将该类添加到主
1 2 3 4 5 6 7 8 9 10 11 12 | 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); } } |
这将覆盖上下文的
这也可以改进,在生产环境中记录错误,而不仅仅是抛出错误。
希望这是有帮助的。
我今天收到了这个错误,暂时无法解决,但我意识到这是在我的模型中添加了一些
我知道这和问题中的问题略有不同,但这是一个很受欢迎的问题,所以我想我会为其他和我有相同问题的人的答案再加一点。希望这能帮助别人:)
这是托尼分机的分机…-)
对于Entity Framework 4.x,如果要获取键字段的名称和值,以便知道哪个实体实例(db record)有问题,可以添加以下内容。这提供了从dbContext对象访问更强大的objectContext类成员的权限。
1 2 3 4 5 | // 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; |
我不喜欢例外我注册了OnSaveChanges并拥有这个
1 2 3 4 5 6 7 | var validationErrors = model.GetValidationErrors(); var h = validationErrors.SelectMany(x => x.ValidationErrors .Select(f =>"Entity:" +(x.Entry.Entity) +" :" + f.PropertyName +"->" + f.ErrorMessage)); |
确保如果数据库行中有nvarchar(50),则不会尝试在其中插入超过50个字符。愚蠢的错误,但我花了3个小时才弄明白。
当试图保存具有验证错误的实体时,也会发生此错误。导致这种情况的一个好方法是在保存到数据库之前忘记检查modelstate.isvalid。
这可能是由于特定列所允许的最大字符数,如在SQL中,一个字段可能具有以下数据类型
几天前我在更新数据库时遇到了同样的问题。在我的例子中,为维护而添加的新的不可为空的列很少,而导致异常的代码中没有提供这些列。我计算出这些字段,并为它们提供值,并对其进行解析。
在我的例子中,我有一个表列名路径,我设置的数据类型是varchar(200)。在将它更新为nvarchar(max)之后,我已经从edmx中删除了表,然后再次添加了表,它为我正常工作。
它可能是由模型未填充的属性引起的。而是由控制器填充。可能导致此错误。解决方案是在应用模型状态验证之前分配属性。第二个假设是。数据库中可能已有数据尝试更新它,但现在正在获取它。
为了你的答案,它帮助了我很多。当我在vb.net中编码时,这个bolt代码用于vb.net
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 |