Finding the reason for DBUpdateException
C#.NetEntity FrameworkC# Problem Overview
When calling DbContext.SaveChanges
, I get a DbUpdateException:
> An unhandled exception of type > 'System.Data.Entity.Infrastructure.DbUpdateException' occurred in > EntityFramework.dll. Additional information: An error occurred while > updating the entries. See the inner exception for details.
Unfortunately, there is no inner exception (at least, not as far as I can see). Is there any way to see exactly why SaveChanges
threw an exception? At the very least, it would be helpful to see what table SaveChanges tried to update with when the error occured.
C# Solutions
Solution 1 - C#
This is my override of SaveChanges. It gives me a useful place to put breakpoints:
public override int SaveChanges()
{
try
{
return base.SaveChanges();
}
catch (DbEntityValidationException e)
{
foreach (var eve in e.EntityValidationErrors)
{
Debug.WriteLine(@"Entity of type ""{0}"" in state ""{1}""
has the following validation errors:",
eve.Entry.Entity.GetType().Name,
eve.Entry.State);
foreach (var ve in eve.ValidationErrors)
{
Debug.WriteLine(@"- Property: ""{0}"", Error: ""{1}""",
ve.PropertyName, ve.ErrorMessage);
}
}
throw;
}
catch(DbUpdateException e)
{
//Add your code to inspect the inner exception and/or
//e.Entries here.
//Or just use the debugger.
//Added this catch (after the comments below) to make it more obvious
//how this code might help this specific problem
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
throw;
}
}
Reference:
Solution 2 - C#
Here's my override of SaveChanges, showing the additional code to deal with the DbUpdateException (as per the question).
public override int SaveChanges()
{
try
{
return base.SaveChanges();
}
catch (DbEntityValidationException vex)
{
var exception = HandleDbEntityValidationException(vex);
throw exception;
}
catch(DbUpdateException dbu)
{
var exception = HandleDbUpdateException(dbu);
throw exception;
}
}
private Exception HandleDbUpdateException(DbUpdateException dbu)
{
var builder = new StringBuilder("A DbUpdateException was caught while saving changes. ");
try
{
foreach (var result in dbu.Entries)
{
builder.AppendFormat("Type: {0} was part of the problem. ", result.Entity.GetType().Name);
}
}
catch (Exception e)
{
builder.Append("Error parsing DbUpdateException: " + e.ToString());
}
string message = builder.ToString();
return new Exception(message, dbu);
}
I've not made the logging code very specific, but it improves on the standard error message of something like:
The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value.
This way, at least I can see which entity has the problem, and that's normally enough to work it out.
Solution 3 - C#
Based on Colin's answer, fully detailed information on EF persistence failure can be provided like this:
public bool SaveChangesEx()
{
try
{
SaveChanges();
return true;
}
catch (DbEntityValidationException exc)
{
// just to ease debugging
foreach (var error in exc.EntityValidationErrors)
{
foreach (var errorMsg in error.ValidationErrors)
{
// logging service based on NLog
Logger.Log(LogLevel.Error, $"Error trying to save EF changes - {errorMsg.ErrorMessage}");
}
}
throw;
}
catch (DbUpdateException e)
{
var sb = new StringBuilder();
sb.AppendLine($"DbUpdateException error details - {e?.InnerException?.InnerException?.Message}");
foreach (var eve in e.Entries)
{
sb.AppendLine($"Entity of type {eve.Entity.GetType().Name} in state {eve.State} could not be updated");
}
Logger.Log(LogLevel.Error, e, sb.ToString());
throw;
}
}
Beside validation errors, update exception will output both general error and context information.
Note: C# 6.0 is required for this code to work, as it uses null propagation and string interpolation.
For .NET Core the code is slightly changed since possible raised exceptions have a different structure / are populated differently:
public void SaveChangesEx()
{
try
{
// this triggers defined validations such as required
Context.Validate();
// actual save of changes
Context.SaveChangesInner();
}
catch (ValidationException exc)
{
Logger.LogError(exc, $"{nameof(SaveChanges)} validation exception: {exc?.Message}");
throw;
}
catch (DbUpdateException exc)
{
Logger.LogError(exc, $"{nameof(SaveChanges)} db update error: {exc?.InnerException?.Message}");
throw;
}
catch (Exception exc)
{
// should never reach here. If it does, handle the more specific exception
Logger.LogError(exc, $"{nameof(SaveChanges)} generic error: {exc.Message}");
throw;
}
}
The Context can be enhanced to automatically reject changes on failure, if the same context is not immediately disposed:
public void RejectChanges()
{
foreach (var entry in ChangeTracker.Entries().Where(e => e.Entity != null).ToList())
{
switch (entry.State)
{
case EntityState.Modified:
case EntityState.Deleted:
entry.State = EntityState.Modified; //Revert changes made to deleted entity.
entry.State = EntityState.Unchanged;
break;
case EntityState.Added:
entry.State = EntityState.Detached;
break;
}
}
}
public bool SaveChangesInner()
{
try
{
SaveChanges();
return true;
}
catch (Exception)
{
RejectChanges();
throw;
}
}
Solution 4 - C#
When it seems that the real exception gets lost somewhere, your best bet is to break on every exception. Regardless of if it's catched or swallowed somewhere, in or out your reach, the debugger will break and allow you to see what's going on.
See this MSDN link for more info:
Solution 5 - C#
I was having the same error "The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value.\r\nThe statement has been terminated."
I put the Datetime value field like this Datetime.Now
var person = new Person
{
FirstName = "Sebastian",
LastName = "Back",
**BirthDate = DateTime.Now,**
IsActive = true,
};
Solution 6 - C#
I faced the same issue
If you are using SQL Server and you have parent and child tables with (FK) FOREIGN KEY,
make sure that the relationship among tables for example CASCADE on Delete or on Update.
Solution 7 - C#
I got same error and solve like that;
I have a field which data type is datetime and i aware that i am setting it without assigned datetime property. So i change it to "Datetime.Now" then i see problem was solved and it's been saved succesfuly
Solution 8 - C#
In my case i was using Stored Procedure to add an entity to DB. My SP is giving the runtime error due to which i was getting this error in C#. I corrected the SP and the EF worked perfectly. So if you are making use of SP to add,edit,delete or update in EF check if the corresponding sp is working correctly.
Solution 9 - C#
In my case, i there was created the table as DOMAIN\MyUser.TableName
without noticing... Instead, I needed to recreate it as dbo.TableName
.
My exception:
> {"ClassName":"System.Data.Entity.Infrastructure.DbUpdateException","Message":"An > error occurred while updating the entries. See the inner exception for > details.","Data":null,"InnerException":{"ClassName":"System.Data.Entity.Core.UpdateException","Message":"An > error occurred while updating the entries. See the inner exception for > details.","Data":null,"InnerException":{"Errors":[{"Source":".Net > SqlClient Data > Provider","Number":208,"State":1,"Class":16,"Server":"***","Message":"Invalid > object name > 'dbo.TableName'.","Procedure":"","LineNumber":1}],"ClientConnectionId":"ab3fcb89-392d-42a5-9548-19a8d0cf0cdb","ClassName":"System.Data.SqlClient.SqlException","Message":"Invalid > object name > 'dbo.TableName'.","Data":{"HelpLink.ProdName":"Microsoft SQL > Server","HelpLink.ProdVer":"15.00.2000","HelpLink.EvtSrc":"MSSQLServer","HelpLink.EvtID":"208","HelpLink.BaseHelpUrl":"http://go.microsoft.com/fwlink","HelpLink.LinkId":"20476"}