รหัส EF ก่อน: ฉันจะดูคุณสมบัติ 'EntityValidationErrors' ได้จากคอนโซลแพคเกจ nuget ได้อย่างไร


127

ฉันกำลังแพ้เรื่องนี้:

ฉันได้กำหนดคลาสของฉันสำหรับรหัสเอนทิตี Framework (4.1.3) วิธีแรก ทุกอย่างเรียบร้อย (ฉันสร้างตาราง ฯลฯ ) จนกระทั่งฉันเริ่ม Seed

ตอนนี้เมื่อฉันทำ

Add-Migration "remigrate" ; Update-Database;

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

ฉันมีจุดพักในวิธีการของฉัน Seed () แต่เนื่องจากฉันใช้สิ่งนี้บนคอนโซลเมื่อโปรเจ็กต์ไม่ทำงานฉัน clueless เป็นวิธีการรับรายละเอียด (PS - ฉันได้เห็นการตรวจสอบเธรดล้มเหลว สำหรับเอนทิตีหนึ่งแห่งหรือมากกว่าในขณะที่บันทึกการเปลี่ยนแปลงของฐานข้อมูลเซิร์ฟเวอร์ SQL โดยใช้ Entity Frameworkซึ่งแสดงวิธีที่ฉันสามารถดูคุณสมบัติได้)

ฉันรู้ว่าวิธี Seed () ของฉันมีปัญหาเพราะถ้าฉันใส่คืนทันทีหลังจากการเรียกเมธอดข้อผิดพลาดจะหายไป ดังนั้นฉันจะตั้งค่าเบรกพอยต์ของฉันได้อย่างไรเพื่อดูว่าข้อผิดพลาดในการตรวจสอบคืออะไร คินหายไป หรือมีวิธีอื่นในการติดตามในคอนโซล nuget หรือไม่?


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

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

น่าผิดหวังเมื่อคำตอบที่ผิดอยู่ด้านบนของคำตอบและได้รับเครดิตทั้งหมด สถานที่ที่ StackOverflow สั้นชัดเจน!
jwize

หากคุณใช้Entity Frameworkคุณสามารถดูคำตอบของฉันในโซลูชันสำหรับ“ การตรวจสอบล้มเหลวสำหรับเอนทิตีอย่างน้อยหนึ่งรายการ สถานที่ให้บริการดู 'EntityValidationErrors' สำหรับรายละเอียดเพิ่มเติม หวังว่าสิ่งนี้จะช่วยให้ ...
Murat Yıldız

คำตอบ:


216

เมื่อเร็ว ๆ นี้ฉันก็รู้สึกหงุดหงิดเช่นกัน ฉันแก้ไขมันโดยการใส่ฟังก์ชั่น wrapper ในคลาส Configuration ในเมธอด Seed และแทนที่การโทรSaveChangesด้วยการโทรไปยังฟังก์ชั่นของฉันแทน ฟังก์ชั่นนี้จะระบุข้อผิดพลาดภายในEntityValidationErrorsคอลเลกชันและสร้างข้อยกเว้นใหม่เมื่อข้อความข้อยกเว้นแสดงรายการปัญหาแต่ละรายการ สิ่งนี้ทำให้ผลลัพธ์ปรากฏขึ้นในคอนโซลตัวจัดการแพคเกจของ NuGet

รหัสดังต่อไปนี้:

/// <summary>
/// Wrapper for SaveChanges adding the Validation Messages to the generated exception
/// </summary>
/// <param name="context">The context.</param>
private void SaveChanges(DbContext context) {
    try {
        context.SaveChanges();
    } catch (DbEntityValidationException ex) {
        StringBuilder sb = new StringBuilder();

        foreach (var failure in ex.EntityValidationErrors) {
            sb.AppendFormat("{0} failed validation\n", failure.Entry.Entity.GetType());
            foreach (var error in failure.ValidationErrors) {
                sb.AppendFormat("- {0} : {1}", error.PropertyName, error.ErrorMessage);
                sb.AppendLine();
            }
        }

        throw new DbEntityValidationException(
            "Entity Validation Failed - errors follow:\n" + 
            sb.ToString(), ex
        ); // Add the original exception as the innerException
    }
}

เพียงแทนที่การโทรcontext.SaveChanges()ด้วยSaveChanges(context)ในวิธีการเพาะของคุณ


ริชาร์ดในที่สุด! บางคนมีความคิด ฉันจะกลับมาที่คำถามนี้เมื่อฉันลอง
jeremy

นี้จริงๆช่วยติดตามการลง nasties :) ความ
Eminem

3
ฉันใช้เทคนิคนี้ แต่ใช้การแทนที่การบันทึกภายในบริบทแทน public override int SaveChanges() ภายในบริบท
Kirsten Greed

5
จะมีประสิทธิภาพมากกว่าการใช้คลาสบางส่วนตามที่ฉันตอบไว้ด้านล่าง
jwize

1
หากคุณกำลังดำเนินการ UserManager ในวิธีการ seed ของคุณการเปลี่ยนแปลงนี้จะไม่รวมถึงข้อผิดพลาดในการตรวจสอบความถูกต้องในการส่งออกคุณจะต้องแทนที่ DBContext SaveChanges, SaveChangesAsync และ SaveChangesAsync (CT) ตามวิธีการต่อ @jwize
Carl

115

ขยายคลาส DBContext ของคุณด้วยนิยามคลาสบางส่วนแล้ว!

ถ้าคุณดูคำจำกัดความของคลาสสำหรับ DbContext ของคุณมันจะเป็นดังนี้:

// DatabaseContext.cs   -- This file is auto generated and thus shouldn't be changed. 
public partial class [DatabaseContextName] : DbContext { ... }

ดังนั้นในไฟล์อื่นคุณสามารถสร้างคำจำกัดความเดียวกันและแทนที่ส่วนที่คุณต้องการ

// partialDatabaseContext.cs  -- you can safely make changes 
// that will not be overwritten in here.
public partial class [DatabaseContextName] : DbContext { // Override defaults here } 

ความคิดทั้งหมดกับการเรียนบางส่วน--did คุณสังเกตเห็นDbContextเป็น class-- บางส่วนคือการที่คุณสามารถขยายชั้นเรียนที่ได้รับการสร้าง (หรือจัดชั้นเรียนเป็นหลายไฟล์) และในกรณีที่เรายังต้องการที่จะแทนที่SaveChangesวิธี จากภายในชั้นเรียนบางส่วนที่จะเพิ่มการDbContext

วิธีนี้เราสามารถรับข้อมูลการแก้ไขข้อผิดพลาดจากการโทร DbContext / SaveChanges ที่มีอยู่ทั้งหมดได้ทุกที่โดยไม่ต้องเปลี่ยนรหัส Seed หรือรหัสการพัฒนาเลย

นี่คือสิ่งที่ฉันจะทำ ( หมายเหตุความแตกต่างคือฉันเพิ่งแทนที่วิธี SaveChanges ในชั้นเรียนDbContextบางส่วนของเราประพันธ์ไม่ใช่ผู้สร้าง ) นอกจากนี้ตรวจสอบให้แน่ใจว่าคุณแบ่งชั้นเรียนใช้เนมสเปซที่ถูกต้องไม่เช่นนั้นคุณจะต้องต่อสู้กับกำแพง

public partial class Database : DbContext
{
    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbEntityValidationException ex)
        {
            var sb = new StringBuilder();

            foreach (var failure in ex.EntityValidationErrors)
            {
                sb.AppendFormat("{0} failed validation\n", failure.Entry.Entity.GetType());
                foreach (var error in failure.ValidationErrors)
                {
                    sb.AppendFormat("- {0} : {1}", error.PropertyName, error.ErrorMessage);
                    sb.AppendLine();
                }
            }

            throw new DbEntityValidationException(
                "Entity Validation Failed - errors follow:\n" +
                sb.ToString(), ex
                ); // Add the original exception as the innerException
        }
    }
}

คุณคืออัจฉริยะ ...!
Florian F.

ทางออกที่ดี ผู้คนควรอ่านคำตอบทั้งหมดก่อนการถอนออก
Guilherme de Jesus Santos

3
คุณควรแทนที่ SaveChangesAsync และ SaveChangesAsync (CancellationToken) ด้วย - อย่างน้อยก็เป็นกรณีที่มีรหัสก่อนไม่แน่ใจเกี่ยวกับ model / db ก่อน
Carl

@jwize คำตอบของคุณช่วยฉันในฐานข้อมูลก่อนสร้างแบบจำลองปัญหาการจัดการข้อยกเว้น คำตอบที่ดี
3355307

1
เมื่อใช้ CodeFirst DbContext จะไม่ถูกสร้างขึ้นอย่างชัดเจน อย่างไรก็ตามเมื่อคุณใช้ผู้ออกแบบคลาส DbContext และ Entity จะถูกสร้างขึ้นและจะต้องถูกแทนที่ด้วยคลาสบางส่วน
jwize

35

ฉันแปลงคำตอบของ Richards เป็นวิธีส่วนขยาย:

  public static int SaveChangesWithErrors(this DbContext context)
    {
        try
        {
            return context.SaveChanges();
        }
        catch (DbEntityValidationException ex)
        {
            StringBuilder sb = new StringBuilder();

            foreach (var failure in ex.EntityValidationErrors)
            {
                sb.AppendFormat("{0} failed validation\n", failure.Entry.Entity.GetType());
                foreach (var error in failure.ValidationErrors)
                {
                    sb.AppendFormat("- {0} : {1}", error.PropertyName, error.ErrorMessage);
                    sb.AppendLine();
                }
            }

            throw new DbEntityValidationException(
                "Entity Validation Failed - errors follow:\n" +
                sb.ToString(), ex
            ); // Add the original exception as the innerException
        }
    }

โทรแบบนี้:

context.SaveChangesWithErrors();

4

ฉันแปลงเวอร์ชันของ craigvl เป็น C # ฉันต้องเพิ่ม context.SaveChanges (); เพื่อให้มันทำงานสำหรับฉันดังต่อไปนี้

try
{
    byte[] bytes = System.IO.File.ReadAllBytes(@"C:\Users\sheph_000\Desktop\Rawr.png");
    Console.WriteLine(bytes);

    context.BeverageTypes.AddOrUpdate(
        x => x.Name,
        new AATPos.DAL.Entities.BeverageType { ID = 1, Name = "Sodas" }
        );

    context.Beverages.AddOrUpdate(
        x => x.Name,
        new AATPos.DAL.Entities.Beverage { ID = 1, Name = "Coke", BeverageTypeID = 1, ImageData = bytes, IsStocked = true, StockLevel = 10, Price = 10.00M, ImageMimeType = "test" },
        new AATPos.DAL.Entities.Beverage { ID = 2, Name = "Fanta", BeverageTypeID = 1, ImageData = bytes, IsStocked = true, StockLevel = 10, Price = 10.00M, ImageMimeType = "test" },
        new AATPos.DAL.Entities.Beverage { ID = 3, Name = "Sprite", BeverageTypeID = 1, ImageData = bytes, IsStocked = true, StockLevel = 10, Price = 10.00M, ImageMimeType = "test" },
        new AATPos.DAL.Entities.Beverage { ID = 4, Name = "Cream Soda", BeverageTypeID = 1, ImageData = bytes, IsStocked = true, StockLevel = 10, Price = 10.00M, ImageMimeType = "test" },
        new AATPos.DAL.Entities.Beverage { ID = 5, Name = "Pepsi", BeverageTypeID = 1, ImageData = bytes, IsStocked = true, StockLevel = 10, Price = 10.00M, ImageMimeType = "test" }
        );

    context.SaveChanges();
}
catch (System.Data.Entity.Validation.DbEntityValidationException ex)
{
    var sb = new System.Text.StringBuilder();
    foreach (var failure in ex.EntityValidationErrors)
            {
                sb.AppendFormat("{0} failed validation", failure.Entry.Entity.GetType());
        foreach (var error in failure.ValidationErrors)
                {
            sb.AppendFormat("- {0} : {1}", error.PropertyName, error.ErrorMessage);
            sb.AppendLine();
                }
            }

    throw new Exception(sb.ToString());
}

3

Richard ขอบคุณที่ให้ฉันมาบนเส้นทางที่ถูกต้อง (มีปัญหาเดียวกัน) ด้านล่างนี้เป็นอีกทางเลือกหนึ่งโดยไม่มี wrapper ที่ใช้งานได้สำหรับฉันในวิธีการกำหนดค่าการย้ายข้อมูล:

 Protected Overrides Sub Seed(context As NotificationContext)

        Try
            context.System.AddOrUpdate(
               Function(c) c.SystemName,
                New E_NotificationSystem() With {.SystemName = "System1"},
                New E_NotificationSystem() With {.SystemName = "System2"},
                New E_NotificationSystem() With {.SystemName = "System3"})

            context.SaveChanges()

        Catch ex As DbEntityValidationException

            Dim sb As New StringBuilder

            For Each failure In ex.EntityValidationErrors

                sb.AppendFormat("{0} failed validation" & vbLf, failure.Entry.Entity.[GetType]())

                For Each [error] In failure.ValidationErrors
                    sb.AppendFormat("- {0} : {1}", [error].PropertyName, [error].ErrorMessage)
                    sb.AppendLine()
                Next
            Next

            Throw New Exception(sb.ToString())

        End Try
End Sub

จากนั้นสามารถดูข้อยกเว้นในคอนโซลตัวจัดการแพคเกจ หวังว่านี่จะช่วยใครซักคน


-1

I Also had same model validation problem but successfully catch by myself after lot of thinking;

I use reverse engineering method to catch the problem out of Over 80 + Model Classes;

1> Made copy of dbcontext, changing the name (I add "1" at end and make respective changes in class constructor and initialization etc.

Old:
 
>public class AppDb : IdentityDbContext<ApplicationUser>
>     
> {
> public AppDb(): base("DefaultConnection", throwIfV1Schema: false)
> {
> 
> }
>     
> public static AppDb Create()
>{
>return new AppDb();
>} 

**New:**

>public class AppDb1 : IdentityDbContext<ApplicationUser>
>{
>public AppDb1()
>: base("DefaultConnection", throwIfV1Schema: false)
>{
>}
> 
>public static AppDb1 Create()
> {
> return new AppDb1();
>  }`

...
2> Make changes to Codefirst Migration Configuration from Old DbContext to my new Context.

> internal sealed class Configuration :
> DbMigrationsConfiguration<DAL.AppDb1> { public Configuration() {
> AutomaticMigrationsEnabled = false; }    protected override void
> Seed(DAL.AppDb1 context) {`

3> Comment the Dbsets in new DbContext which was doubt.
4> Apply update migration if succeeded the probelm lye in Commented section.
5> if not then commented section is clear of bug clear.
6> repeat the (4) until found the right place of bug.
7> Happy Codding


1
จะดีเมื่อคุณจัดรูปแบบรหัสของคุณว่าข้อความของคุณไม่ได้อยู่ในบล็อกรหัส
jmattheis

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