web-dev-qa-db-ja.com

DBUpdateExceptionの理由を見つける

DbContext.SaveChangesを呼び出すと、DbUpdateExceptionが発生します。

EntityFramework.dllで、タイプ 'System.Data.Entity.Infrastructure.DbUpdateException'の未処理の例外が発生しました。追加情報:エントリの更新中にエラーが発生しました。詳細については、内部例外を参照してください。

残念ながら、内部の例外はありません(少なくとも、私が見る限りではありません)。 SaveChangesが例外をスローした理由を正確に確認する方法はありますか?少なくとも、エラーが発生したときにSaveChangesがどのテーブルを更新しようとしたかを確認しておくと役立ちます。

41
John Reynolds

実際の例外がどこかで失われたように思える場合、最善の策はすべての例外を破ることです。それがどこか、手の届くところまたは外のどこでキャッチまたは飲み込まれたかに関係なく、デバッガーは壊れ、何が起こっているかを見ることができます。

詳細については、次のMSDNリンクを参照してください。

方法:例外がスローされたときにブレークする

3
Crono

これはSaveChangesのオーバーライドです。ブレークポイントを置く便利な場所を提供してくれます。

    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbEntityValidationException e)
        {
            foreach (var eve in e.EntityValidationErrors)
            {
                Debug.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)
                {
                    Debug.WriteLine(@"- Property: ""{0}"", Error: ""{1}""",
                        ve.PropertyName, ve.ErrorMessage);
                }
            }
            throw;
        }
        catch(DbUpdateException e)
        {
           //Add your code to inspect the inner exception and/or
           //e.Entries here.
           //Or just use the debugger.
           //Added this catch (after the comments below) to make it more obvious 
           //how this code might help this specific problem
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.Message);
            throw;
        }
    }

参照:

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

28
Colin

SaveChangesのオーバーライドは次のとおりです。DbUpdateExceptionを処理するための追加のコードを示しています(質問どおり)。

    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbEntityValidationException vex)
        {
            var exception = HandleDbEntityValidationException(vex);
            throw exception;
        }
        catch(DbUpdateException dbu)
        {
            var exception = HandleDbUpdateException(dbu);
            throw exception;
        }
    }

    private Exception HandleDbUpdateException(DbUpdateException dbu)
    {
        var builder = new StringBuilder("A DbUpdateException was caught while saving changes. ");

        try
        {
            foreach (var result in dbu.Entries)
            {
                builder.AppendFormat("Type: {0} was part of the problem. ", result.Entity.GetType().Name);
            }
        }
        catch (Exception e)
        {
            builder.Append("Error parsing DbUpdateException: " + e.ToString());
        }

        string message = builder.ToString();
        return new Exception(message, dbu);
    }

ロギングコードをあまり具体的にしていませんが、次のような標準エラーメッセージが改善されています。

The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value.

この方法で、少なくともどのエンティティに問題があるかがわかります。通常はそれで問題を解決するのに十分です。

16
a-h

Colinの回答に基づいて、EF持続性障害に関する完全に詳細な情報を次のように提供できます。

public bool SaveChangesEx()
{
    try
    {
        SaveChanges();
        return true;
    }
    catch (DbEntityValidationException exc)
    {
        // just to ease debugging
        foreach (var error in exc.EntityValidationErrors)
        {
            foreach (var errorMsg in error.ValidationErrors)
            {
                // logging service based on NLog
                Logger.Log(LogLevel.Error, $"Error trying to save EF changes - {errorMsg.ErrorMessage}");
            }
        }

        throw;
    }
    catch (DbUpdateException e)
    {
        var sb = new StringBuilder();
        sb.AppendLine($"DbUpdateException error details - {e?.InnerException?.InnerException?.Message}");

        foreach (var eve in e.Entries)
        {
            sb.AppendLine($"Entity of type {eve.Entity.GetType().Name} in state {eve.State} could not be updated");
        }

        Logger.Log(LogLevel.Error, e, sb.ToString());

        throw;
    }
}

検証エラーに加えて、更新例外は一般的なエラーとコンテキスト情報の両方を出力します。

注: C#6.0は、null伝播と文字列補間を使用するため、このコードが機能するために必要です。


.NET Coreの場合、発生する可能性のある例外は異なる構造を持ち、異なる方法で設定されるため、コードはわずかに変更されます。

    public void SaveChangesEx()
    {
        try
        {
            // this triggers defined validations such as required
            Context.Validate();
            // actual save of changes
            Context.SaveChangesInner();
        }
        catch (ValidationException exc)
        {
            Logger.LogError(exc, $"{nameof(SaveChanges)} validation exception: {exc?.Message}");
            throw;
        }
        catch (DbUpdateException exc)
        {
            Logger.LogError(exc, $"{nameof(SaveChanges)} db update error: {exc?.InnerException?.Message}");
            throw;
        }
        catch (Exception exc)
        {
            // should never reach here. If it does, handle the more specific exception
            Logger.LogError(exc, $"{nameof(SaveChanges)} generic error: {exc.Message}");
            throw;
        }
    }

コンテキストは、同じコンテキストがすぐに破棄されない場合、失敗時に変更を自動的に拒否するように拡張できます。

public void RejectChanges()
{
    foreach (var entry in ChangeTracker.Entries().Where(e => e.Entity != null).ToList())
    {
        switch (entry.State)
        {
            case EntityState.Modified:
            case EntityState.Deleted:
                entry.State = EntityState.Modified; //Revert changes made to deleted entity.
                entry.State = EntityState.Unchanged;
                break;
            case EntityState.Added:
                entry.State = EntityState.Detached;
                break;
        }
    }
}

public bool SaveChangesInner()
{
    try
    {
        SaveChanges();
        return true;
    }
    catch (Exception)
    {
        RejectChanges();
        throw;
    }
}
9
Alexei

「datetime2データ型からdatetimeデータ型への変換の結果、範囲外の値になりました。\ r\nステートメントは終了しました。」という同じエラーが発生していました。

このように日時値フィールドを配置しますDatetime.Now

var person = new Person
            {
               FirstName = "Sebastian",
               LastName = "Back",
               **BirthDate = DateTime.Now,**
               IsActive = true,
            };
2

私は同じエラーを得て、そのように解決しました。

データ型が日時であるフィールドがあり、日時プロパティが割り当てられていない状態で設定していることを認識しています。だから私はそれを「Datetime.Now」に変更すると、問題が解決し、保存されていることがわかります

0
Taha Karaca

私の場合、ストアドプロシージャを使用してエンティティをDBに追加していました。私のSPは、C#でこのエラーを受け取っていたため、ランタイムエラーを示しています。 SPを修正し、EFは完全に機能しました。したがって、SPを使用してEFで追加、編集、削除、または更新を行う場合は、対応するspが正しく機能しているかどうかを確認してください。

0
yogihosting