Validation failed for one or more entities while saving changes to SQL Server Database using Entity Framework
Asked Answered
P

17

339

I want to save my Edit to Database and I am using Entity FrameWork Code-First in ASP.NET MVC 3 / C# but I am getting errors. In my Event class, I have DateTime and TimeSpan datatypes but in my database, I've got Date and time respectively. Could this be the reason? How can I cast to the appropriate datatype in the code before saving changes to database.

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; }
}

Method in the controller >>>> Problem at 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);
    }
}

with

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 Database / T-SQL

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  

Server Error in '/' Application.

Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Data.Entity.Validation.DbEntityValidationException: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.

Source Error:

Line 75:             if (TryUpdateModel(theEvent))
Line 76:             {
Line 77:                 storeDB.SaveChanges();
Line 78:                 return RedirectToAction("Index");
Line 79:             }

Source File: C:\sep\MvcEventCalendar\MvcEventCalendar\Controllers\EventManagerController.cs Line: 77

Stack Trace:

[DbEntityValidationException: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.]

Pasol answered 23/3, 2011 at 3:18 Comment(4)
probably one of your required fields has a null value. Like EventDate , StartTime , Price, Category etcSpickandspan
Have you inspected form variables being posted to ensure each matches the database defined type? Or like what Daveo said, one of the required form values is missing...Renaldo
Not all the posted form variables match with the database defined type. I have used date and time in the database but there is no direct datatype equivalent in .NET. Therefore, I used DateTime and TimeSpan. Now I need to convert the two to date and time respectively.Pasol
The source of error for me was a string field which has more than 30 chars and my field size in the database was 30.Shanley
P
837

You can extract all the information from the DbEntityValidationException with the following code (you need to add the namespaces: System.Data.Entity.Validation and System.Diagnostics to your using list):

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);
        }
    }
}
Procurable answered 6/6, 2011 at 21:22 Comment(11)
You can also get this error if any seed data is not fully satisfying model attribute rules (like Required). I've added an answer with a bit more info.Nemeth
The answer could be improved by explaining where you can actually see the trace output.Dromedary
I found this msdn article about trace being very usefulMayapple
You sir, won the internet.Begum
Fantastic, but rather than trace I just used string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage)Gilgamesh
i got this error when after importing System.Data.Entity.Validation The type caught or thrown must be derived from System.ExceptionMercy
The Trace output can be seen in the Output window. Click Debug at the top -> Windows -> OutputKhalil
My error happens during the _db.SaveChanges() line, which is before the catch block that I pasted into my "Create" controller Action Result. So I can't get to any Trace stuff. try{_db.MyModel.Add(mymodel; _db.SaveChanges()}Pesticide
I can't see the output even though I press F5 and have the Debug->Windows->Output opened. After closing my debug session the output still does not appear. Anyway I was able to hover over the properties and follow them to see what cause the error. That being said. Up vote and a big thank you! You probably saved me a lot of search.Munger
or in linq - errMsg = dbEx.EntityValidationErrors.SelectMany(validationErrors => validationErrors.ValidationErrors).Aggregate(errMsg, (current, validationError) => current + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage));Virgievirgil
you can use MesssageBox to get an error also: MessageBox.Show("Property: " + validationError.PropertyName + " Error: " + validationError.ErrorMessage);Dumfound
L
249

No code change required:

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

or:

((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors

If you are not in a try/catch or don't have access to the exception object.

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.

Lithuanian answered 25/8, 2011 at 1:25 Comment(10)
You my friend are a genius, that you you helped me trace down the ET error I was receiving, Thank you!Snare
No problem :) But not a genius, just love QuickWatch :)Lithuanian
Just updated the answer for those who don't have access to the exception object/variable.Lithuanian
Every time I get this exception I search for the error, then I come here (2nd result at Google), I find this post and I use the second solution in the Debug > Watch panel. Did dozens of times. Thank you GONeale.Setting
Am I the only one who gets "The name '$exception' does not exist in the current context" on running the second query?Hassett
@Klaus that appears to be a local that won't be there unless you're debugging in an exception handling context. Haven't gone looking for documentation, too busy using it.Bayonne
Thanks a lot @Lithuanian , this was really killing me...Saved my whole day.Thanks againSnazzy
@Klaus try changing the expression to match the name that you gave your exception instance. So if named you exception instance e like this catch(Exception e) { }, then you would put the following into QuickWatch: ((System.Data.Entity.Validation.DbEntityValidationException)e).EntityValidationErrors. As you can see, I changed $exception to ePrimavera
Thanks for the answer, but what if i don't want to insert a try catch sentence?Inkling
@srebella I believe the second entry will then work regardless.Lithuanian
C
37

In the case you have classes with same property names, here is a small extension to Praveen's answer:

 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);
       }
    }
 }
Chatelain answered 26/2, 2012 at 19:10 Comment(0)
H
22

As an improvement to both Praveen and Tony, I use an override:

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...
        }
    }
}
Holmun answered 9/8, 2012 at 14:36 Comment(0)
Y
6

This implementation wrap entity exception to exception with detail text. It handles DbEntityValidationException, DbUpdateException, datetime2 range errors (MS SQL), and include key of invalid entity in message (useful when savind many entities at one SaveChanges call).

First, override SaveChanges in DbContext class:

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);
    }
}
Ylangylang answered 30/11, 2016 at 6:55 Comment(2)
You should await the SaveChangesAsync call, if you want to handle the exception.Marteena
Thanks, Arnab Chakraborty! Answer fixedYlangylang
V
5

This code helped find my problem when I had issue with my Entity VAlidation Erros. It told me the exact problem with my Entity Definition. Try following code where you need to cover storeDB.SaveChanges(); in following try catch block.

  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;
}
Voyage answered 10/4, 2016 at 0:50 Comment(0)
N
4

I was getting this error today and couldn't work it out for a while, but I realised it was after adding some RequireAttributes to my models and that some development seed data was not populating all of the required fields.
So just a note that if you're getting this error whilst updating the database through some sort of init strategy like DropCreateDatabaseIfModelChanges then you have to make sure that your seed data fulfils and satisfies any model data validation attributes.

I know this is slightly different to the problem in the question, but it's a popular question so I thought I'd add a bit more to the answer for others having the same issue as myself.
Hope this helps others :)

Nemeth answered 13/12, 2012 at 10:51 Comment(0)
C
4

I think adding try/catch for every SaveChanges() operation is not a good practice, it's better to centralize this :

Add this class to the main DbContext class :

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);
    }
}

This will overwrite your context's SaveChanges() method and you'll get a comma separated list containing all the entity validation errors.

this also can improved, to log errors in production env, instead of just throwing an error.

hope this is helpful.

Chant answered 18/4, 2015 at 18:39 Comment(3)
Where might I find the "DbContext Class"? I'm kinda new at all this MVC stuff. I'm using MVC 5 and Entity Framework 6. Just don't know what the name would like like in my Solution Explorer.Pesticide
@Pesticide it's in your database model (class file), if you dont know where it is you can do a full search of the keyword DbContext against your project.Chant
I like this approach the best as it's universal solution in the app and reveals what's beneath the surface easily without changing much for any other modules in the systemBlowing
J
3

Here's an extension to Tony's extension... :-)

For Entity Framework 4.x, if you want to get the name and value of the key field so that you know which entity instance (DB record) has the problem, you can add the following. This provides access to the more powerful ObjectContext class members from your DbContext object.

// 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;
Jellybean answered 14/4, 2012 at 9:38 Comment(0)
W
3

I dont like exceptions I registered the OnSaveChanges and have this

var validationErrors = model.GetValidationErrors();

var h = validationErrors.SelectMany(x => x.ValidationErrors
                                          .Select(f => "Entity: " 
                                                      +(x.Entry.Entity) 
                                                      + " : " + f.PropertyName 
                                                      + "->" + f.ErrorMessage));
Wellesley answered 19/7, 2012 at 9:16 Comment(2)
What do you mean by saying "I registered the OnSaveChanges"?Portillo
I hooked up to the OnSaveChanges on the Context same as the others, i just didn't reach the exception stage.\Wellesley
P
2

This error also happens when you try to save an entity that has validation errors. A good way to cause this is to forget to check ModelState.IsValid before saving to your DB.

Pedicel answered 20/11, 2013 at 19:24 Comment(0)
L
2

Make sure that if you have nvarchar(50)in DB row you don't trying to insert more than 50characters in it. Stupid mistake but took me 3 hours to figure it out.

L answered 11/9, 2014 at 8:52 Comment(0)
P
1

Thnaks for your answers, it help me alot. as i code in Vb.Net, this Bolt code for 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
Powell answered 2/4, 2013 at 16:35 Comment(0)
V
1

it may caused by Property which is not populated by model.. instead it is populated by Controller.. which may cause this error.. solution to this is assign the property before applying ModelState validation. and this second Assumption is . you may have already have Data in your Database and trying to update it it but now fetching it.

Volitant answered 13/4, 2016 at 16:43 Comment(0)
F
1

This might be due to the maximum number of characters allowed for a specific column, like in sql one field might have following Data Type nvarchar(5) but the number of characters entered from the user is more than the specified, hence the error arises.

Flatto answered 1/6, 2016 at 8:0 Comment(0)
P
1

I have faced same issue a couple of days ago while updating the database. In my case, there was few new non nullable columns added for maintenance which was not supplied in the code which is causing the exception. I figure out those fields and supplied values for them and its resolved.

Psephology answered 5/2, 2017 at 3:40 Comment(0)
N
1

In my case I have a Table Column name Path which datatype i set was varchar(200).After updating it to nvarchar(max), I have deleted the table from edmx and then again added the table and it wokred properly for me.

Nymphomania answered 20/2, 2019 at 11:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.