2013-04-04 3 views
194

У меня есть проект, который использует Entity Framework. Призывая SaveChanges на моем DbContext, я получаю следующее исключение:DbEntityValidationException - Как я могу легко объяснить, что вызвало ошибку?

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

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

Как я могу увидеть детали, скрытые в пределах DbEntityValidationException?

ответ

388

Самое легкое решение - переопределить SaveChanges на класс объектов. Вы можете поймать DbEntityValidationException, развернуть фактические ошибки и создать новый DbEntityValidationException с улучшенным сообщением.

  1. Создайте частичный класс рядом с вашим файлом SomethingSomething.Context.cs.
  2. Используйте код в нижней части этой публикации.
  3. Вот и все. Ваша реализация автоматически будет использовать переопределенные SaveChanges без какой-либо работы с рефактором.

Ваше сообщение исключение теперь будет выглядеть следующим образом:

System.Data.Entity.Validation.DbEntityValidationException: Проверка Сбой одного или нескольких объектов. Дополнительную информацию см. В разделе «Свойство EntityValidationErrors» . Ошибки проверки: Поле PhoneNumber должно быть типом строки или массива с максимальной длиной «12»; Поле LastName не требуется.

Вы можете оставить переопределенном SaveChanges в любом классе, который наследуется от DbContext:

public partial class SomethingSomethingEntities 
{ 
    public override int SaveChanges() 
    { 
     try 
     { 
      return base.SaveChanges(); 
     } 
     catch (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); 
     
      // Throw a new DbEntityValidationException with the improved exception message. 
      throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors); 
     } 
    } 
} 

DbEntityValidationException также содержит объекты, которые вызвали ошибки проверки. Поэтому, если вам требуется еще больше информации, вы можете изменить приведенный выше код для вывода информации об этих объектах.

Смотрите также: http://devillers.nl/improving-dbentityvalidationexception/

+6

Сгенерированный класс объектов уже наследуется от DbContext, поэтому вам не нужно добавлять его снова в частичный класс. Вы не будете сломать или изменить что-либо, добавив его в частичный класс. Фактически, если вы добавите наследование из DbContext, Resharper предложит вам удалить его: «Базовый тип« DbContext »уже определен в других частях». –

+10

Почему это поведение по умолчанию SaveChanges не по умолчанию? –

+3

«Почему это не поведение по умолчанию SaveChanges?» - Это действительно хороший вопрос. Это было удивительное решение, это спасло меня! Мне пришлось бросить «using System.Linq;» –

40

Как указано Мартин, есть больше информации в DbEntityValidationResult. Я счел полезным получить оба имени и имени класса POCO в каждом сообщении и хотел бы избежать необходимости писать настраиваемые атрибуты ErrorMessage во всех моих тегах [Required] только для этого.

Следующая твик для кода Мартина позаботился эти детали для меня:

// Retrieve the error messages as a list of strings. 
List<string> errorMessages = new List<string>(); 
foreach (DbEntityValidationResult validationResult in ex.EntityValidationErrors) 
{ 
    string entityName = validationResult.Entry.Entity.GetType().Name; 
    foreach (DbValidationError error in validationResult.ValidationErrors) 
    { 
     errorMessages.Add(entityName + "." + error.PropertyName + ": " + error.ErrorMessage); 
    } 
} 
+1

Использование 'SelectMany и Aggregate' в [github] (https://github.com/DaringCoders/DaringCore/blob/14a3e6e694890da128c94e635c2eb3e41e587bd9/DaringCore.EF/EntityFrameworkExceptionHelper.cs) by _Daring Coders_ – Kiquenet

-2

Используйте TRY блок в коде, как

try 
{ 
    // Your code... 
    // Could also be before try if you know the exception occurs in SaveChanges 

    context.SaveChanges(); 
} 
catch (DbEntityValidationException e) 
{ 
    foreach (var eve in e.EntityValidationErrors) 
    { 
     Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:", 
      eve.Entry.Entity.GetType().Name, eve.Entry.State); 
     foreach (var ve in eve.ValidationErrors) 
     { 
      Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"", 
       ve.PropertyName, ve.ErrorMessage); 
     } 
    } 
    throw; 
} 

Вы можете проверить детали здесь, а

  1. http://mattrandle.me/viewing-entityvalidationerrors-in-visual-studio/

  2. Validation failed for one or more entities. See 'EntityValidationErrors' property for more details

  3. http://blogs.infosupport.com/improving-dbentityvalidationexception/

+0

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

+0

Так что, пытаясь помочь кому-то с правильной ссылкой, проблема здесь? –

+0

Да, ваш ответ не должен содержать только ссылки. Сделайте резюме, которое отвечает на вопрос, а затем опубликуйте ссылку в конце для дальнейшего чтения. – ChrisO

38

Для просмотра EntityValidationErrors коллекции, добавить следующее выражение Watch в окне Watch.

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

Я использую Visual Studio 2013

+0

$ Исключительно! это означает, что в непосредственном окне я могу сделать $ exception.EntityValidationErrors.SelectMany (x => x.ValidationErrors) .Выберите (x => x.ErrorMessage); – chrispepper1989

2

Я думаю, что «фактические ошибки проверки» может содержать конфиденциальную информацию, и это может быть причиной того, почему Microsoft решила поместить их в другом месте (свойства). Решение, обозначенное здесь, практично, но его следует принимать с осторожностью.

Я бы предпочел создать метод расширения. Другие причины для этого:

  • Keep оригинальных трассировки стека
  • Следуйте открытым/закрытый принцип (т.е. .: Я могу использовать различные сообщения для различного рода журналов)
  • В производственных условиях может быть и другими местами (например: other dbcontext), где может быть выбрано исключение DbEntityValidationException.
12

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

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

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

Для визуальных 2012+ пользователей, которые заботятся только о первой ошибке и не может иметь catch блока, вы даже можете сделать:

((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors.First().ValidationErrors.First().ErrorMessage 
7

Чтобы быстро найти осмысленное сообщение об ошибке при проверке ошибки во время отладки:

  • Добавить быстрые часы для:

    ((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors 
    
  • Сверните в EntityValidationErrors, как это:

    (элемент коллекции, например. [0])> ValidationErrors> (коллекционная вещь, например, [0])> ErrorMessage

4

На самом деле, это просто вопрос проверки, EF будет проверять свойства сущностей первого до внесения изменений в базу данных. Итак, EF проверит, вне зависимости от того, находится ли значение свойства, например, при создании таблицы. Table_Column_UserName - varchar (20). Но в EF вы указали значение, превышающее 20. Или, в других случаях, если столбец не может быть Null. Итак, в процессе проверки вам нужно установить значение для столбца, отличного от нуля, независимо от того, собираетесь ли вы изменить его. Я лично, как ответ Leniel Macaferi. Он может показать вам детали вопросов валидации.

Смежные вопросы