Problem
I’m trying to save my changes to the database with Entity Framework Code-First in ASP.NET MVC 3 / C#, but I’m getting errors. I have DateTime and TimeSpan datatypes in my Event class, but I only have Date and Time in my database. Is it possible that this is the case? Before storing changes to the database, how can I cast to the proper datatype in the code?
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; }
}
>>>> Issue with storeDB.SaveChanges() in the controller;
// 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);
}
}
with
public class EventCalendarEntities : DbContext
{
public DbSet<Event> Events { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Place> Places { get; set; }
}
T-SQL / SQL Server 2008 R2 Database
EventDate (Datatype = date)
StartTime (Datatype = time)
EndTime (Datatype = time)
Http Form
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
In the ‘/’ application, there was a server error.
One or more entities were unable to be validated. For more information, see the ‘EntityValidationErrors’ field.
During the execution of the current web request, an unhandled exception occurred. Please look at the stack trace for further details on the mistake and where it occurred in the code.
System is an exception. Data. Entity. Validation. DbEntityValidation One or more entities failed to pass validation. For more information, see the ‘EntityValidationErrors’ field.
Source Error:
Line 75: if (TryUpdateModel(theEvent))
Line 76: {
Line 77: storeDB.SaveChanges();
Line 78: return RedirectToAction("Index");
Line 79: }
C:sepMvcEventCalendarMvcEventCalendarControllers is the source file. Line 77 of EventManagerController.cs
Stack Trace:
[DbEntityValidationException: One or more entities failed to pass validation. For more information, see the ‘EntityValidationErrors’ field.]
Asked by user522767
Solution #1
With the following code (and the namespaces System.Data.Entity.Validation and System.Diagnostics added to your using list), you can extract all the information from the DbEntityValidationException:
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);
}
}
}
Answered by Praveen Prasad
Solution #2
There is no need to update the code:
Open the “QuickWatch” window (Ctrl+Alt+Q) while in debug mode within the catch… block and put the following into it:
((System.Data.Entity.Validation.DbEntityValidationException)ex).EntityValidationErrors
or:
((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
If you’re not in a try/catch block, or if you don’t have access to the exception object.
You’ll be able to drill down into the ValidationErrors tree this way. It’s the simplest method I’ve found for gaining immediate insight into these problems.
Answered by GONeale
Solution #3
Here’s a minor addition to Praveen’s answer in case you have classes with the same property names:
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);
}
}
}
Answered by Tony
Solution #4
I employ an override as a benefit to both Praveen and 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...
}
}
}
Answered by Bolt Thunder
Solution #5
This implementation uses detail text to package entity exceptions to exceptions. It can handle DbEntityValidationException, DbUpdateException, and datetime2 range issues in MS SQL, as well as include the key of an incorrect entity in the message (useful when savind many entities at one SaveChanges call).
First, in the DbContext class, override 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 class:
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);
}
}
Answered by Sel
Post is based on https://stackoverflow.com/questions/5400530/validation-failed-for-one-or-more-entities-while-saving-changes-to-sql-server-da