web-dev-qa-db-fra.com

Recherche du motif de DBUpdateException

En appelant DbContext.SaveChanges, je reçois une DbUpdateException: 

Une exception de type non gérée 'System.Data.Entity.Infrastructure.DbUpdateException' s'est produite dans EntityFramework.dll. Informations complémentaires: Une erreur est survenue pendant le mettre à jour les entrées. Voir l'exception interne pour plus de détails.

Malheureusement, il n'y a pas d'exception interne (du moins, pas aussi loin que je peux voir). Existe-t-il un moyen de voir exactement pourquoi SaveChanges a lancé une exception? Au minimum, il serait utile de voir la table avec laquelle SaveChanges a tenté de se mettre à jour lorsque l'erreur s'est produite.

40
John Reynolds

Quand il semble que la véritable exception se perde quelque part, le mieux est de rompre à chaque exception. Qu'il soit attrapé ou avalé quelque part, à votre portée ou non, le débogueur se cassera et vous permettra de voir ce qui se passe.

Voir ce lien MSDN pour plus d'informations:

Comment: rompre quand une exception est levée

3
Crono

Ceci est mon remplacement de SaveChanges. Cela me donne un endroit utile pour mettre des points d'arrêt:

    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbEntityValidationException e)
        {
            foreach (var eve in e.EntityValidationErrors)
            {
                Debug.WriteLine(@"Entity of type ""{0}"" in state ""{1}"" 
                   has the following validation errors:",
                    eve.Entry.Entity.GetType().Name, 
                    eve.Entry.State);
                foreach (var ve in eve.ValidationErrors)
                {
                    Debug.WriteLine(@"- Property: ""{0}"", Error: ""{1}""",
                        ve.PropertyName, ve.ErrorMessage);
                }
            }
            throw;
        }
        catch(DbUpdateException e)
        {
           //Add your code to inspect the inner exception and/or
           //e.Entries here.
           //Or just use the debugger.
           //Added this catch (after the comments below) to make it more obvious 
           //how this code might help this specific problem
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.Message);
            throw;
        }
    }

Référence:

La validation a échoué pour une ou plusieurs entités. Voir la propriété 'EntityValidationErrors' pour plus de détails

26
Colin

Voici mon remplacement de SaveChanges, montrant le code supplémentaire pour traiter l'exception DbUpdateException (selon la question).

    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbEntityValidationException vex)
        {
            var exception = HandleDbEntityValidationException(vex);
            throw exception;
        }
        catch(DbUpdateException dbu)
        {
            var exception = HandleDbUpdateException(dbu);
            throw exception;
        }
    }

    private Exception HandleDbUpdateException(DbUpdateException dbu)
    {
        var builder = new StringBuilder("A DbUpdateException was caught while saving changes. ");

        try
        {
            foreach (var result in dbu.Entries)
            {
                builder.AppendFormat("Type: {0} was part of the problem. ", result.Entity.GetType().Name);
            }
        }
        catch (Exception e)
        {
            builder.Append("Error parsing DbUpdateException: " + e.ToString());
        }

        string message = builder.ToString();
        return new Exception(message, dbu);
    }

Je n'ai pas précisé le code de journalisation, mais cela améliore le message d'erreur standard de quelque chose comme:

The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value.

De cette façon, au moins, je peux voir quelle entité a le problème, ce qui est normalement suffisant pour résoudre le problème. 

15
a-h

D'après la réponse de Colin, des informations détaillées sur l'échec de la persistance d'EF peuvent être fournies comme suit:

public bool SaveChangesEx()
{
    try
    {
        SaveChanges();
        return true;
    }
    catch (DbEntityValidationException exc)
    {
        // just to ease debugging
        foreach (var error in exc.EntityValidationErrors)
        {
            foreach (var errorMsg in error.ValidationErrors)
            {
                // logging service based on NLog
                Logger.Log(LogLevel.Error, $"Error trying to save EF changes - {errorMsg.ErrorMessage}");
            }
        }

        throw;
    }
    catch (DbUpdateException e)
    {
        var sb = new StringBuilder();
        sb.AppendLine($"DbUpdateException error details - {e?.InnerException?.InnerException?.Message}");

        foreach (var eve in e.Entries)
        {
            sb.AppendLine($"Entity of type {eve.Entity.GetType().Name} in state {eve.State} could not be updated");
        }

        Logger.Log(LogLevel.Error, e, sb.ToString());

        throw;
    }
}

Outre les erreurs de validation, l'exception de mise à jour générera à la fois des informations d'erreur générales et des informations de contexte.

Remarque: C # 6.0 est requis pour que ce code fonctionne, car il utilise une propagation nulle et une interpolation de chaîne.


Pour .NET Core, le code est légèrement modifié car les éventuelles exceptions déclenchées ont une structure différente/sont renseignées différemment:

    public void SaveChangesEx()
    {
        try
        {
            // this triggers defined validations such as required
            Context.Validate();
            // actual save of changes
            Context.SaveChangesInner();
        }
        catch (ValidationException exc)
        {
            Logger.LogError(exc, $"{nameof(SaveChanges)} validation exception: {exc?.Message}");
            throw;
        }
        catch (DbUpdateException exc)
        {
            Logger.LogError(exc, $"{nameof(SaveChanges)} db update error: {exc?.InnerException?.Message}");
            throw;
        }
        catch (Exception exc)
        {
            // should never reach here. If it does, handle the more specific exception
            Logger.LogError(exc, $"{nameof(SaveChanges)} generic error: {exc.Message}");
            throw;
        }
    }

Le contexte peut être amélioré pour rejeter automatiquement les modifications en cas d'échec, si le même contexte n'est pas immédiatement éliminé:

public void RejectChanges()
{
    foreach (var entry in ChangeTracker.Entries().Where(e => e.Entity != null).ToList())
    {
        switch (entry.State)
        {
            case EntityState.Modified:
            case EntityState.Deleted:
                entry.State = EntityState.Modified; //Revert changes made to deleted entity.
                entry.State = EntityState.Unchanged;
                break;
            case EntityState.Added:
                entry.State = EntityState.Detached;
                break;
        }
    }
}

public bool SaveChangesInner()
{
    try
    {
        SaveChanges();
        return true;
    }
    catch (Exception)
    {
        RejectChanges();
        throw;
    }
}
7
Alexei

J'avais la même erreur "La conversion d'un type de données datetime2 en un type de données datetime a entraîné une valeur hors limites.\R\nL'instruction a été arrêtée".

Je mets le champ de valeur Datetime comme ceci Datetime.Now

var person = new Person
            {
               FirstName = "Sebastian",
               LastName = "Back",
               **BirthDate = DateTime.Now,**
               IsActive = true,
            };
2
Irving Saul Quiroz

J'ai la même erreur et résoudre comme ça;

J'ai un champ dont le type de données est datetime et je suis conscient que je le mets sans propriété datetime assignée. Donc je le change en " Datetime.Now " alors je vois que le problème a été résolu et qu'il a été enregistré avec succès

0
Taha Karaca

Dans mon cas, j'utilisais la procédure stockée pour ajouter une entité à la base de données. Mon SP donne l'erreur d'exécution à cause de laquelle je recevais cette erreur en C #. J'ai corrigé le SP et le EF fonctionnait parfaitement ... Donc, si vous utilisez SP pour ajouter, modifier, supprimer ou mettre à jour dans EF, vérifiez si le sp correspondant fonctionne correctement. 

0
yogihosting