ChatGPT解决这个技术问题 Extra ChatGPT

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

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

probably one of your required fields has a null value. Like EventDate , StartTime , Price, Category etc
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...
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.
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.

M
Marcus Mangelsdorf

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

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.
The answer could be improved by explaining where you can actually see the trace output.
I found this msdn article about trace being very useful
You sir, won the internet.
The Trace output can be seen in the Output window. Click Debug at the top -> Windows -> Output
G
GONeale

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.


You my friend are a genius, that you you helped me trace down the ET error I was receiving, Thank you!
No problem :) But not a genius, just love QuickWatch :)
Just updated the answer for those who don't have access to the exception object/variable.
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.
Am I the only one who gets "The name '$exception' does not exist in the current context" on running the second query?
M
Michael0x2a

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

B
Bolt Thunder

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

S
Sel

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

You should await the SaveChangesAsync call, if you want to handle the exception.
Thanks, Arnab Chakraborty! Answer fixed
Р
Рахул Маквана

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

d
dan richardson

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 :)


C
Chtioui Malek

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.


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 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.
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
M
Martin_W

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;

j
johnny 5

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

What do you mean by saying "I registered the OnSaveChanges"?
I hooked up to the OnSaveChanges on the Context same as the others, i just didn't reach the exception stage.\
l
laylarenee

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.


u
user4030144

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.


j
johnny 5

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

M
Muhammad Mustafa

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.


l
lokopi

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.


J
Jhabar

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.


S
Saket Singh

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.


关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now