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

337

Я хочу сохранить свою правку в базу данных и использую 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 для более подробной информации.]

user522767
источник
8
вероятно, одно из ваших обязательных полей имеет нулевое значение. Как EventDate, StartTime, Цена, Категория и т. Д.
Daveo
Проверяли ли вы переменные формы, чтобы убедиться, что они соответствуют определенному типу базы данных? Или как то, что сказал Давео, одно из обязательных значений формы отсутствует ...
Тимотиклиффорд
Не все опубликованные переменные формы соответствуют определенному типу базы данных. Я использовал дату и время в базе данных, но в .NET нет прямого эквивалента типа данных. Поэтому я использовал DateTime и TimeSpan. Теперь мне нужно преобразовать два в дату и время соответственно.
user522767
Источником ошибки для меня было строковое поле, которое содержит более 30 символов, а размер моего поля в базе данных был 30.
Mojtaba

Ответы:

840

Вы можете извлечь всю информацию из 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);
        }
    }
}
Правин Прасад
источник
7
Вы также можете получить эту ошибку, если какие-либо начальные данные не полностью соответствуют правилам атрибута модели (например Required). Я добавил ответ с немного большей информацией.
Дэн Ричардсон
8
Ответ можно улучшить, объяснив, где на самом деле можно увидеть вывод трассировки.
mkataja
1
Я нашел эту статью MSDN о трассировке очень полезной
Борик
2
Вы, сэр, выиграл интернет.
Леандро
8
Выход Trace можно увидеть в окне Output. Нажмите Отладка вверху -> Windows -> Вывод
reggaeguitar
249

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

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

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

или:

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

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

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

GONeale
источник
6
Вы, мой друг, гений, что вы помогли мне отследить инопланетную ошибку, которую я получил, Спасибо!
Марк Крам
4
Нет проблем :) Но не гений, просто люблю QuickWatch :)
GONeale
4
Только что обновили ответ для тех, у кого нет доступа к объекту / переменной исключения.
GONeale
7
Каждый раз, когда я получаю это исключение, я ищу ошибку, затем я прихожу сюда (2-й результат в Google), я нахожу этот пост и использую второе решение на панели «Отладка> Просмотр». Делали десятки раз. Спасибо, GONeale.
Ата С.
1
Я единственный, кто получает «Имя $ исключение не существует в текущем контексте» при выполнении второго запроса?
Алекс Клаус
37

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

 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);
       }
    }
 }
Тони
источник
22

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

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...
        }
    }
}
Болт Гром
источник
6

Эта реализация переносит исключение объекта в исключение с подробным текстом. Он обрабатывает DbEntityValidationException, DbUpdateException, datetime2ошибки диапазона (MS SQL), и включают в себя ключе недействительного лица в сообщении (полезно , когда savind многих объектов в одном 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);
    }
}
Sel
источник
Вам следует дождаться вызова SaveChangesAsync, если вы хотите обработать исключение.
Арнаб Чакраборти
Спасибо, Арнаб Чакраборты ! Ответ зафиксирован
Sel
5

Этот код помог найти мою проблему, когда у меня возникла проблема с ошибкой 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;
}
Рахул Маквана
источник
4

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

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

Дэн Ричардсон
источник
4

Я думаю, добавив попробовать / поймать для каждого 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() метод и вы получите разделенный запятыми список, содержащий все ошибки проверки сущности.

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

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

Читиви Малек
источник
Где я могу найти «Класс DbContext»? Я новичок во всем этом MVC. Я использую MVC 5 и Entity Framework 6. Просто не знаю, какое имя хотелось бы видеть в моем обозревателе решений.
JustJohn
@JustJohn находится в вашей модели базы данных (файл класса), если вы не знаете, где это, вы можете выполнить полный поиск по ключевому слову в DbContextвашем проекте.
Chtiwi Malek
1
Мне больше нравится этот подход, так как это универсальное решение в приложении, и он легко показывает, что находится под поверхностью, не сильно меняя другие модули в системе
Korayem
3

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

Для 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;
Martin_W
источник
3

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

var validationErrors = model.GetValidationErrors();

var h = validationErrors.SelectMany(x => x.ValidationErrors
                                          .Select(f => "Entity: " 
                                                      +(x.Entry.Entity) 
                                                      + " : " + f.PropertyName 
                                                      + "->" + f.ErrorMessage));
Микки Перлштейн
источник
Что вы имеете в виду, говоря "я зарегистрировал OnSaveChanges"?
Акира Ямамото
Я подключил OnSaveChanges к контексту так же, как и другие, я просто не достиг стадии исключения. \
Микки Перлштейн
2

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

laylarenee
источник
2

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

user4030144
источник
1

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

lokopi
источник
1

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

Jhabar
источник
0

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

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

Мухаммед Мустафа
источник
0

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

Сакет Сингх
источник