Не вдалося перевірити для однієї або декількох сутностей, зберігаючи зміни в базі даних SQL Server за допомогою Entity Framework


337

Я хочу зберегти свою Правка в базі даних, і я використовую Entity FrameWork Code-First в ASP.NET MVC 3 / C #, але я отримую помилки. У моєму класі подій у мене є типи даних DateTime та TimeSpan, але в моїй базі даних є дата та час відповідно. Чи може це бути причиною? Як я можу передати відповідний тип даних у коді перед збереженням змін у базі даних.

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 \ Контролери \ EventManagerController.cs Рядок: 77

Слід стека:

[DbEntityValidationException: Не вдалося перевірити для однієї або декількох сутностей. Докладніше див. У властивості EntityValidationErrors.]


8
ймовірно, що в одному з необхідних полів є нульове значення. Як EventDate, StartTime, ціна, категорія тощо
Daveo

Ви перевіряли розміщені змінні форми, щоб переконатися, що кожна збігається із визначеним типом бази даних? Або як те, що сказав Давео, одне з необхідних значень форми відсутнє ...
timothyclifford

Не всі розміщені форми розміру відповідають типу, визначеному базою даних. Я використовував дату та час у базі даних, але в .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

Якщо ви не перебуваєте у спробі / лові або не маєте доступу до об’єкта винятку.

Це дозволить вам спуститися в ValidationErrorsдерево. Це найпростіший спосіб я зрозуміти ці помилки.


6
Ти, мій друг, геній, що ти мені допоміг простежити помилку ET, яку я отримував, дякую!
Марк Крам

4
Без проблем :) Але не геній, просто люблю QuickWatch :)
GONeale

4
Щойно оновлена ​​відповідь для тих, хто не має доступу до об'єкта / змінної винятку.
GONeale

7
Кожен раз, коли я отримую цей виняток, я шукаю помилку, тоді я приходжу сюди (другий результат в 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);
        }
    }

Виняток клас "Хелпер":

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

Вам потрібно дочекатися виклику SaveChangesAsync, якщо ви хочете обробити виняток.
Арнаб Чакраборті

Спасибі, Арнаб Чакраборті ! Відповідь фіксованою
Sel

5

Цей код допоміг знайти мою проблему, коли у мене виникли проблеми з моєю помилкою Entity VAlidation Erros. Це розповіло мені точну проблему з моїм визначенням сутності. Спробуйте наступний код, де потрібно прикрити 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

Я отримував цю помилку сьогодні і не міг її виправити деякий час, але зрозумів, що це було після додавання деяких RequireAttributes до моїх моделей і що деякі дані насіннєвих розробників не заповнюють усі необхідні поля.
Отже, лише зауважте, що якщо ви отримуєте цю помилку під час оновлення бази даних через якусь стратегію init, наприклад, 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() метод і ви отримаєте список, розділений комами, що містить усі помилки перевірки сутності.

це також може покращитись, щоб увімкнути помилки у виробництві env, а не просто видавати помилку.

сподіваюся, що це корисно.


Де я можу знайти "клас DbContext"? Я якось новий у всіх цих речах MVC. Я використовую MVC 5 та Entity Framework 6. Просто не знаю, яке ім'я хотілося б у моєму Solution Explorer.
JustJohn

@JustJohn - це у вашій моделі бази даних (файл класу), якщо ви не знаєте, де це, ви можете зробити повний пошук за ключовим словом у відповідності DbContextз вашим проектом.
Чтіві Малек

1
Мені подобається цей підхід найкраще, оскільки це універсальне рішення в додатку і дозволяє легко розкрити те, що знаходиться під поверхнею, не змінюючи багато для будь-яких інших модулів у системі
Korayem

3

Ось розширення до розширення Тоні ... :-)

Для Entity Framework 4.x, якщо ви хочете отримати ім'я та значення ключового поля, щоб ви знали, який екземпляр сутності (запис DB) має проблему, ви можете додати наступне. Це забезпечує доступ до більш потужних членів класу 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;

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 перед збереженням у вашій БД.


2

Переконайтеся, що якщо у рядку БД є nvarchar (50), ви не намагаєтеся вставити в нього більше 50 символів. Дурна помилка, але мені знадобилося 3 години, щоб зрозуміти це.


1

Це може бути пов’язано з максимальною кількістю символів, дозволеною для конкретного стовпця, наприклад, у sql одне поле може мати такий тип даних, nvarchar(5)але кількість символів, введених користувачем, більше зазначеної, отже, виникає помилка.


1

Я зіткнувся з таким же питанням пару днів тому під час оновлення бази даних. У моєму випадку для обслуговування було додано кілька нових ненульованих стовпців, які не були надані в коді, що викликає виняток. Я з'ясував ці поля та подав значення для них та вирішив їх.


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

0

це може бути викликано властивістю, яка не заповнюється моделлю .. замість цього заповнюється контролером .. що може спричинити цю помилку .. рішенням цього є призначення властивості перед застосуванням валідації ModelState. і це друге Успіння. можливо у вас вже є дані у вашій базі даних і намагаєтесь її оновлювати, але тепер виймаєте їх.


0

У моєму випадку я маю Шлях стовпчика таблиці Таблиця, для якого я встановив тип даних varchar (200). Після оновлення до nvarchar (max) я видалив таблицю з edmx, а потім знову додав таблицю, і вона прокинулася належним чином для мене.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.