一个或多个实体的validation失败,同时使用entity framework保存对SQL Server数据库的更改
我想保存我的编辑到数据库,我在ASP.NET MVC 3 / C#中使用entity framework代码,但我得到的错误。 在我的Event类中,我有DateTime和TimeSpan数据types,但是在我的数据库中,分别有Date和Time。 这可能是原因吗? 在保存对数据库的更改之前,如何在代码中转换为适当的数据types。
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) eg 4/8/2011 12:00:00 AM StartTime (Datatype = Timespan/time not sure) eg 08:30:00 EndTime (Datatype = Timespan/time not sure) eg 09:00:00
“/”应用程序中的服务器错误。
一个或多个实体的validation失败。 有关更多详细信息,请参阅“EntityValidationErrors”属性。
说明:执行当前Web请求期间发生未处理的exception。 请查看堆栈跟踪,了解有关错误的更多信息以及源代码的来源。
exception详细信息:System.Data.Entity.Validation.DbEntityValidationException:一个或多个实体的validation失败。 有关更多详细信息,请参阅“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:一个或多个实体的validation失败。 有关更多详细信息,请参阅“EntityValidationErrors”属性。
您可以使用以下代码从DbEntityValidationException
提取所有信息(您需要将名称空间: System.Data.Entity.Validation
和System.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); } } }
无需更改代码:
当您在catch {...}
块中处于debugging模式时,打开“QuickWatch”窗口( Ctrl + Alt + Q )并粘贴到那里:
((System.Data.Entity.Validation.DbEntityValidationException)ex).EntityValidationErrors
要么:
((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
如果您不在try / catch中或者无法访问exception对象。
这将允许您深入查看ValidationErrors
树。 这是我发现能够即时洞察这些错误的最简单的方法。
在这种情况下,你有相同的属性名称的类,这是普拉文的答案的一个小扩展:
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的改进,我使用覆盖:
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... } } }
我今天得到这个错误,并且不能解决一段时间,但是我意识到这是在我的模型中添加了一些RequireAttribute
之后,一些开发种子数据没有填充所有必填字段。
所以只需要注意,如果在通过某种初始化策略(如DropCreateDatabaseIfModelChanges
更新数据库时遇到此错误,则必须确保您的种子数据满足并满足任何模型数据validation属性 。
我知道这与问题中的问题略有不同,但是这是一个很受欢迎的问题,所以我想我会为与我自己有同样问题的其他人增加一些答案。
希望这可以帮助别人:)
我认为为每个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()
方法,你会得到一个包含所有实体validation错误的逗号分隔的列表。
这也可以改善,logging生产环境中的错误,而不是只是抛出一个错误。
希望这是有帮助的。
当我遇到我的Entity VAlidation Erros问题时,此代码帮助find了我的问题。 它告诉我实体定义的确切问题。 尝试下面的代码,你需要覆盖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; }
这是对Tony的扩展的扩展…… 🙂
对于Entity Framework 4.x,如果要获取关键字段的名称和值,以便知道哪个实体实例(数据库logging)存在问题,可以添加以下内容。 这提供了从您的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;
我不喜欢例外,我注册了OnSaveChanges并有此
var validationErrors = model.GetValidationErrors(); var h = validationErrors.SelectMany(x => x.ValidationErrors .Select(f => "Entity: " +(x.Entry.Entity) + " : " + f.PropertyName + "->" + f.ErrorMessage));
当您尝试保存具有validation错误的实体时,也会发生此错误。 导致这种情况的一个好方法是在保存到数据库之前忘记检查ModelState.IsValid。
确保如果在DB行中有nvarchar(50),则不要在其中插入超过50个字符。 愚蠢的错误,但花了我3个小时弄清楚。
此实现将实体包含在具有细节文本的exception中。 它处理DbEntityValidationException
, DbUpdateException
, datetime2
范围错误(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); } }
这可能是由于特定列允许的最大字符数所致,例如在sql中,一个字段可能具有以下数据typesnvarchar(5)
但是从用户input的字符数超过了指定的字符数,因此会出现错误。
Thnaks为您的答案,它帮助我很多。 因为我在Vb.Net代码,这个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
它可能是由不是由模型填充的属性引起的,而是由控制器填充,这可能会导致此错误..解决scheme是在应用ModelStatevalidation之前分配属性。 这第二个假设是。 您可能已经在您的数据库中有数据,并尝试更新它,但现在获取它。
几天前我在更新数据库时遇到了同样的问题。 在我的情况下,很less有新的不可空列被添加到维护中,而这些列在导致exception的代码中没有提供。 我找出这些领域,并为他们提供的价值和解决。