การตรวจสอบล้มเหลวสำหรับเอนทิตี้อย่างน้อยหนึ่งรายการในขณะที่บันทึกการเปลี่ยนแปลงของฐานข้อมูลเซิร์ฟเวอร์ SQL โดยใช้ Entity Framework


337

ฉันต้องการบันทึก Edit to Database และฉันใช้ Entity FrameWork Code-First ใน ASP.NET MVC 3 / C # แต่ฉันได้รับข้อผิดพลาด ในคลาสกิจกรรมของฉันฉันมี DateTime และ TimeSpan datatypes แต่ในฐานข้อมูลของฉันฉันมี Date and 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  

ผิดพลาดเซิร์ฟเวอร์ใน '/' แอพลิเคชัน.

การตรวจสอบล้มเหลวสำหรับเอนทิตีอย่างน้อยหนึ่งรายการ ดูคุณสมบัติ 'EntityValidationErrors' สำหรับรายละเอียดเพิ่มเติม

คำอธิบาย: มีข้อยกเว้นที่ไม่สามารถจัดการได้เกิดขึ้นระหว่างการดำเนินการตามคำขอเว็บปัจจุบัน โปรดตรวจสอบการติดตามสแต็กสำหรับข้อมูลเพิ่มเติมเกี่ยวกับข้อผิดพลาดและที่มาในรหัส

รายละเอียดข้อยกเว้น: System.Data.Entity.Validation.DbEntityValidationException: การตรวจสอบล้มเหลวสำหรับเอนทิตีหนึ่งรายการขึ้นไป ดูคุณสมบัติ '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: การตรวจสอบล้มเหลวสำหรับเอนทิตี้อย่างน้อยหนึ่งรายการ ดูคุณสมบัติ 'EntityValidationErrors' สำหรับรายละเอียดเพิ่มเติม]


8
อาจเป็นหนึ่งในเขตข้อมูลที่คุณต้องการมีค่าเป็นศูนย์ เช่นเดียวกับ EventDate, StartTime, Price, Category และอื่น ๆ
Daveo

คุณได้ตรวจสอบตัวแปรแบบฟอร์มที่โพสต์แล้วเพื่อให้แน่ใจว่าตรงกับประเภทฐานข้อมูลที่กำหนดหรือไม่ หรือเหมือนสิ่งที่ Daveo กล่าวว่าค่าใดค่าหนึ่งรูปแบบที่จำเป็นจะหายไป ...
timothyclifford

ตัวแปรแบบฟอร์มที่โพสต์ทั้งหมดไม่ตรงกับประเภทฐานข้อมูลที่กำหนด ฉันใช้วันที่และเวลาในฐานข้อมูล แต่ไม่มีประเภทข้อมูลโดยตรงเทียบเท่าใน. NET ดังนั้นฉันใช้ DateTime และ TimeSpan ตอนนี้ฉันต้องแปลงทั้งสองเป็นวันที่และเวลาตามลำดับ
user522767

แหล่งที่มาของข้อผิดพลาดสำหรับผมก็คือสนามสตริงซึ่งมีมากกว่า 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) ฉันได้เพิ่มคำตอบพร้อมข้อมูลเพิ่มเติมอีกเล็กน้อย
แดนริชาร์ดสันเมื่อ

8
คำตอบอาจได้รับการปรับปรุงโดยอธิบายว่าคุณสามารถเห็นผลลัพธ์การติดตามได้จากที่ใด
mkataja

1
ฉันพบนี้บทความ MSDNเกี่ยวกับร่องรอยเป็นประโยชน์มาก
Borik

2
คุณคุณชนะอินเทอร์เน็ต
ลีอันโดร

8
เอาต์พุตการติดตามสามารถเห็นได้ในหน้าต่างเอาต์พุต คลิกดีบั๊กที่ด้านบน -> Windows -> Output
reggaeguitar

249

ไม่จำเป็นต้องเปลี่ยนรหัส:

ในขณะที่คุณอยู่ในโหมดแก้ไขข้อบกพร่องภายในcatch {...}บล็อกเปิดหน้าต่าง "QuickWatch" ( Ctrl+ Alt+ Q) และวางใน:

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

หรือ:

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

หากคุณไม่ได้ลอง / จับหรือไม่มีสิทธิ์เข้าถึงวัตถุยกเว้น

นี้จะช่วยให้คุณสามารถเจาะลึกลงไปในValidationErrorsต้นไม้ มันเป็นวิธีที่ง่ายที่สุดที่ฉันพบว่าได้รับข้อมูลเชิงลึกเกี่ยวกับข้อผิดพลาดเหล่านี้ทันที


6
คุณเพื่อนของฉันเป็นอัจฉริยะที่คุณช่วยฉันติดตามข้อผิดพลาด ET ที่ฉันได้รับขอบคุณ!
Mark Kram

4
ไม่มีปัญหา :) แต่ไม่อัจฉริยะเพียงแค่ความรัก QuickWatch :)
GONeale

4
เพิ่งปรับปรุงคำตอบสำหรับผู้ที่ไม่สามารถเข้าถึงวัตถุยกเว้น / ตัวแปร
GONeale

7
ทุกครั้งที่ฉันได้รับข้อยกเว้นนี้ฉันค้นหาข้อผิดพลาดจากนั้นฉันมาที่นี่ (ผลลัพธ์อันดับที่ 2 ของ Google) ฉันพบโพสต์นี้และฉันใช้วิธีที่สองในแผงควบคุม Debug> Watch ทำหลายสิบครั้ง ขอบคุณ GONeale
Ata S.

1
ฉันเป็นคนเดียวที่ได้รับ "ชื่อ '$ exception' ไม่มีอยู่ในบริบทปัจจุบัน" ในการเรียกใช้แบบสอบถามที่สองหรือไม่
Alex Klaus

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

การใช้งานนี้ห่อข้อยกเว้นเอนทิตีเพื่อยกเว้นด้วยข้อความรายละเอียด จะจัดการDbEntityValidationException, DbUpdateException, datetime2ข้อผิดพลาดช่วง (MS SQL) และรวมถึงกุญแจสำคัญของกิจการที่ไม่ถูกต้องในข้อความ (มีประโยชน์เมื่อหน่วยงานหลาย savind ที่หนึ่งSaveChangesโทร)

ก่อนแทนที่SaveChangesใน 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);
        }
    }

คลาส 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

ขอบคุณArnab Chakraborty ! แก้ไขคำตอบแล้ว
Sel

5

รหัสนี้ช่วยค้นหาปัญหาของฉันเมื่อฉันมีปัญหากับ Erros VAlidation Erros ของฉัน มันบอกปัญหาที่แน่นอนกับคำนิยามเอนทิตีของฉัน ลองใช้รหัสต่อไปนี้ซึ่งคุณต้องครอบคลุม storeDB.SaveChanges (); ในการติดตามลอง catch block

  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ไปยังแบบจำลองของฉันแล้วและข้อมูลเมล็ดพันธุ์การพัฒนาบางส่วนไม่ได้เติมฟิลด์ที่จำเป็นทั้งหมด
ดังนั้นโปรดทราบว่าหากคุณได้รับข้อผิดพลาดนี้ในขณะที่อัปเดตฐานข้อมูลผ่านกลยุทธ์การเริ่มต้นบางอย่างเช่นDropCreateDatabaseIfModelChangesนั้นคุณต้องตรวจสอบให้แน่ใจว่าข้อมูลเมล็ดพันธุ์ของคุณปฏิบัติตามและสอดคล้องกับคุณลักษณะการตรวจสอบความถูกต้องของข้อมูลเมล็ดบรรลุเป้าหมายการข้อมูลและตอบสนองคุณลักษณะการตรวจสอบรูปแบบข้อมูลใด

ฉันรู้ว่านี่เป็นปัญหาที่แตกต่างจากคำถามเล็กน้อย แต่เป็นคำถามยอดนิยมดังนั้นฉันคิดว่าฉันจะเพิ่มคำตอบให้คนอื่นที่มีปัญหาแบบเดียวกับตัวเองเพิ่มขึ้นอีกเล็กน้อย
หวังว่านี่จะช่วยผู้อื่น :)


4

ฉันคิดว่าการเพิ่มลอง / จับสำหรับทุกคน SaveChanges()การดำเนินการไม่ใช่วิธีปฏิบัติที่ดี

เพิ่มคลาสนี้ในคลาสหลัก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()วิธีคุณและคุณจะได้รับรายการที่คั่นด้วยเครื่องหมายจุลภาคที่มีข้อผิดพลาดการตรวจสอบเอนทิตีทั้งหมด

นอกจากนี้ยังสามารถปรับปรุงบันทึกข้อผิดพลาดในการผลิต env แทนที่จะโยนข้อผิดพลาด

หวังว่านี่จะเป็นประโยชน์


ฉันจะหา "DbContext Class" ได้จากที่ใด ฉันค่อนข้างใหม่ในสิ่งที่ MVC นี้ ฉันใช้ MVC 5 และ Entity Framework 6. แค่ไม่รู้ว่าชื่อต้องการอะไรใน Solution Explorer ของฉัน
JustJohn

@ JustJohn อยู่ในรูปแบบฐานข้อมูลของคุณ (ไฟล์คลาส) หากคุณไม่ทราบว่าอยู่ที่ไหนคุณสามารถค้นหาคำหลักแบบเต็มDbContextกับโปรเจ็กต์ของคุณได้
Chtiwi Malek

1
ฉันเช่นนี้วิธีการที่ดีที่สุดเท่าที่จะเป็นทางออกที่สากลในการตรวจสอบและเผยให้เห็นสิ่งที่อยู่ใต้พื้นผิวได้อย่างง่ายดายโดยไม่ต้องเปลี่ยนมากสำหรับโมดูลอื่น ๆ ในระบบ
Korayem

3

นี่คือส่วนขยายของส่วนขยายของ Tony ... :-)

สำหรับ Entity Framework 4.x ถ้าคุณต้องการรับชื่อและค่าของเขตข้อมูลคีย์เพื่อให้คุณทราบว่าอินสแตนซ์เอนทิตี (ระเบียน DB) มีปัญหาคุณสามารถเพิ่มสิ่งต่อไปนี้ สิ่งนี้ให้การเข้าถึงสมาชิกคลาส ObjectContext ที่มีประสิทธิภาพยิ่งขึ้นจากวัตถุ DbContext ของคุณ

// 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" แล้ว
Akira Yamamoto

ฉันเชื่อมต่อกับ OnSaveChanges บน Context เหมือนกับคนอื่น ๆ ฉันไม่ถึงขั้นตอนการยกเว้น \
Mickey Perlstein

2

ข้อผิดพลาดนี้เกิดขึ้นเมื่อคุณพยายามบันทึกเอนทิตีที่มีข้อผิดพลาดในการตรวจสอบ วิธีที่ดีในการทำให้เกิดสิ่งนี้คือลืมตรวจสอบ ModelState.IsValid ก่อนบันทึกลงในฐานข้อมูลของคุณ


2

ตรวจสอบให้แน่ใจว่าถ้าคุณมี nvarchar (50) ในแถว DB คุณไม่ได้พยายามแทรกอักขระมากกว่า 50 ตัว ความผิดพลาดโง่ ๆ แต่ใช้เวลา 3 ชั่วโมงในการคิดออก


1

นี่อาจเป็นเพราะจำนวนอักขระสูงสุดที่อนุญาตสำหรับคอลัมน์ที่เฉพาะเจาะจงเช่นในเขตข้อมูล sql หนึ่งอาจมีประเภทข้อมูลต่อไปนี้nvarchar(5)แต่จำนวนอักขระที่ป้อนจากผู้ใช้มีมากกว่าจำนวนที่ระบุดังนั้นข้อผิดพลาดเกิดขึ้น


1

ฉันประสบปัญหาเดียวกันสองสามวันที่ผ่านมาในขณะที่การปรับปรุงฐานข้อมูล ในกรณีของฉันมีการเพิ่มคอลัมน์ไม่ใช่โมฆะใหม่สำหรับการบำรุงรักษาซึ่งไม่ได้ระบุไว้ในรหัสซึ่งทำให้เกิดข้อยกเว้น ฉันหาฟิลด์เหล่านั้นและให้ค่าสำหรับพวกมันและแก้ไขแล้ว


0

ขอบคุณสำหรับคำตอบของคุณมันช่วยฉันได้มาก เช่นเดียวกับฉันใน Vb.Net รหัส Bolt นี้สำหรับ 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

มันอาจเกิดจากคุณสมบัติที่ไม่ได้บรรจุโดยรุ่น .. แต่มันจะมีประชากรโดยตัวควบคุม .. ซึ่งอาจทำให้เกิดข้อผิดพลาดนี้ .. วิธีการแก้ปัญหานี้คือการกำหนดคุณสมบัติก่อนที่จะใช้การตรวจสอบ ModelState และสมมติฐานที่สองนี้คือ คุณอาจมีข้อมูลในฐานข้อมูลของคุณอยู่แล้วและพยายามที่จะอัปเดต แต่ตอนนี้กำลังดึงข้อมูล


0

ในกรณีของฉันฉันมีชื่อคอลัมน์คอลัมน์พา ธ ที่ประเภทข้อมูลที่ฉันตั้งไว้คือ varchar (200) หลังจากอัปเดตเป็น nvarchar (สูงสุด) ฉันได้ลบตารางออกจาก edmx แล้วจึงเพิ่มตารางอีกครั้งอย่างถูกต้องสำหรับฉัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.