web-dev-qa-db-ja.com

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

データベースに編集を保存したいのですが、ASP.NET MVC 3/C#でEntity FrameWork Code-Firstを使用していますが、エラーが発生しています。私のEventクラスには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'プロパティを参照してください。

330
user522767

次のコードを使用してDbEntityValidationExceptionからすべての情報を抽出できます(ネームスペースを追加する必要があります:System.Data.Entity.ValidationおよびSystem.Diagnosticsusingリストに追加する必要があります)。

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);
        }
    }
}
838
Praveen Prasad

コードの変更は不要です。

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

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

または:

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

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

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

248
GONeale

同じプロパティ名のクラスがある場合は、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);
       }
    }
 }
37
Tony

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...
        }
    }
}
22
Bolt Thunder

この実装は、エンティティの例外を詳細テキストで例外にラップします。 DbEntityValidationExceptionDbUpdateExceptiondatetime2の範囲エラー(MS SQL)を処理し、無効なエンティティのキ​​ーをメッセージに含めます(1回のSaveChanges呼び出しで多数のエンティティを削除する場合に役立ちます)。

まず、DbContextクラスのSaveChangesをオーバーライドします。

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

Entity VAlidation Errosで問題が発生したときに、このコードが問題を見つけるのに役立ちました。それは私のエンティティ定義の正確な問題を私に話しました。 storeDB.SaveChanges()をカバーする必要がある場合は、次のコードを試してください。次ではcatchブロックを試してください。

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

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

私はこれが質問の問題とは少し違うことを知っていますが、それはよくある質問なので、私と同じ問題を抱えている他の人のためにもう少し答えに追加したいと思いました。
これが他の人に役立つことを願っています:)

4
dan richardson

すべての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()メソッドを上書きするでしょう、そしてあなたはすべてのエンティティ検証エラーを含むコンマ区切りのリストを得るでしょう。

これは、単にエラーをスローするのではなく、運用環境でエラーを記録するように改善することもできます。

これが役に立つことを願っています。

4
Chtiwi Malek

これがTonyの拡張機能の拡張機能です... :-)

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
Martin_ATS

私はOnSaveChangesを登録し、これを持っている例外が好きではありません

var validationErrors = model.GetValidationErrors();

var h = validationErrors.SelectMany(x => x.ValidationErrors
                                          .Select(f => "Entity: " 
                                                      +(x.Entry.Entity) 
                                                      + " : " + f.PropertyName 
                                                      + "->" + f.ErrorMessage));
3

DBの行にnvarchar(50)がある場合は、50文字を超えないようにしてください。ばかげた間違いだけどそれを理解するのに3時間かかった。

2
user4030144

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

2
laylarenee

データベースを更新している間、私は数日前に同じ問題に直面しました。私の場合は、例外の原因となっているコードで提供されていない、メンテナンスのために追加されたNULL不可の新しい列がほとんどありませんでした。私はそれらのフィールドを見つけ出し、それらに与えられた値とその解決された値を提供します。

1
JM Kumawat

これは、sqlのように特定の列に許容される最大文字数が、データ型nvarchar(5)の後に続く場合があるが、ユーザーから入力された文字数が指定された数を超えているために発生します。

1
lokopi

これは、Modelによって設定されていないPropertyによって引き起こされる可能性があります。代わりに、Controllerによって設定されます。これは、このエラーの原因となる場合があります。そしてこの2番目の仮定はです。データベースにすでにデータがあり、それを更新しようとしていますが、今フェッチしている可能性があります。

0

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

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
Exatex

私の場合は、私が設定したデータ型がvarchar(200)であるTable Column name Pathを持っています。それをnvarchar(max)に更新した後、edmxからテーブルを削除し、テーブルを追加しました。

0
Saket Singh