EF Code First: как мне увидеть свойство EntityValidationErrors из консоли пакета nuget?

127

Я в недоумении:

Я определил свои классы для подхода, основанного на коде структуры сущностей (4.1.3). Все было хорошо (я создавал таблицы и т. Д.), Пока не начал Seed.

Теперь, когда я делаю

Add-Migration "remigrate" ; Update-Database;

Я получаю сообщение об ошибке в консоли пакета «Ошибка проверки для одной или нескольких сущностей. Для получения дополнительной информации см. Свойство EntityValidationErrors».

У меня есть точка останова в моем методе Seed (), но поскольку я запускаю ее на консоли, когда проект не запущен, я не знаю, как добраться до деталей (PS - я видел, что проверка потока не удалась для одной или нескольких сущностей при сохранении изменений в базе данных SQL Server с помощью Entity Framework, которая показывает, как я могу видеть свойство.)

Я знаю, что у моего метода Seed () есть проблема, потому что, если я помещаю возврат сразу после вызова метода, ошибка исчезает. Итак, как мне установить точку останова, чтобы я мог видеть, в чем ошибка проверки? Типа проиграл. Или есть другой способ отследить это в консоли nuget ??

Jeremy
источник
Быстрое обновление: я решил свою проблему, систематически отслеживая каждую переменную в моем методе, пока не нашел причину ошибки. Однако я все же хотел бы узнать ответ на свой вопрос, так как это будет намного быстрее!
Джереми
Я думаю, вы можете запустить миграцию программно, а затем перехватить исключение и повторить ошибки. Это не идеально, но может дать вам необходимую информацию.
Павел
Расстраивает, когда неправильный ответ стоит на первом месте и получает всю заслугу. Место, где StackOverflow явно проигрывает!
jwize
Если вы используете Entity Framework, вы можете взглянуть на мой ответ о решении для «Ошибка проверки для одной или нескольких сущностей. Для получения дополнительных сведений см. Свойство EntityValidationErrors . Надеюсь, это поможет ...
Мурат Йылдыз

Ответы:

216

Меня это тоже недавно раздражало. Я исправил это, поместив функцию-оболочку в класс 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)в методе семян.

Ричард
источник
Ричард, наконец! Кто-то с идеей. Я вернусь к этому вопросу, когда попробую.
Джереми
Это действительно помогает отслеживать гадостей :)
Eminem
3
Я использовал эту технику, но вместо этого использовал переопределение savechanges внутри контекста. public override int SaveChanges() внутри контекста.
Kirsten Greed
5
Как я ответил ниже, более эффективно использовать частичные классы.
jwize
1
Если вы выполняете операции UserManager в своем исходном методе, это изменение не будет включать ошибки проверки в выходных данных, вам потребуется переопределить методы DBContext SaveChanges, SaveChangesAsync и SaveChangesAsync (CT) в соответствии с ответом @jwize.
Карл
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
        }
    }
}
jwize
источник
Ты гений ...!
Флориан Ф.
Отличное решение. Люди должны прочитать все ответы перед голосованием.
Гильерме де Хесус Сантос
3
Вы также должны переопределить SaveChangesAsync и SaveChangesAsync (CancellationToken) - по крайней мере, сначала так обстоит дело с кодом, а сначала не уверен в модели / db.
Карл
@jwize. Ваш ответ помог мне в моей базе данных сначала моделировать проблемы с обработкой исключений. отличный ответ
3355307
1
При использовании CodeFirst DbContext явно не создается. Однако, когда вы используете конструктор, создаются классы DbContext и Entity, которые необходимо переопределить с помощью частичного класса.
jwize
35

Я преобразовал ответ Ричардса в метод расширения:

  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

Ричард благодарит меня за то, что направил меня по правильному пути (была такая же проблема). Ниже представлена ​​альтернатива без оболочки, которая сработала для меня в методе начального значения конфигурации миграции:

 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

Затем удалось увидеть исключение в консоли диспетчера пакетов. Надеюсь, это кому-то поможет.

craigvl
источник
-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