DbEntityValidationException-エラーの原因を簡単に確認するにはどうすればよいですか?


217

Entity Frameworkを使用するプロジェクトがあります。呼び出している間SaveChanges、私にDbContext、私は次の例外を取得します:

System.Data.Entity.Validation.DbEntityValidationException:1つ以上のエンティティの検証に失敗しました。詳細については、「EntityValidationErrors」プロパティを参照してください。

これはすべて順調ですが、この例外が発生するたびにデバッガをアタッチしたくありません。さらに、実稼働環境ではデバッガーを簡単に接続できないため、これらのエラーを再現するにはかなりの時間を費やす必要があります。

内に隠されている詳細を確認するにはどうすればよいDbEntityValidationExceptionですか?

回答:


429

最も簡単な解決策はSaveChanges、エンティティクラスをオーバーライドすることです。をキャッチしDbEntityValidationException、実際のエラーをアンラップDbEntityValidationExceptionして、改善されたメッセージで新しいエラーを作成できます。

  1. SomethingSomething.Context.csファイルの横に部分クラスを作成します。
  2. この投稿の下部にあるコードを使用してください。
  3. それでおしまい。実装は、リファクタリング作業なしで、オーバーライドされたSaveChangesを自動的に使用します。

例外メッセージは次のようになります。

System.Data.Entity.Validation.DbEntityValidationException:1つ以上のエンティティの検証に失敗しました。詳細については、「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
生成されたEntitiesクラスはすでにDbContextを継承しているため、部分クラスに再度追加する必要はありません。部分クラスに追加しても、何も壊したり変更したりすることはありません。実際、DbContextからの継承を追加する場合、Resharperはそれを削除するように提案します。
Martin Devillers 2013年

15
これがSaveChangesのデフォルトの動作ではないのはなぜですか?
John Shedletsky、2015年

4
「なぜこれがSaveChangesのデフォルトの動作ではないのですか?」-それは本当に良い質問です。これは素晴らしい解決策でした、それは私に何時間も節約しました!私はスローインしなければなりませんでしたusing System.Linq;
ジョン8

1
tryブロックにあるbase.SaveChanges()でのビューの作成エラー。キャッチブロックにジャンプすることはありません。私はあなたのコードにSaveChangesを乗り越えさせましたが、エラーでCatch Blockに入ることがありません。
JustJohn 2015年

7
スタックトレースを保持するには、内部例外を設定する必要があります。
dotjoe 2016

48

マーティンが示したように、にはより多くの情報があり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 and Aggregate中にgithubの大胆コーダー
Kiquenet

43

EntityValidationErrorsコレクションを表示するには、次のウォッチ式をウォッチウィンドウに追加します。

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

Visual Studio 2013を使用しています


$ exceptionは素晴らしいです!つまり、イミディエイトウィンドウで$ 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にドリルダウンします。

    (コレクションアイテムeg [0])> ValidationErrors>(コレクションアイテムeg [0])> ErrorMessage


5

実際、これは単なる検証の問題です。EFは、データベースに変更を加える前に、まずエンティティのプロパティを検証します。したがって、EFは、テーブルを設計したときのように、プロパティの値が範囲外であるかどうかをチェックします。Table_Column_UserNameはvarchar(20)です。しかし、EFでは、20を超える値を入力しました。または、列がNullであることを許可しない場合は、他の場合に入力します。したがって、検証プロセスでは、値を変更するかどうかに関係なく、nullでない列に値を設定する必要があります。私は個人的には、レニエル・マカフェリの答えのように。検証の問題の詳細を表示できます


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. 1つ以上のエンティティの検証が失敗しました。詳細については、「EntityValidationErrors」プロパティを参照してください

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


3番目のリンクは、承認された回答のブログのコピーですが、別のサイトにあります。2番目のリンクは、最初のリンクをすでに参照しているスタックオーバーフローの質問です。
Eris

それで、適切な参照で誰かを助けようとすることは、ここで問題です?
Atta H.

はい、あなたの答えはリンクだけを含むべきではありません。質問に答える要約を作成し、最後にリンクを投稿してさらに読んでください。
ChrisO、2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.