Entity Frameworkを使用してSQL Serverデータベースへの変更を保存中に、1つ以上のエンティティの検証が失敗しました


337

編集をデータベースに保存したいのですが、ASP.NET MVC 3 / C#でEntity FrameWork Code-Firstを使用していますが、エラーが発生します。私のイベントクラスでは、DateTimeデータ型とTimeSpanデータ型がありますが、データベースでは、それぞれDateとTimeがあります。これが理由でしょうか?変更をデータベースに保存する前に、コードで適切なデータ型にキャストするにはどうすればよいですか。

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  

「/」アプリケーションでのサーバーエラー。

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

説明:現在のWebリクエストの実行中に未処理の例外が発生しました。エラーの詳細と、エラーがコードのどこで発生したかについては、スタックトレースを確認してください。

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

ソースエラー:

Line 75:             if (TryUpdateModel(theEvent))
Line 76:             {
Line 77:                 storeDB.SaveChanges();
Line 78:                 return RedirectToAction("Index");
Line 79:             }

ソースファイル:C:\ sep \ MvcEventCalendar \ MvcEventCalendar \ Controllers \ EventManagerController.cs行:77

スタックトレース:

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


8
おそらく、必須フィールドの1つにヌル値があります。EventDate、StartTime、Price、Categoryなど
Daveo

ポストされているフォーム変数を検査して、それぞれがデータベース定義のタイプと一致することを確認しましたか?または、Daveoが言ったように、必要なフォーム値の1つが欠落しています...
timothyclifford

ポストされたすべてのフォーム変数がデータベース定義のタイプと一致するわけではありません。データベースで日付と時刻を使用しましたが、.NETに相当する直接のデータ型はありません。そのため、DateTimeとTimeSpanを使用しました。次に、2つをそれぞれ日付と時刻に変換する必要があります。
user522767 2011年

私のためにエラーの原因は、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)を完全に満たしていない場合にも、このエラーが発生する可能性があります。もう少し詳しい情報を含む回答を追加しました。
dan richardson、

8
トレース出力が実際に表示される場所を説明することで、答えを改善できます。
mkataja 14

1
私は、トレースに関するこのmsdnの記事が非常に有用であることを発見しました
Borik

2
あなたはインターネットに勝ちました。
Leandro

8
トレース出力は、[出力]ウィンドウで確認できます。上部の[デバッグ]をクリックします->
Windows-

249

コードを変更する必要はありません:

catch {...}ブロック内でデバッグモードになっているときに、[QuickWatch]ウィンドウ(Ctrl+ Alt+ Q)を開いて貼り付けます。

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

または:

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

try / catchに参加していない場合、または例外オブジェクトにアクセスできない場合。

これにより、ValidationErrorsツリーにドリルダウンできます。これは、これらのエラーをすばやく洞察するために私が見つけた最も簡単な方法です。


6
あなたは私の友人が天才です。あなたは私が受け取っていたETエラーを追跡するのを手伝ってくれました。ありがとうございます。
Mark Kram

4
問題ありません:)しかし、天才ではなく、ただクイックウォッチを愛してください:)
GONeale

4
例外オブジェクト/変数にアクセスできない人のために答えを更新しました。
GONeale 2013年

7
この例外が発生するたびにエラーを検索してからここに来て(Googleで2番目の結果)、この投稿を見つけて、[デバッグ]> [ウォッチ]パネルで2番目の解決策を使用します。何十回もしました。GONealeありがとうございます。
Ata S. 14

1
2番目のクエリの実行時に「名前 '$ exception'は現在のコンテキストに存在しません」と表示されるのは私だけですか?
Alex Klaus 14

37

同じプロパティ名のクラスがある場合、Praveenの答えの小さな拡張を次に示します。

 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

PraveenとTonyの両方の改善として、オーバーライドを使用します。

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

この実装は、エンティティ例外を詳細テキストで例外にラップします。また、ハンドルDbEntityValidationExceptionDbUpdateExceptiondatetime2範囲エラー(MS SQL)、およびメッセージに無効なエンティティのキーを含める(1つので便利savind多くの企業SaveChanges呼び出し)。

まず、SaveChangesDbContextクラスでオーバーライドします。

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

ExceptionHelperクラス:

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呼び出しを待つ必要があります。
Arnab Chakraborty 2017

ありがとう、Arnab Chakraborty!回答を修正
Sel

5

このコードは、エンティティ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

今日このエラーが発生し、しばらくは解決できませんでしたRequireAttributeが、モデルにsを追加した後であり、一部の開発シードデータがすべての必須フィールドに入力されていないことに気付きました。
ですから、ある種の初期化戦略によってデータベースを更新しているときにこのエラーが発生したDropCreateDatabaseIfModelChanges場合は、シードデータがモデルデータ検証属性を満たし、それを満たしていることを確認する必要があります。

これは質問の問題とは少し異なることはわかっていますが、よくある質問なので、自分と同じ問題を抱えている他の人のために、もう少し答えを追加したいと思いました。
これが他の人を助けることを願っています:)


4

すべてのSaveChanges()操作にtry / catchを追加することは良い習慣ではないと思います。これを一元化することをお勧めします。

このクラスをメイン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()メソッドが上書きされ、すべてのエンティティ検証エラーを含むコンマ区切りのリストが表示されます。

これは、エラーをスローするだけでなく、本番環境のエラーをログに記録するように改善することもできます。

これがお役に立てば幸いです。


「DbContextクラス」はどこにありますか?私はこのMVCに関してはまったく新しいです。私はMVC 5とEntity Framework 6を​​使用しています。ソリューションエクスプローラーで名前がどのようになるかわからないだけです。
JustJohn 2015年

@JustJohnこれはデータベースモデル(クラスファイル)にあります。どこにあるかわからない場合はDbContext、プロジェクトに対してキーワードを完全に検索できます。
Chtiwi Malek、2015

1
アプリの汎用ソリューションであり、システム内の他のモジュールに大きな変更を
加える

3

これがトニーの拡張の拡張です... :-)

Entity Framework 4.xの場合、問題のあるエンティティインスタンス(DBレコード)がわかるようにキーフィールドの名前と値を取得するには、以下を追加します。これにより、DbContextオブジェクトからより強力なObjectContextクラスメンバーにアクセスできます。

// 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を登録しました」とはどういう意味ですか?
山本章14

他のコンテキストと同じように、コンテキストのOnSaveChangesに接続しましたが、例外ステージに到達しませんでした。\
Mickey Perlstein '27

2

このエラーは、検証エラーのあるエンティティを保存しようとしたときにも発生します。これを引き起こす良い方法は、DBに保存する前にModelState.IsValidをチェックすることを忘れることです。


2

DB行にnvarchar(50)がある場合は、50文字を超えて挿入しないでください。愚かな間違いですが、それを理解するのに3時間かかりました。


1

これは、SQLのように特定の列に許可されている最大文字数が原因である可能性があります。1つのフィールドに次のデータ型nvarchar(5)がある可能性がありますが、ユーザーから入力された文字数が指定より多いため、エラーが発生します。


1

数日前、データベースの更新中に同じ問題に直面しました。私の場合、例外を引き起こしているコードで提供されなかった、メンテナンスのために追加された新しいnull不可の列はほとんどありませんでした。私はそれらのフィールドとそれらに提供された値とその解決された値を理解します。


0

あなたの答えをありがとう、それは私をたくさん助けます。Vb.Netのiコードとして、Vb.NetのこのBoltコード

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検証を適用する前にプロパティを割り当てることです。この2番目の仮定はです。データベースにすでにデータがあり、それを更新しようとしているが、フェッチしている可能性があります。


0

私の場合、私が設定したデータ型がvarchar(200)であるテーブルカラム名のパスがあります.nvarchar(max)に更新した後、edmxからテーブルを削除し、再度テーブルを追加して、適切に動作しました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.