I have a project that uses Entity Framework. While calling SaveChanges on my DbContext, I get the following exception:
System.Data.Entity.Validation.DbEntityValidationException: Validation
failed for one or more entities. See 'EntityValidationErrors' property
for more details.
This is all fine and dandy, but I don't want to attach a debugger every time this exception occurs. More over, in production environments I cannot easily attach a debugger so I have to go to great lengths to reproduce these errors.
How can I see the details hidden within the DbEntityValidationException?
The easiest solution is to override SaveChanges on your entities class. You can catch the DbEntityValidationException, unwrap the actual errors and create a new DbEntityValidationException with the improved message.
Create a partial class next to your SomethingSomething.Context.cs file.
Use the code at the bottom of this post.
That's it. Your implementation will automatically use the overriden SaveChanges without any refactor work.
Your exception message will now look like this:
System.Data.Entity.Validation.DbEntityValidationException: Validation
failed for one or more entities. See 'EntityValidationErrors' property
for more details. The validation errors are: The field PhoneNumber
must be a string or array type with a maximum length of '12'; The
LastName field is required.
You can drop the overridden SaveChanges in any class that inherits from DbContext:
public partial class SomethingSomethingEntities
{
public override int SaveChanges()
{
try
{
return base.SaveChanges();
}
catch (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);
// Throw a new DbEntityValidationException with the improved exception message.
throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors);
}
}
}
The DbEntityValidationException also contains the entities that caused the validation errors. So if you require even more information, you can change the above code to output information about these entities.
See also: http://devillers.nl/improving-dbentityvalidationexception/
As Martin indicated, there is more information in the DbEntityValidationResult. I found it useful to get both my POCO class name and property name in each message, and wanted to avoid having to write custom ErrorMessage attributes on all my [Required] tags just for this.
The following tweak to Martin's code took care of these details for me:
// Retrieve the error messages as a list of strings.
List<string> errorMessages = new List<string>();
foreach (DbEntityValidationResult validationResult in ex.EntityValidationErrors)
{
string entityName = validationResult.Entry.Entity.GetType().Name;
foreach (DbValidationError error in validationResult.ValidationErrors)
{
errorMessages.Add(entityName + "." + error.PropertyName + ": " + error.ErrorMessage);
}
}
To view the EntityValidationErrors collection, add the following Watch expression to the Watch window.
((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
I'm using visual studio 2013
While you are in debug mode within the catch {...} block open up the "QuickWatch" window (ctrl+alt+q) and paste in there:
((System.Data.Entity.Validation.DbEntityValidationException)ex).EntityValidationErrors
This will allow you to drill down into the ValidationErrors tree. It's the easiest way I've found to get instant insight into these errors.
For Visual 2012+ users who care only about the first error and might not have a catch block, you can even do:
((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors.First().ValidationErrors.First().ErrorMessage
To quickly find a meaningful error message by inspecting the error during debugging:
Add a quick watch for:
((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
Drill down into EntityValidationErrors like this:
(collection item e.g. [0]) > ValidationErrors > (collection item e.g. [0]) > ErrorMessage
Actually, this is just the validation issue, EF will validate the entity properties first before making any changes to the database.
So, EF will check whether the property's value is out of range, like when you designed the table. Table_Column_UserName is varchar(20). But, in EF, you entered a value that longer than 20.
Or, in other cases, if the column does not allow to be a Null.
So, in the validation process, you have to set a value to the not null column, no matter whether you are going to make the change on it.
I personally, like the Leniel Macaferi answer. It can show you the detail of the validation issues
I think "The actual validation errors" may contain sensitive information, and this could be the reason why Microsoft chose to put them in another place (properties). The solution marked here is practical, but it should be taken with caution.
I would prefer to create an extension method. More reasons to this:
Keep original stack trace
Follow open/closed principle (ie.: I can use different messages for different kind of logs)
In production environments there could be other places (ie.: other dbcontext) where a DbEntityValidationException could be thrown.
For Azure Functions we use this simple extension to Microsoft.Extensions.Logging.ILogger
public static class LoggerExtensions
{
public static void Error(this ILogger logger, string message, Exception exception)
{
if (exception is DbEntityValidationException dbException)
{
message += "\nValidation Errors: ";
foreach (var error in dbException.EntityValidationErrors.SelectMany(entity => entity.ValidationErrors))
{
message += $"\n * Field name: {error.PropertyName}, Error message: {error.ErrorMessage}";
}
}
logger.LogError(default(EventId), exception, message);
}
}
and example usage:
try
{
do something with request and EF
}
catch (Exception e)
{
log.Error($"Failed to create customer due to an exception: {e.Message}", e);
return await StringResponseUtil.CreateResponse(HttpStatusCode.InternalServerError, e.Message);
}
Use try block in your code like
try
{
// Your code...
// Could also be before try if you know the exception occurs in SaveChanges
context.SaveChanges();
}
catch (DbEntityValidationException e)
{
foreach (var eve in e.EntityValidationErrors)
{
Console.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)
{
Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
ve.PropertyName, ve.ErrorMessage);
}
}
throw;
}
You can check the details here as well
http://mattrandle.me/viewing-entityvalidationerrors-in-visual-studio/
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details
http://blogs.infosupport.com/improving-dbentityvalidationexception/
Related
I have a problem when I update entity in EF6. The code looks like this:
public PICCOSSourceCost GetCOSSourceCost(int sourceCostID)
{
return ERPContext.PICCOSSourceCost.Where(sc => sc.ID == sourceCostID && !sc.Deleted).FirstOrDefault();
}
public PICCOSSourceCost UpdateCOSSourceCost(PICCOSSourceCost sourceCost, bool saveChanges = true)
{
var sc = GetCOSSourceCost(sourceCost.ID);
if (sc == null)
{
throw new PICObjectNotFoundException<PICCOSSourceCost>(sourceCost, new List<string>()
{
nameof(PICCOSSourceCost.PICCOSSourceID),
nameof(PICCOSSourceCost.PICCOSPriceTypeID),
nameof(PICCOSSourceCost.Price),
nameof(PICCOSSourceCost.EffectiveDate)
});
}
sc.PICCOSSourceID = sourceCost.PICCOSSourceID;
sc.PICCOSPriceTypeID = sourceCost.PICCOSPriceTypeID;
sc.Price = sourceCost.Price;
sc.EffectiveDate = sourceCost.EffectiveDate;
sc.Deleted = sourceCost.Deleted;
sc.CreatedBy = sourceCost.CreatedBy;
sc.CreatedDate = sourceCost.CreatedDate;
sc.LastModifiedBy = sourceCost.LastModifiedBy;
sc.LastModifiedDate = sourceCost.LastModifiedDate;
if (saveChanges) ERPContext.SaveChanges();
return sc;
}
As you can see that the "GetCOSSourceCost" method get entity from EF. And the first parameter "sourceCost" in the "UpdateCOSSourceCost" method is passed in from FrontEnd, which is also got from EF6.
When I debug the code, there is an "System.Data.Entity.Validation.DbEntityValidationException" occurred. I don't know why. I think that it should be okay because I just get an entity object and just change its properties and save changes. Is it because there are two references to the same object?
If I remove the property assignment code, the error will disappear.
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
Do you know why it throws this exception? Please help me. Thank you so much!
Based on this
http://mattrandle.me/viewing-entityvalidationerrors-in-visual-studio/
You can use is a special debugger variable- $exception
To view the EntityValidationErrors collection you can use below to show watch windows
((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
Thank you for your help so much. I have solved my issue by the following link:
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details
For the sake of this link, I debugged and find the root cause of the entity validation error. Some required fields are set to NULL value.
The code from the answer is very useful:
try
{
// Your code...
// Could also be before try if you know the exception occurs in SaveChanges
context.SaveChanges();
}
catch (DbEntityValidationException e)
{
foreach (var eve in e.EntityValidationErrors)
{
Console.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)
{
Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"", ve.PropertyName, ve.ErrorMessage);
}
}
throw;
}
My original problem was that I was experiencing deadlocks often when updating my SQL database. Through a little bit of research, I found that I'm able to define a custom DbConfiguration and with it a DbExecutionStrategy which instructs Entity Framework to automatically retry after getting certain errors after x milliseconds and y number of times. Great!
So, following the guide at https://msdn.microsoft.com/en-us/data/jj680699, I build my custom DbConfiguration, which is being used, but the associated DbExecutionStrategy seems to be ignored.
Originally, my entire DbConfiguration was being ignored, but I found that was because I was referencing it in my app.config as well as decorating my entity constructor with the DbConfigurationType attribute [DbConfigurationType(typeof(MyConfiguration))]. Now that I'm only using the app.config, at least my custom configuration is being called.
In its simplest form, my custom config looks like this:
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
System.Windows.MessageBox.Show("Hey! Here I am!"); //I threw this in just to check that I was calling the constructor. Simple breakpoints don't seem to work here.
SetExecutionStrategy("System.Data.SqlClient", () => new MyExecutionStrategy(3, TimeSpan.FromMilliseconds(500)));
}
}
My custom DbConfiguration is referenced in my app.config like so:
<entityFramework codeConfigurationType="MyDataLayer.MyConfiguration, MyDataLayer">
...
</entityFramework>
My custom DbExecutionStrategy is built like so:
private class MyExecutionStrategy : DbExecutionStrategy
{
public MyExecutionStrategy() : this(3, TimeSpan.FromSeconds(2))
{
System.Windows.MessageBox.Show($"MyExecutionStrategy instantiated through default constructor.");
}
public MyExecutionStrategy(int maxRetryCount, TimeSpan maxDelay) : base(maxRetryCount, maxDelay)
{
System.Windows.MessageBox.Show($"MyExecutionStrategy instantiated through parametered constructor.");
}
protected override bool ShouldRetryOn(Exception ex)
{
System.Windows.MessageBox.Show($"Overriding ShouldRetryOn.");
bool retry = false;
SqlException sqlException = GetSqlException(ex);
if (sqlException != null)
{
int[] errorsToRetry =
{
1205, //Deadlock
-2 //Timeout
};
if (sqlException.Errors.Cast<SqlError>().Any(x => errorsToRetry.Contains(x.Number)))
{
retry = true;
}
}
if (ex is TimeoutException)
{
retry = true;
}
return retry;
}
}
I'm not hitting anything at all in this particular piece of the code.
One thing that may be of note, is that every example I've seen so far (for example http://blog.analystcircle.com/2015/08/01/connection-resiliency-in-entity-framework-6-0-and-above/) has casted the exception in ShouldRetryOn directly to a SqlException using
SqlException sqlException = ex as SqlException;
I found that using this method always resulted in a null SqlException because my program is throwing an EntityException which can't be cast into a SqlException. My underlying SqlException is actually the inner exception of the inner exception of the EntityException. So, I put together a short recursive call to dig in and find it.
private SqlException GetSqlException(Exception ex)
{
SqlException result = ex as SqlException;
if (result == null && ex.InnerException != null)
result = GetSqlException(ex.InnerException);
return result;
}
This works properly, but the fact that I need to do it when the examples I've found don't is probably a clue as to what's going wrong. Do EntityExceptions not trigger a DbExecutionStrategy? If not, why is this listed as a solution to be used with EF 6? Any insight would be much appreciated.
EDIT:
Doing some more digging into the source for DbExecutionStrategy (https://github.com/aspnet/EntityFramework6/blob/master/src/EntityFramework/Infrastructure/DbExecutionStrategy.cs), I've found that my recursive function to find my SqlException from the EntityException is unnecessary. DbExecutionStrategy has a function UnwrapAndHandleException which does just that and passes the SqlException on to ShouldRetryOn. So, it seems I'm right back at square one.
EDIT 2:
Not really a solution, because it doesn't explain why my DbExecutionStrategy isn't being called as it should, but I have found that if I explicitly call the execution strategy, it works.
The code to use the execution strategy explicitly is:
var executionStrategy = new MyConfiguration.MyExecutionStrategy();
executionStrategy.Execute(
() =>
{
//build your context and execute db functions here
using (var context = new Entities())
{
...do stuff
}
});
Probably way too old by now but in case anyone is having the same issues:
exception.GetBaseException() gets you the root cause of any exception. No need for recursion
I'm able to get this to work using EF 6.4.0
I'm developing with C#, ASP.NET MVC Web Api, Entity Framework and .NET Framework 4.0.
I have this code to log an exception:
public void LogCompleteException(
string controllerName,
string actionName,
Exception exception)
{
string exceptionMessage = string.Empty;
Exception e = exception;
if (e.InnerException == null)
e = null;
else
while (e.InnerException != null) e = e.InnerException;
if (e == null)
exceptionMessage = exception.Message;
else
exceptionMessage = string.Format("{0}\n\rInnerException: {1}", exception.Message, e.Message);
_logger.ErrorFormat(
LogTextFormatString,
ExceptionText,
controllerName,
actionName,
exceptionMessage);
}
But on my log file I have found this:
Validation failed for one or more entities. See
'EntityValidationErrors' property for more details.
When I wrote 'See 'EntityValidationErrors' property for more details.', I'm only showing an example where I haven't log an important property.
There are a lot of kind of exceptions; but with my method I'm not logging all the relevant information because there could be properties like 'EntityValidationErrors' that I don't log.
When I pass the exception to log it I don't know what properties it has, and I don't know how to log each property it has.
Do you know a method to log an exception completely? My code doesn't long exceptions with an EntityValidationErrors property or any other important property.
I'm doing the logging with log4net.
Since the inner exception is an exception itself, perhaps you can just recurse and reuse the method you already have:
if (e.InnerException != null)
{
LogCompleteException(controllerName, actionName, e.InnerException);
}
If this does work, however, you will still be missing the EntityValidationErrors.
Another method, which I used recently is to just explicitly trap and log the exception where it occurs:
try
{
db.Add(something);
db.SaveChanges();
}
catch (DbEntityValidationException ex)
{
StringBuilder sb = new StringBuilder();
// collect some extra info about the exception before logging
foreach (var eve in ex.EntityValidationErrors)
{
sb.AppendLine(String.Format("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)
{
sb.AppendLine(String.Format("Property: \"{0}\", Error: \"{1}\"", ve.PropertyName, ve.ErrorMessage));
}
}
logger.Error("There was an error while trying to parse and save something!\n" + sb.ToString(), ex);
}
If you want the Exception.Data dictionary entries, you can add those as well:
foreach (DictionaryEntry entry in ex.Data)
{
// you will want to use a StringBuilder instead of concatenating strings if you do this
exceptionMessage = exceptionMessage + string.Format("Exception.Data[{0}]: {1}", entry.Key, entry.Value);
}
As for properties for Custom exception classes just as the EntityValidationErrors, I would just trap and parse those where they occur. Otherwise you would have to override ToString() on every exception type or use some reflection hackery to enumerate all the properties which would significantly clutter the logs with properties you dont care about.
you could use elmah and the log4net appender. Elmah logs catches all exceptions and can log them to a log4net instance.
I have a process that is parsing an XML file.
This is occuring in the PAckage Class.
The Package class has a Delegate that sets the object to an invalid state and captures the detailed info on error that occured the Package Class
For simplicity I am showing the filitem being passed to the package..
i.e `
foreach( var package in Packages)
{
try
{
package.ProcessXml(fileitem.nextfile);
}
catch (CustomeErrorException ex)
{
Logger.LogError(ex)
}
}
Inside The Package my validations look something like this
var Album = xml.Descendants()
.Select(albumShards => new Album {
Label = (string)albumShards.Descendants(TempAlbum.LabelLoc).FirstOrDefault() == "" ?
FailPackage("Error on label Load",Componets.Package,SubComp.BuildAlbum ) : (string)albumShards.Descendants(TempAlbum.LabelLoc).FirstOrDefault()
On this validation I check to see if "" is returned for label... if so Call Failpackage with error info and create exception
protected override void FailPackage(string msg, LogItem logItem)
{
Valid = ProcessState.Bad;
Logger.LogError(msg,logItem);
throw CustomErrorException(msg, Logitem);
}
that is captured via the containing try catch block
My concern is that I am using Exceptions for Program flow ... how else should I look at approaching this problem or is this a valid Pattern.
ProcessXml fails to do what its name implies in certain cases; these are error cases. Exceptions are for error handling, despite what the name implies.
One of the biggest misconceptions about exceptions is that they are
for “exceptional conditions.” The reality is that they are for
communicating error conditions.
Krzysztof Cwalina, Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries
In other words, you're in the right. :)
Read the chapter on exceptions in the above book for some excellent guidelines.
You can put the error along with the ProcessState:
foreach( var package in Packages)
{
package.ProcessXml(fileitem.nextfile);
if(!package.Valid)
Logger.LogError(package.Error)
}
var albumShards = xml.Descendants().FirstOrDefault();
if((string)albumShards.Descendants(TempAlbum.LabelLoc).FirstOrDefault() == "")
return FailPackage("Error on label Load",Componets.Package,SubComp.BuildAlbum );
album = (string)albumShards.Descendants(TempAlbum.LabelLoc);
protected override void FailPackage(string msg, LogItem logItem)
{
Valid = ProcessState.Bad;
Logger.LogError(msg,logItem);
Error = msg;
}
hi i would like to return the group data back to the ui layer.
here is how i want to do a simplify the groupby "GenerationDate" and return the data in List<> back to UI gridview.
i find it very troublesome as i got to do the forloop. Also in the UI layer i got to do another forloop for this simple groupby. Could you help to simplify it?
public List<SalaryTracker> GetSalaryTrackerOrderByGenerationDate(int tutorId)
{
List<SalaryTracker> salary = new List<SalaryTracker>();
using (leDataContext db = new leDataContext())
{
try
{
var r =
from s in db.SalaryTrackers
where s.StaffId == 2 && s.PaymentDate == null
group s by s.GenerationDate into g
where g.Count() > 0
select new
{
date = g.Key, totalSalary = g.Sum(p => p.SalaryAmount)
};
foreach (var rr in r)
{
SalaryTracker m = new SalaryTracker();
m.GenerationDate = rr.date;
m.SalaryAmount = rr.totalSalary;
salary.Add(m);
}
return salary;
}
catch (Exception ex)
{
Logger.Error(typeof(SalaryTracker), ex.ToString());
throw;
}
}
}
--------------- GUI
totalCommissionsGroup = salary.GetSalaryTrackerOrderByGenerationDate(tutor.Id);
IList<SalaryTracker> rr = (
totalCommissionsGroup.GroupBy(x => x.GenerationDate)
.Select(g => new SalaryTracker
{
MonthId = g.Key.Month,
MonthToPay = common.GetMonthName(Convert.ToInt16(g.Key), true),
SalaryAmount = g.Sum(x => x.SalaryAmount)
})).ToList<SalaryTracker>();
gvSalaryPayment.DataSource = rr;
i do this so that i can get the MonthToPay in string
public List<SalaryTracker> GetSalaryTrackerOrderByGenerationDate(int tutorId)
{
using (var db = new leDataContext())
{
try
{
return (
from s in db.SalaryTrackers
where s.StaffId == 2 && s.PaymentDate == null
group s by s.GenerationDate into g
select new
{
MonthId = g.Key.Month,
// I don't know what "common" is in your UI code,
// I am just using GetMonthName here
MonthToPay = GetMonthName(Convert.ToInt16(g.Key), true),
SalaryAmount = g.Sum(p => p.SalaryAmount)
})
.AsEnumerable()
.Select(x => new SalaryTracker
{
MonthId = x.MonthId,
MonthToPay = x.MonthToPay,
SalaryAmount = x.SalaryAmount
})
.ToList();
}
catch (Exception ex)
{
Logger.Error(typeof(SalaryTracker), ex.ToString());
throw;
}
}
}
I agree with the refactoring of Mahesh Velaga (+1 for that) and I like to add that logging errors at that level of the application is unusual. Especially when you decide to rethrow the exception any way. Therefore, I suggest that you remove the complete try catch with error logging and log only in the root of your application (if you're not already doing that). That makes your code much more cleaner.
When you've done that, you'll see that what's left is a nice readable method with hardly anything else than business logic.
UPDATE
But if we do not put the 'try catch'
in the datamodel layer, how would the
main caller catch the error? i have
been using this pattern for years..
please correct me if i am wrong. Refer
to this link.
Catching the way you do in your question or is done as is shown in the referenced article you provide is almost always sub optimal, because of several reasons:
Catching, logging and rethrowing at the service layer is unfortunate, because you will end up with double entries of this error in your logging system, simply, because you need to log at the top layer of your application anyway, because otherwise you will miss logging errors that do not originate from the service layer. Ignoring the error (by not rethrowing it) is also a bad idea, because the client should hardly ever continue when an error occurs.
Catching at the presentation layer as shown in the referenced article is also unfortunate, because this way you don't log the errors, but more importantly, you present the user with possibly technical and obscure error messages (perhaps even in a foreign language) that they don't understand and it will frustrate them. Even worse, it could lead to information leakage that allows hackers to see what going on under the surface when attacking your application (in case of a publicly exposed web application).
This doesn't mean that you can't display any error information to the users, but only do that for exceptions that you thrown explicitly to present error information for the users. For instance:
try
{
// Call into service layer
}
catch (BusinessLayerException ex)
{
this.ValidationSummary1.Add(new CustomValidator
{
ErrorMessage = ex.Message, IsValid = false
});
}
In this example, the BusinessLayerException is a special exception that is thrown from the business layer that contains error messages that are understandable for the user (possibly with a localized error message if your application is used by users of multiple languages).
For all other exceptions, most of the time you don't want to show the user that exact error message, because you have no idea what went wrong and how severe the error is and what information the exception contains. The best thing to do is to have as less error handling logic as possible in your presentation layer, and handle this at one place in the top layer of the application. Here you ensure that an error page is displayed to the user in that case and you ensure that the error is logged to the logging system.
You can configure ASP.NET to show a custom error page in case of an error. Here's an example:
<customErrors mode="RemoteOnly"
defaultRedirect="~/ErrorPage.htm">
</customErrors>
The ErrorPage.htm can display general information like the general "it's our fault" error page here at Stackoverflow. Perhaps some contact information of support, your home phone number, so they can call you at night ;-)
By implementing the Application_Error method in the global.asax, you can at a single place in the application log unhandled exceptions:
void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
// Log the exception here.
}
Make sure you log al the information about the error you need, such as stack trace, error message, exception type, and all the inner exceptions that can occur.
I hope this helps.