Ошибка проверки для одного или нескольких объектов при сохранении изменений в базе данных SQL Server с использованием Entity Framework

Я хочу сохранить свою правку в базу данных и использую Entity FrameWork Code-First в ASP.NET MVC 3 / C#, но получаю ошибки. В моем классе Event у меня есть типы данных DateTime и TimeSpan, но в моей базе данных у меня соответственно Date и Time. Может ли это быть причиной? Как я могу привести к соответствующему типу данных в коде перед сохранением изменений в базе данных.

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

Метод в контроллере >>>> Проблема у 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);
    }
}

с

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

EventDate (Datatype = date)  
StartTime (Datatype = time)  
EndTime (Datatype = time)  

Http Форма

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  

Ошибка сервера в приложении '/'

Проверка не удалась для одного или нескольких объектов. См. Свойство EntityValidationErrors для более подробной информации.

Описание: во время выполнения текущего веб-запроса произошло необработанное исключение. Пожалуйста, просмотрите трассировку стека для получения дополнительной информации об ошибке и о том, где она возникла в коде.

Сведения об исключении: System.Data.Entity.Validation.DbEntityValidationException: проверка не удалась для одного или нескольких объектов. См. Свойство EntityValidationErrors для более подробной информации.

Ошибка источника:

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

Исходный файл: C:\sep\MvcEventCalendar\MvcEventCalendar\Controllers\EventManagerController.cs Строка: 77

Трассировки стека:

[DbEntityValidationException: проверка не удалась для одного или нескольких объектов. См. Свойство EntityValidationErrors для более подробной информации.]

16 ответов

Вы можете извлечь всю информацию из DbEntityValidationException с помощью следующего кода (вам нужно добавить пространства имен: System.Data.Entity.Validation а также System.Diagnostics на ваш using список):

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

Изменение кода не требуется:

Пока вы находитесь в режиме отладки в catch {...} блок откройте окно "QuickWatch" (Ctrl+Alt+Q) и вставьте туда:

((System.Data.Entity.Validation.DbEntityValidationException)ex).EntityValidationErrors

или же:

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

Если вы не участвуете в программе try/catch или не имеете доступа к объекту исключения.

Это позволит вам углубиться в ValidationErrors дерево. Я нашел самый простой способ получить мгновенное представление об этих ошибках.

В случае, если у вас есть классы с одинаковыми именами свойств, вот небольшое расширение ответа Правина:

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

В качестве улучшения как Правина, так и Тони я использую переопределение:

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

Эта реализация переносит исключение объекта в исключение с подробным текстом. Это обрабатывает DbEntityValidationException, DbUpdateException, datetime2 ошибки диапазона (MS SQL) и включение ключа недействительной сущности в сообщение (полезно, когда сохраняется несколько сущностей одновременно) SaveChanges вызов).

Во-первых, переопределить SaveChanges в классе DbContext:

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 класс:

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

Этот код помог найти мою проблему, когда у меня возникла проблема с моей ошибкой Entity VAlidation. Он сказал мне точную проблему с моим определением сущности. Попробуйте следующий код, где вам нужно покрыть storeDB.SaveChanges(); в следующем попробуйте перехватить блок.

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

Сегодня я получал эту ошибку и некоторое время не мог ее исправить, но понял, что это после добавления некоторых RequireAttributeс моими моделями и что некоторые исходные данные разработки не заполняют все обязательные поля.
Так что просто обратите внимание, что если вы получаете эту ошибку при обновлении базы данных через какую-то стратегию инициализации, такую ​​как DropCreateDatabaseIfModelChanges Затем вы должны убедиться, что ваши начальные данные удовлетворяют и удовлетворяют любым атрибутам проверки данных модели.

Я знаю, что это немного отличается от проблемы в вопросе, но это популярный вопрос, поэтому я решил добавить немного больше к ответу для других, имеющих такую ​​же проблему, как и я.
Надеюсь, что это помогает другим:)

Я думаю, что добавление попробовать / поймать для каждого SaveChanges() операция не является хорошей практикой, лучше централизовать это:

Добавить этот класс в основной DbContext учебный класс:

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

Это перезапишет ваш контекст SaveChanges() метод, и вы получите разделенный запятыми список, содержащий все ошибки проверки сущности.

это также может быть улучшено, чтобы регистрировать ошибки в рабочей среде, а не просто выдавать ошибку.

надеюсь, что это полезно.

Я не люблю исключения, я зарегистрировал OnSaveChanges и имею это

var validationErrors = model.GetValidationErrors();

var h = validationErrors.SelectMany(x => x.ValidationErrors
                                          .Select(f => "Entity: " 
                                                      +(x.Entry.Entity) 
                                                      + " : " + f.PropertyName 
                                                      + "->" + f.ErrorMessage));

Вот расширение к расширению Тони...:-)

Для Entity Framework 4.x, если вы хотите получить имя и значение ключевого поля, чтобы вы знали, с каким экземпляром объекта (записью БД) возникла проблема, вы можете добавить следующее. Это обеспечивает доступ к более мощным членам класса ObjectContext из вашего объекта DbContext.

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

Убедитесь, что если у вас есть nvarchar(50) в строке БД, вы не пытаетесь вставить в нее более 50 символов. Глупая ошибка, но мне понадобилось 3 часа, чтобы понять это.

Эта ошибка также происходит, когда вы пытаетесь сохранить объект, имеющий ошибки проверки. Хороший способ вызвать это - забыть проверить ModelState.IsValid перед сохранением в вашей БД.

Я столкнулся с той же проблемой пару дней назад при обновлении базы данных. В моем случае было немного новых необнуляемых столбцов, добавленных для обслуживания, которые не были включены в код, который вызывает исключение. Я выяснил эти поля и предоставил значения для них и их решения.

Это может быть вызвано Свойством, которое не заполняется моделью.. вместо этого оно заполняется Контроллером.. что может вызвать эту ошибку.. Решением этой проблемы является присвоение свойства перед применением проверки ModelState. и это второе предположение. возможно, у вас уже есть данные в вашей базе данных, и вы пытаетесь обновить их, но теперь извлекаете их.

В моем случае у меня есть имя столбца таблицы Path, тип данных которого я установил как varchar(200). После обновления до nvarchar(max) я удалил таблицу из edmx, а затем снова добавил таблицу, и она работала правильно для меня.

Спасибо за ваши ответы, это очень помогает мне. как я кодирую в Vb.Net, этот код болта для 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

Это может быть связано с максимальным количеством символов, разрешенным для определенного столбца, как в sql одно поле может иметь следующий тип данных nvarchar(5) но количество символов, введенных пользователем, превышает указанное, поэтому возникает ошибка.

Другие вопросы по тегам