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.]

HonourCode
  • 318
  • 1
  • 3
  • 14
user522767
  • 4,013
  • 6
  • 21
  • 17
  • 9
    probably one of your required fields has a null value. Like EventDate , StartTime , Price, Category etc – Daveo Mar 23 '11 at 04:00
  • 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... – timothyclifford Mar 23 '11 at 04:40
  • 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. – user522767 Mar 23 '11 at 04:51
  • 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. – Mojtaba Sep 27 '16 at 16:12

17 Answers17

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);
        }
    }
}
Marcus Mangelsdorf
  • 2,852
  • 1
  • 30
  • 40
Praveen Prasad
  • 31,561
  • 18
  • 73
  • 106
  • 7
    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. – dan richardson Dec 13 '12 at 10:52
  • 8
    The answer could be improved by explaining where you can actually see the trace output. – mkataja Apr 03 '14 at 09:03
  • 1
    I found this [msdn article](http://msdn.microsoft.com/en-us/library/sk36c28t%28v=vs.110%29.aspx) about trace being very useful – Borik May 08 '14 at 00:41
  • 2
    You sir, won the internet. – Leandro Bardelli Nov 13 '14 at 20:17
  • Fantastic, but rather than trace I just used `string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage)` – tocallaghan Nov 18 '14 at 04:45
  • i got this error when after importing System.Data.Entity.Validation The type caught or thrown must be derived from System.Exception – ATHER Jan 06 '15 at 06:32
  • 8
    The Trace output can be seen in the Output window. Click Debug at the top -> Windows -> Output – reggaeguitar Mar 30 '15 at 20:35
  • 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()} – JustJohn Oct 20 '15 at 23:14
  • 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. – Matthis Kohli Jul 26 '16 at 10:58
  • 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)); – oCcSking Mar 19 '17 at 15:47
  • you can use MesssageBox to get an error also: MessageBox.Show("Property: " + validationError.PropertyName + " Error: " + validationError.ErrorMessage); – Pawel Czapski Jan 18 '19 at 10:38
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.

GONeale
  • 26,302
  • 21
  • 106
  • 149
  • 6
    You my friend are a genius, that you you helped me trace down the ET error I was receiving, Thank you! – Mark Kram Oct 24 '12 at 20:19
  • 4
    No problem :) But not a genius, just love QuickWatch :) – GONeale Oct 25 '12 at 23:20
  • 4
    Just updated the answer for those who don't have access to the exception object/variable. – GONeale Jan 24 '13 at 00:49
  • 7
    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. – Ata S. Feb 08 '14 at 13:16
  • 1
    Am I the only one who gets "The name '$exception' does not exist in the current context" on running the second query? – Alex Klaus Apr 02 '14 at 05:38
  • @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. – Tetsujin no Oni May 16 '14 at 04:30
  • Thanks a lot @GONeale , this was really killing me...Saved my whole day.Thanks again – Saroj Oct 09 '14 at 12:26
  • @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 `e` – hvaughan3 Nov 02 '15 at 14:05
  • Thanks for the answer, but what if i don't want to insert a try catch sentence? – Santiago Rebella Jul 12 '16 at 20:31
  • 1
    @srebella I believe the second entry will then work regardless. – GONeale Feb 08 '17 at 23:30
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);
       }
    }
 }
Michael0x2a
  • 58,192
  • 30
  • 175
  • 224
Tony
  • 1,566
  • 2
  • 15
  • 27
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...
        }
    }
}
Bolt Thunder
  • 745
  • 6
  • 26
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);
    }
}
Sel
  • 1,934
  • 1
  • 22
  • 13
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;
}
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 :)

dan richardson
  • 3,871
  • 4
  • 31
  • 38
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.

Chtioui Malek
  • 11,197
  • 1
  • 72
  • 69
  • 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. – JustJohn Oct 20 '15 at 23:09
  • @JustJohn 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. – Chtioui Malek Oct 21 '15 at 15:00
  • 1
    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 system – Korayem Aug 27 '16 at 18:41
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;
Martin_W
  • 1,582
  • 1
  • 19
  • 24
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));
johnny 5
  • 19,893
  • 50
  • 121
  • 195
Mickey Perlstein
  • 2,508
  • 2
  • 30
  • 37
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.

laylarenee
  • 3,276
  • 7
  • 32
  • 40
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.

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
johnny 5
  • 19,893
  • 50
  • 121
  • 195
Exatex
  • 359
  • 2
  • 11
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.

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.

lokopi
  • 101
  • 1
  • 1
  • 10
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.

Jhabar
  • 109
  • 10
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.