Validasi gagal untuk satu atau lebih entitas sambil menyimpan perubahan ke Database SQL Server menggunakan Entity Framework

337

Saya ingin menyimpan Edit ke Database dan saya menggunakan Entity FrameWork Code-First di ASP.NET MVC 3 / C # tetapi saya mendapatkan kesalahan. Di kelas Event saya, saya memiliki tipe data DateTime dan TimeSpan tetapi dalam database saya, saya masing-masing memiliki Date dan time. Dapatkah ini menjadi alasan? Bagaimana saya bisa melakukan cast ke tipe data yang sesuai dalam kode sebelum menyimpan perubahan ke database.

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

Metode di controller >>>> Masalah di 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);
    }
}

dengan

public class EventCalendarEntities : DbContext
{
    public DbSet<Event> Events { get; set; }
    public DbSet<Category> Categories { get; set; }
    public DbSet<Place> Places { get; set; } 
}

Database SQL Server 2008 R2 / T-SQL

EventDate (Datatype = date)  
StartTime (Datatype = time)  
EndTime (Datatype = time)  

Formulir 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  

Kesalahan Server dalam Aplikasi '/'.

Validasi gagal untuk satu entitas atau lebih. Lihat properti 'EntityValidationErrors' untuk detail lebih lanjut.

Deskripsi: Pengecualian yang tidak ditangani terjadi selama eksekusi permintaan web saat ini. Harap tinjau jejak tumpukan untuk informasi lebih lanjut tentang kesalahan dan dari mana asalnya dalam kode.

Detail Pengecualian: System.Data.Entity.Validation.DbEntityValidationException: Validasi gagal untuk satu entitas atau lebih. Lihat properti 'EntityValidationErrors' untuk detail lebih lanjut.

Kesalahan Sumber:

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

Sumber File: C: \ sep \ MvcEventCalendar \ MvcEventCalendar \ Controllers \ EventManagerController.cs Baris: 77

Jejak Tumpukan:

[DbEntityValidationException: Validasi gagal untuk satu entitas atau lebih. Lihat properti 'EntityValidationErrors' untuk detail lebih lanjut.]

pengguna522767
sumber
8
mungkin salah satu bidang wajib Anda memiliki nilai nol. Seperti EventDate, StartTime, Harga, Kategori dll
Daveo
Sudahkah Anda memeriksa variabel formulir yang diposkan untuk memastikan masing-masing cocok dengan jenis basis data yang ditentukan? Atau seperti apa yang dikatakan Daveo, salah satu nilai formulir yang diperlukan hilang ...
timothyclifford
Tidak semua variabel formulir yang diposting cocok dengan jenis yang ditentukan database. Saya telah menggunakan tanggal dan waktu dalam database tetapi tidak ada datatype langsung yang setara dengan .NET. Oleh karena itu, saya menggunakan DateTime dan TimeSpan. Sekarang saya harus mengonversi keduanya ke tanggal dan waktu masing-masing.
user522767
Sumber kesalahan bagi saya adalah bidang string yang memiliki lebih dari 30 karakter dan ukuran bidang saya dalam database adalah 30.
Mojtaba

Jawaban:

840

Anda dapat mengekstrak semua informasi dari DbEntityValidationException dengan kode berikut (Anda perlu menambahkan ruang nama: System.Data.Entity.Validationdan System.Diagnosticske usingdaftar Anda ):

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);
        }
    }
}
Praveen Prasad
sumber
7
Anda juga bisa mendapatkan kesalahan ini jika ada data unggulan yang tidak sepenuhnya memenuhi aturan atribut model (seperti Required). Saya telah menambahkan jawaban dengan sedikit info lebih lanjut.
dan richards
8
Jawabannya dapat ditingkatkan dengan menjelaskan di mana Anda benar-benar dapat melihat jejak keluaran.
mkataja
1
Saya menemukan artikel msdn ini tentang jejak yang sangat berguna
Borik
2
Anda Tuan, memenangkan internet.
Leandro
8
Output Jejak dapat dilihat di jendela Output. Klik Debug di bagian atas -> Windows -> Output
reggaeguitar
249

Tidak diperlukan perubahan kode:

Saat Anda berada dalam mode debug dalam catch {...}blok, buka jendela "QuickWatch" ( Ctrl+ Alt+ Q) dan tempel di sana:

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

atau:

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

Jika Anda tidak mencoba / menangkap atau tidak memiliki akses ke objek pengecualian.

Ini akan memungkinkan Anda untuk menelusuri ValidationErrorspohon. Ini cara termudah yang saya temukan untuk mendapatkan wawasan instan tentang kesalahan ini.

GONeale
sumber
6
Anda teman saya jenius, bahwa Anda membantu saya melacak kesalahan ET yang saya terima, Terima kasih!
Mark Kram
4
Tidak masalah :) Tapi bukan jenius, cintai QuickWatch :)
GONeale
4
Baru saja memperbarui jawaban bagi mereka yang tidak memiliki akses ke objek / variabel pengecualian.
GONeale
7
Setiap kali saya mendapatkan pengecualian ini saya mencari kesalahan, lalu saya datang ke sini (hasil 2 di Google), saya menemukan posting ini dan saya menggunakan solusi kedua di panel Debug> Watch. Sudah puluhan kali. Terima kasih GONeale.
Ata S.
1
Apakah saya satu-satunya yang mendapat "Nama '$ exception' tidak ada dalam konteks saat ini" saat menjalankan kueri kedua?
Alex Klaus
37

Jika Anda memiliki kelas dengan nama properti yang sama, berikut adalah sedikit ekstensi untuk jawaban 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);
       }
    }
 }
Tony
sumber
22

Sebagai peningkatan untuk Praveen dan Tony, saya menggunakan penggantian:

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...
        }
    }
}
Baut Guntur
sumber
6

Implementasi ini membungkus pengecualian entitas dengan pengecualian dengan teks detail. Ini menangani DbEntityValidationException, DbUpdateException, datetime2kesalahan range (MS SQL), dan termasuk kunci entitas tidak valid dalam pesan (berguna ketika savind banyak entitas pada satuSaveChanges panggilan).

Pertama, menimpa SaveChangesdi kelas DbContext:

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

Kelas 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);
    }
}
Sel
sumber
Anda harus menunggu panggilan SaveChangesAsync, jika Anda ingin menangani pengecualian.
Arnab Chakraborty
Terima kasih, Arnab Chakraborty ! Jawaban tetap
Sel
5

Kode ini membantu menemukan masalah saya ketika saya memiliki masalah dengan Entitas VAlidation Erros saya. Itu memberi tahu saya masalah yang tepat dengan Definisi Entitas saya. Coba kode berikut di mana Anda perlu untuk menutupi storeDB.SaveChanges (); dalam mengikuti coba tangkap blok.

  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;
}
Рахул Маквана
sumber
4

Saya mendapatkan kesalahan ini hari ini dan tidak bisa menyelesaikannya untuk sementara waktu, tetapi saya menyadari itu setelah menambahkan beberapa RequireAttributeke model saya dan bahwa beberapa data seed pengembangan tidak mengisi semua bidang yang diperlukan.
Jadi hanya sebuah catatan bahwa jika Anda mendapatkan kesalahan ini sementara memperbarui database melalui semacam strategi init seperti DropCreateDatabaseIfModelChangesmaka Anda harus memastikan bahwa data unggulan Anda memenuhi dan memenuhi semua atribut validasi data model .

Saya tahu ini sedikit berbeda dengan masalah dalam pertanyaan, tetapi ini adalah pertanyaan yang populer jadi saya pikir saya akan menambahkan sedikit lebih banyak untuk jawaban bagi orang lain yang memiliki masalah yang sama seperti saya.
Semoga ini bisa membantu orang lain :)

dan richardson
sumber
4

Saya pikir menambahkan try / catch untuk setiap SaveChanges()operasi bukanlah praktik yang baik, lebih baik memusatkan ini:

Tambahkan kelas ini ke DbContextkelas utama :

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

Ini akan menimpa SaveChanges()metode konteks Anda dan Anda akan mendapatkan daftar yang dipisahkan koma yang berisi semua kesalahan validasi entitas.

ini juga dapat ditingkatkan, untuk mencatat kesalahan dalam produksi env, bukan hanya membuang kesalahan.

semoga ini membantu.

Chtiwi Malek
sumber
Di mana saya dapat menemukan "Kelas DbContext"? Saya agak baru dalam hal-hal MVC ini. Saya menggunakan MVC 5 dan Entity Framework 6. Hanya tidak tahu seperti apa namanya di Solution Explorer saya.
JustJohn
@JustJohn itu ada dalam model database Anda (file kelas), jika Anda tidak tahu di mana itu, Anda dapat melakukan pencarian penuh kata kunci DbContextterhadap proyek Anda.
Chtiwi Malek
1
Saya suka pendekatan ini yang terbaik karena merupakan solusi universal dalam aplikasi dan mengungkapkan apa yang ada di bawah permukaan dengan mudah tanpa mengubah banyak modul lainnya dalam sistem
Korayem
3

Berikut ini ekstensi untuk ekstensi Tony ... :-)

Untuk Entity Framework 4.x, jika Anda ingin mendapatkan nama dan nilai bidang kunci sehingga Anda tahu instance entitas mana (catatan DB) yang memiliki masalah, Anda bisa menambahkan yang berikut ini. Ini memberikan akses ke anggota kelas ObjectContext yang lebih kuat dari objek DbContext Anda.

// 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;
Martin_W
sumber
3

Saya tidak suka pengecualian, saya mendaftarkan OnSaveChanges dan memilikinya

var validationErrors = model.GetValidationErrors();

var h = validationErrors.SelectMany(x => x.ValidationErrors
                                          .Select(f => "Entity: " 
                                                      +(x.Entry.Entity) 
                                                      + " : " + f.PropertyName 
                                                      + "->" + f.ErrorMessage));
Mickey Perlstein
sumber
Apa maksud Anda dengan mengatakan "Saya sudah mendaftar OnSaveChanges"?
Akira Yamamoto
Saya terhubung ke OnSaveChanges pada Konteks yang sama dengan yang lain, saya hanya tidak mencapai tahap pengecualian. \
Mickey Perlstein
2

Kesalahan ini juga terjadi ketika Anda mencoba untuk menyimpan entitas yang memiliki kesalahan validasi. Cara yang baik untuk menyebabkan ini adalah lupa memeriksa ModelState.IsValid sebelum menyimpan ke DB Anda.

orang awam
sumber
2

Pastikan bahwa jika Anda memiliki nvarchar (50) di baris DB Anda tidak mencoba memasukkan lebih dari 50 karakter di dalamnya. Kesalahan bodoh tapi butuh 3 jam untuk mencari tahu.

pengguna4030144
sumber
1

Ini mungkin disebabkan oleh jumlah karakter maksimum yang diperbolehkan untuk kolom tertentu, seperti dalam sql satu bidang mungkin memiliki tipe data berikut nvarchar(5)tetapi jumlah karakter yang dimasukkan dari pengguna lebih dari yang ditentukan, maka timbul kesalahan.

lokopi
sumber
1

Saya telah menghadapi masalah yang sama beberapa hari yang lalu ketika memperbarui basis data. Dalam kasus saya, ada beberapa kolom baru yang tidak dapat dibatalkan yang ditambahkan untuk pemeliharaan yang tidak disediakan dalam kode yang menyebabkan pengecualian. Saya mencari tahu bidang-bidang itu dan memberikan nilai-nilai untuk mereka dan itu diselesaikan.

Jhabar
sumber
0

Terima kasih atas jawaban Anda, itu banyak membantu saya. seperti yang saya kode di Vb.Net, kode Bolt ini untuk 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
Exatex
sumber
0

mungkin disebabkan oleh Properti yang tidak diisi oleh model .. melainkan itu diisi oleh Controller .. yang dapat menyebabkan kesalahan ini .. solusi untuk ini adalah menetapkan properti sebelum menerapkan validasi ModelState. dan Asumsi kedua ini adalah. Anda mungkin sudah memiliki Data di Database Anda dan mencoba memperbaruinya tetapi sekarang mengambilnya.

Muhammad Mustafa
sumber
0

Dalam kasus saya, saya memiliki Path nama Kolom Tabel yang datatype saya set adalah varchar (200). Setelah memperbarui ke nvarchar (maks), saya telah menghapus tabel dari edmx dan kemudian menambahkan tabel dan wokred dengan benar untuk saya.

Saket Singh
sumber