Código EF Primeiro: como vejo a propriedade 'EntityValidationErrors' no console do pacote nuget?

127

Estou perplexo por isso:

Eu defini minhas classes para uma primeira abordagem de código da estrutura de entidade (4.1.3). Tudo estava bem (eu estava criando as tabelas etc.) até começar a Seed.

Agora quando eu faço o

Add-Migration "remigrate" ; Update-Database;

Eu recebo um erro no console do pacote "Falha na validação para uma ou mais entidades. Consulte a propriedade 'EntityValidationErrors' para obter mais detalhes."

Eu tenho um ponto de interrupção no meu método Seed (), mas como estou executando isso no console quando o projeto não está em execução, não tenho noção de como obter os detalhes (PS - vi a validação do thread falhou para uma ou mais entidades enquanto salva as alterações no Banco de Dados SQL Server usando o Entity Framework, que mostra como posso ver a propriedade.)

Eu sei que meu método Seed () tem um problema, porque se eu colocar um retorno logo após a chamada do método, o erro desaparece. Então, como defino meu ponto de interrupção para poder ver qual é o erro de validação? Meio perdido. Ou existe alguma outra maneira de rastreá-lo no console de pepitas?

Jeremy
fonte
Atualização rápida: resolvi meu problema rastreando sistematicamente cada variável no meu método até encontrar o que estava causando o erro. No entanto, eu ainda gostaria de saber a resposta para minha pergunta, pois isso seria muito mais rápido!
jeremy
Eu acho que você pode executar a migração programaticamente e, em seguida, capturar a exceção e repetir os erros. Não é o ideal, mas pode fornecer os detalhes necessários.
Pawel
Frustrante quando a resposta errada está no topo das respostas e recebe todo o crédito. Um lugar onde o StackOverflow claramente fica aquém!
jwize
Se você usa o Entity Framework, pode dar uma olhada na minha resposta na Solução para “Falha na validação de uma ou mais entidades. Consulte a propriedade 'EntityValidationErrors' para obter mais detalhes . Espero que isso ajude ...
Murat Yıldız

Respostas:

216

Também fiquei irritado com isso recentemente. Eu o corrigi colocando uma função de wrapper na classe Configuration no método Seed e substituindo as chamadas SaveChangespor chamadas para a minha função. Essa função simplesmente enumera os erros na EntityValidationErrorscoleção e retorna uma exceção em que a mensagem Exceção lista os problemas individuais. Isso faz com que a saída apareça no console do gerenciador de pacotes do NuGet.

O código a seguir:

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

Basta substituir as chamadas context.SaveChanges()por SaveChanges(context)no seu método de propagação.

Richard
fonte
Richard, finalmente! Alguém com uma ideia. Voltarei a esta pergunta depois de tentar.
Jeremy
Isso realmente ajuda a rastrear os males :)
Eminem
3
Eu usei essa técnica, mas usei uma substituição de alterações dentro do contexto. public override int SaveChanges() dentro do contexto.
precisa
5
É mais eficaz usando classes parciais como respondi abaixo.
jwize
1
Se você estiver executando operações do UserManager no seu método de propagação, essa alteração não incluirá os erros de validação na saída, será necessário substituir os métodos DBContext SaveChanges, SaveChangesAsync e SaveChangesAsync (CT) conforme a resposta @jwize.
Carl
115

Estenda sua classe DBContext já com uma definição de classe parcial!

Se você olhar para a definição de classe para o seu DbContext, será algo como o seguinte:

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

Portanto, em outro arquivo, você pode criar a mesma definição e substituir as partes que deseja.

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

Toda a ideia com classes parciais - você notou que o DbContext é uma classe parcial - é que você pode estender uma classe que foi gerada (ou organizar classes em vários arquivos) e, no nosso caso, também queremos substituir o método SaveChanges de dentro de uma classe parcial que adiciona ao DbContext .

Dessa forma, podemos obter informações de depuração de erros de todas as chamadas DbContext / SaveChanges existentes em todos os lugares e não precisamos alterar seu código Seed ou código de desenvolvimento.

Isto é o que eu faria ( note que a diferença é que eu apenas substituí o método SaveChanges na nossa classe parcial DbContext de autoria , NÃO NA GERADA ). Além disso, certifique-se de que a classe parcial use o espaço para nome correto ou você estará batendo a cabeça contra a parede.

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
fonte
Você é um gênio ...!
Florian F.
Ótima solução. As pessoas devem ler todas as respostas antes da votação.
Guilherme de Jesus Santos
3
Você também deve substituir SaveChangesAsync e SaveChangesAsync (CancellationToken) também - pelo menos esse é o caso do código primeiro, sem ter certeza do modelo / db primeiro.
Carl
@jwize. sua resposta me ajudou no meu banco de dados primeiro modelando problemas de manipulação de exceção. ótima resposta
3355307 15/05
1
Ao usar o CodeFirst, o DbContext obviamente não é gerado. No entanto, quando você usa o designer, as classes DbContext e Entity são geradas e devem ser substituídas usando uma classe parcial.
jwize
35

Eu converti a resposta de Richards para um método de extensão:

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

Ligue assim:

context.SaveChangesWithErrors();
Gordo
fonte
4

Eu converti a versão do craigvl para C # e tive que adicionar context.SaveChanges (); para que funcione para mim como abaixo.

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());
}
Ben Pretorius
fonte
3

Richard, obrigado por me colocar no caminho certo (tinha o mesmo problema) abaixo é uma alternativa sem o wrapper que funcionou para mim no método de semente da configuração de migração:

 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

Foi capaz de ver a exceção no console do gerenciador de pacotes. Espero que isso ajude alguém.

craigvl
fonte
-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

Muhammad Usama
fonte
1
Seria bom quando você formata seu código que seu texto não está dentro de um bloco de código.
Jttheis # 28/16
esta é provavelmente a pior resposta stackoverflow formatado que eu já vi
crazy_crank