DbEntityValidationException-오류의 원인을 쉽게 알 수있는 방법은 무엇입니까?


217

Entity Framework를 사용하는 프로젝트가 있습니다. SaveChanges내 전화하는 동안 DbContext다음 예외가 발생합니다.

System.Data.Entity.Validation.DbEntityValidationException : 하나 이상의 엔티티에 대한 유효성 검증에 실패했습니다. 자세한 내용은 'EntityValidationErrors'속성을 참조하십시오.

이것은 모두 훌륭하고 멋지지만이 예외가 발생할 때마다 디버거를 연결하고 싶지 않습니다. 또한 프로덕션 환경에서는 디버거를 쉽게 연결할 수 없으므로 이러한 오류를 재현하기 위해 많은 시간을 투자해야합니다.

안에 숨겨진 세부 정보를 어떻게 볼 수 DbEntityValidationException있습니까?

답변:


430

가장 쉬운 해결책은 SaveChanges엔터티 클래스 에서 재정의 하는 것입니다. 을 포착하고 DbEntityValidationException실제 오류를 풀고 DbEntityValidationException개선 된 메시지 로 새 메시지를 작성할 수 있습니다 .

  1. SomethingSomething.Context.cs 파일 옆에 부분 클래스를 작성하십시오.
  2. 이 게시물의 맨 아래에있는 코드를 사용하십시오.
  3. 그게 다야. 구현시 리팩터링 작업없이 재정의 된 SaveChanges를 자동으로 사용합니다.

예외 메시지는 이제 다음과 같습니다.

System.Data.Entity.Validation.DbEntityValidationException : 하나 이상의 엔티티에 대한 유효성 검증에 실패했습니다. 자세한 내용은 'EntityValidationErrors'속성을 참조하십시오. 유효성 검사 오류는 다음과 같습니다. PhoneNumber 필드는 최대 길이가 '12'인 문자열 또는 배열 유형이어야합니다. 성 필드는 필수입니다.

다음에서 상속되는 모든 클래스에서 재정의 된 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'는 이미 다른 부분에 지정되어 있습니다."
Martin Devillers

15
이것이 왜 SaveChanges의 기본 동작이 아닙니까?
John Shedletsky

4
"이것이 왜 SaveChanges의 기본 동작이 아닙니까?" -정말 좋은 질문입니다. 이것은 놀라운 솔루션이었습니다. 시간이 절약되었습니다! 나는 던져 져야했다using System.Linq;
John John

1
try 블록에있는 base.SaveChanges ()에서 My Create View 오류가 발생했습니다. 캐치 블록으로 점프하지 않습니다. SaveChanges를 능가하는 코드를 얻었지만 오류가 발생하면 Catch Block에 들어 가지 않습니다.
JustJohn

7
스택 추적을 유지하려면 내부 예외를 설정해야합니다.
dotjoe

48

Martin이 지적했듯이,에 더 많은 정보가 있습니다 DbEntityValidationResult. 각 메시지에서 POCO 클래스 이름과 속성 이름을 모두 얻는 것이 유용하다는 것을 알았으며 ErrorMessage모든 사용자 정의 속성 을 작성하지 않아도되었습니다.[Required] 태그 했습니다.

Martin의 코드를 다음과 같이 조정하면 이러한 세부 사항을 처리했습니다.

// 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 and Aggregategithub에 의해 대담 코더
Kiquenet

43

EntityValidationErrors컬렉션 을 보려면 다음 Watch 식을 Watch 창에 추가하십시오.

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

Visual Studio 2013을 사용하고 있습니다.


$ 예외는 훌륭합니다! 즉, 바로 창에서 $ exception.EntityValidationErrors.SelectMany (x => x.ValidationErrors) .Select (x => x.ErrorMessage)를 수행 할 수 있습니다.
chrispepper1989

13

catch {...}블록 내에서 디버그 모드에있는 동안 "QuickWatch"창 ( ctrl+ alt+ q)을 열고 여기에 붙여 넣으십시오.

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

그러면 ValidationErrors트리 로 드릴 다운 할 수 있습니다 . 이 오류에 대한 즉각적인 통찰력을 얻는 가장 쉬운 방법입니다.

첫 번째 오류 만 신경 쓰고 catch블록 이없는 Visual 2012+ 사용자의 경우 다음을 수행 할 수도 있습니다.

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

9

디버깅 중 오류를 검사하여 의미있는 오류 메시지를 빠르게 찾으려면 다음을 수행하십시오.

  • 다음에 대한 빠른 시계 추가 :

    ((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
  • 다음과 같이 EntityValidationErrors로 드릴 다운하십시오.

    (수집 항목 예 : [0])> ValidationErrors> (수집 항목 예 : [0])> ErrorMessage


5

실제로 이것은 검증 문제 일 뿐이므로 EF는 데이터베이스를 변경하기 전에 먼저 엔티티 특성을 검증합니다. 따라서 EF는 테이블을 디자인 할 때와 같이 속성 값이 범위를 벗어 났는지 확인합니다. Table_Column_UserName은 varchar (20)입니다. 그러나 EF에서 20보다 긴 값을 입력했습니다. 또는 다른 경우 열이 널이 될 수없는 경우. 따라서 유효성 검사 프로세스에서 변경 여부에 관계없이 값을 null이 아닌 열로 설정해야합니다. 나는 개인적으로 Leniel Macaferi 답변과 같습니다. 유효성 검사 문제의 세부 정보를 보여줄 수 있습니다


4

"실제 유효성 검사 오류"에 중요한 정보가 포함되어있을 수 있으며 이것이 Microsoft가 다른 위치 (속성)에 정보를 싣는 이유 일 수 있습니다. 여기에 표시된 해결책은 실용적이지만주의해서 사용해야합니다.

확장 방법을 만들고 싶습니다. 더 많은 이유 :

  • 원본 스택 추적 유지
  • 개방 / 폐쇄 원칙을 따릅니다 (예 : 다른 종류의 로그에 다른 메시지를 사용할 수 있음)
  • 프로덕션 환경에는 DbEntityValidationException이 발생할 수있는 다른 위치 (예 : 다른 dbcontext)가있을 수 있습니다.

1

Azure Functions의 경우이 간단한 확장명을 Microsoft.Extensions.Logging.ILogger에 사용합니다.

public static class LoggerExtensions
{
    public static void Error(this ILogger logger, string message, Exception exception)
    {
        if (exception is DbEntityValidationException dbException)
        {
            message += "\nValidation Errors: ";
            foreach (var error in dbException.EntityValidationErrors.SelectMany(entity => entity.ValidationErrors))
            {
                message += $"\n * Field name: {error.PropertyName}, Error message: {error.ErrorMessage}";
            }
        }

        logger.LogError(default(EventId), exception, message);
    }
}

예제 사용법 :

try
{
    do something with request and EF
}
catch (Exception e)
{
    log.Error($"Failed to create customer due to an exception: {e.Message}", e);
    return await StringResponseUtil.CreateResponse(HttpStatusCode.InternalServerError, e.Message);
}

0

코드에서 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. 하나 이상의 엔터티에 대한 유효성 검사에 실패했습니다. 자세한 내용은 'EntityValidationErrors'속성을 참조하십시오.

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


세 번째 링크는 허용 된 답변 블로그의 사본이지만 다른 사이트에 있습니다. 두 번째 링크는 이미 첫 번째 링크를 참조하는 스택 오버플로 질문입니다.
Eris

그렇다면 적절한 참조를 가진 사람을 도와주는 것은 어떤 문제입니까?
Atta H.

예, 답변에 링크 만 포함되어서는 안됩니다. 질문에 대한 답변을 요약 한 다음 끝에 읽을 수 있도록 링크를 게시하십시오.
ChrisO
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.