使用实体框架保存对SQL Server数据库的更改时,一个或多个实体的验证失败
我想将我的编辑保存到数据库,并且我在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”属性。]
您可以提取所有从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 {...}
块内的调试模式时,打开“QuickWatch”窗口(Ctrl + Alt + Q)并粘贴在那里:
((System.Data.Entity.Validation.DbEntityValidationException)ex).EntityValidationErrors
或:
((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
如果你不是一个try/catch或不能访问异常对象。
这将允许您深入到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);
}
}
}
这里的一个扩展,托尼的扩展... :-)
对于实体框架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;
我不喜欢例外 我注册的OnSaveChanges和有此
var validationErrors = model.GetValidationErrors();
var h = validationErrors.SelectMany(x => x.ValidationErrors
.Select(f => "Entity: "
+(x.Entry.Entity)
+ " : " + f.PropertyName
+ "->" + f.ErrorMessage));
你说“我注册了OnSaveChanges”是什么意思? – 2014-01-24 20:09:15
我连接到上下文的OnSaveChanges与其他人相同,我只是没有达到异常阶段。\ – 2014-01-27 21:01:10
作为改进既普利文和托尼,我使用覆盖:
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
s和一些开发种子数据w因为没有填充所有必填字段。
所以,只需要注意,如果在通过某种初始策略(如DropCreateDatabaseIfModelChanges
)更新数据库时发生此错误,那么您必须确保您的种子数据符合并满足任何模型数据验证属性。
我知道这与问题中的问题略有不同,但这是一个受欢迎的问题,所以我想我会为与我自己有同样问题的其他人增加一点答案。
希望这可以帮助他人:)
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
当您尝试保存具有验证错误的实体时,也会发生此错误。导致这种情况的一个好方法是在保存到数据库之前忘记检查ModelState.IsValid。
确保如果在DB行中有nvarchar(50),则不要在其中插入超过50个字符。愚蠢的错误,但花了我3个小时弄清楚。
我认为加上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()
方法,您将得到一个包含所有实体验证错误的逗号分隔列表。
这也可以改善,记录生产环境中的错误,而不是只是抛出一个错误。
希望这会对您有所帮助。
当我遇到我的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;
}
其可以通过属性其不受模型填充引起..代替它是由控制器填充..这可能会导致此错误..解决方案是施加的ModelState验证之前分配属性。 和这第二个假设是。您的数据库中可能已有数据 并尝试更新它,但现在将其提取。
这可能是由于特定列允许的字符的最大数量所致,例如在sql中,字段可能有以下数据类型nvarchar(5)
,但从用户输入的字符数超过了指定的字符数,因此出现错误。
此实现将实体包装为具有详细文本的异常的异常。 它处理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);
}
}
如果要处理异常,则应等待SaveChangesAsync调用。 – 2017-03-21 00:25:54
谢谢,** Arnab Chakraborty **!答案固定 – Sel 2017-03-21 16:03:22
在更新数据库中,我面临同样的问题,一个几天前。在我的情况下,几乎没有添加新的非空列可用于维护,而这些列在导致异常的代码中未提供。我找出这些字段并为它们提供了值并解决了它们。
可能您的一个必填字段为空值。像EventDate,StartTime,价格,类别等 – Daveo 2011-03-23 04:00:26
您是否检查了发布的表单变量以确保每个匹配数据库定义的类型?或者像Daveo所说的那样,缺少所需的表单值之一... – timothyclifford 2011-03-23 04:40:28
并非所有发布的表单变量都与数据库定义的类型相匹配。我在数据库中使用了日期和时间,但在.NET中没有直接的数据类型。因此,我使用了DateTime和TimeSpan。现在我需要将这两者分别转换为日期和时间。 – user522767 2011-03-23 04:51:17