web-dev-qa-db-fra.com

La validation a échoué pour une ou plusieurs entités lors de l'enregistrement des modifications dans la base de données SQL Server à l'aide de Entity Framework

Je souhaite enregistrer mon édition dans la base de données et j'utilise Entity FrameWork Code-First dans ASP.NET MVC 3/C #, mais des erreurs se produisent. Dans ma classe Event, j'ai des types de données DateTime et TimeSpan, mais dans ma base de données, j'ai respectivement Date et heure. Cela pourrait-il être la raison? Comment puis-je transtyper le type de données approprié dans le code avant d'enregistrer les modifications dans la base de données.

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

Méthode dans le contrôleur >>>> Problème sur 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);
    }
}

avec

public class EventCalendarEntities : DbContext
{
    public DbSet<Event> Events { get; set; }
    public DbSet<Category> Categories { get; set; }
    public DbSet<Place> Places { get; set; } 
}

Base de données SQL Server 2008 R2/T-SQL

EventDate (Datatype = date)  
StartTime (Datatype = time)  
EndTime (Datatype = time)  

Formulaire 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  

Erreur serveur dans l'application '/'.

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

Description: une exception non gérée s'est produite lors de l'exécution de la demande Web en cours. Consultez la trace de la pile pour plus d'informations sur l'erreur et son origine dans le code.

Détails des exceptions: System.Data.Entity.Validation.DbEntityValidationException: la validation a échoué pour une ou plusieurs entités. Voir la propriété 'EntityValidationErrors' pour plus de détails.

Erreur de source:

Line 75:             if (TryUpdateModel(theEvent))
Line 76:             {
Line 77:                 storeDB.SaveChanges();
Line 78:                 return RedirectToAction("Index");
Line 79:             }

Fichier source: C:\sep\MvcEventCalendar\MvcEventCalendar\Controllers\EventManagerController.cs Ligne: 77

Trace de la pile:

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

331
user522767

Vous pouvez extraire toutes les informations de la DbEntityValidationException avec le code suivant (vous devez ajouter les espaces de noms: System.Data.Entity.Validation et System.Diagnostics à votre liste 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);
        }
    }
}
838
Praveen Prasad

Aucun changement de code requis:

Lorsque vous êtes en mode débogage dans le bloc catch {...}, ouvrez la fenêtre "QuickWatch" (Ctrl+Alt+Q) et coller dedans:

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

ou:

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

Si vous n'êtes pas dans un try/catch ou n'avez pas accès à l'objet exception.

Cela vous permettra d’avancer dans l’arborescence ValidationErrors. C'est le moyen le plus simple que j'ai trouvé d'obtenir un aperçu instantané de ces erreurs.

248
GONeale

Si vous avez des classes avec les mêmes noms de propriétés, voici une petite extension de la réponse de 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);
       }
    }
 }
37
Tony

Pour améliorer Praveen et Tony, j'utilise une substitution:

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...
        }
    }
}
22
Bolt Thunder

Cette implémentation encapsule l'exception entité à l'exception avec un texte de détail. Il traite DbEntityValidationException, DbUpdateException, datetime2 erreurs de plage (MS SQL) et inclut la clé de l'entité non valide dans le message (utile lorsque plusieurs entités sont sauvegardées lors d'un appel SaveChanges.).

Commencez par remplacer SaveChanges dans la classe 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);
        }
    }

Classe 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);
    }
}
6
Sel

Ce code a aidé à trouver mon problème lorsque j'ai eu un problème avec mon Entity VAlidation Erros. Il m'a dit le problème exact avec ma définition d'entité. Essayez de suivre le code où vous devez couvrir storeDB.SaveChanges (); en suivant essayez 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;
}

Je recevais cette erreur aujourd'hui et je n'ai pas pu résoudre le problème pendant un moment, mais j'ai réalisé que c'était après avoir ajouté quelques RequireAttribute à mes modèles et que certaines données de base de développement ne renseignaient pas tous les champs requis.
Il suffit donc de noter que si vous obtenez cette erreur lors de la mise à jour de la base de données via une sorte de stratégie d’initialisation comme DropCreateDatabaseIfModelChanges, vous devez vous assurer que votre données de base remplit et satisfait attributs de validation des données du modèle.

Je sais que cela est légèrement différent du problème de la question, mais c'est une question populaire, alors j'ai pensé ajouter un peu plus à la réponse pour ceux qui ont le même problème que moi.
Espérons que cela aide les autres :)

4
dan richardson

Je pense qu'ajouter essayer/attraper pour chaque opération SaveChanges() n'est pas une bonne pratique, il est préférable de centraliser ceci:

Ajoutez cette classe à la classe principale 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);
    }
}

Cela écrasera la méthode SaveChanges() de votre contexte et vous obtiendrez une liste séparée par des virgules contenant toutes les erreurs de validation d'entité.

cela peut aussi être amélioré, pour enregistrer les erreurs dans les envs de production, au lieu de simplement lancer une erreur.

j'espère que c'est utile.

4
Chtiwi Malek

Voici une extension de l'extension de Tony ... :-)

Pour Entity Framework 4.x, si vous souhaitez obtenir le nom et la valeur du champ clé afin de savoir quelle instance d'entité (enregistrement de base de données) présente le problème, vous pouvez ajouter ce qui suit. Cela donne accès aux membres de la classe ObjectContext plus puissants à partir de votre objet 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
Martin_ATS

Je n'aime pas les exceptions. J'ai enregistré OnSaveChanges et j'ai ceci

var validationErrors = model.GetValidationErrors();

var h = validationErrors.SelectMany(x => x.ValidationErrors
                                          .Select(f => "Entity: " 
                                                      +(x.Entry.Entity) 
                                                      + " : " + f.PropertyName 
                                                      + "->" + f.ErrorMessage));
3
Mickey Perlstein

Assurez-vous que si vous avez nvarchar (50) dans la ligne de base de données, vous n'essayez pas d'y insérer plus de 50 caractères. Erreur stupide mais m'a pris 3 heures pour le comprendre.

2
user4030144

Cette erreur se produit également lorsque vous essayez de sauvegarder une entité comportant des erreurs de validation. Une bonne façon de le faire est d'oublier de vérifier ModelState.IsValid avant de l'enregistrer dans votre base de données.

2
laylarenee

J'ai rencontré le même problème il y a quelques jours lors de la mise à jour de la base de données. Dans mon cas, peu de nouvelles colonnes non nullables ont été ajoutées pour la maintenance et n'ont pas été fournies dans le code à l'origine de l'exception. Je découvre ces champs et leur fournit des valeurs et leur résolution.

1
JM Kumawat

Cela peut être dû au nombre maximal de caractères autorisé pour une colonne spécifique, comme dans SQL. Un champ peut avoir le type de données suivant nvarchar(5) mais le nombre de caractères saisis par l'utilisateur est supérieur au nombre spécifié, d'où l'erreur .

1
lokopi

elle peut être provoquée par une propriété qui n'est pas renseignée par le modèle .. à la place, elle est renseignée par le contrôleur .., ce qui peut entraîner cette erreur .. La solution à cela est d'affecter la propriété avant d'appliquer la validation ModelState. et cette seconde hypothèse est. Vous avez peut-être déjà des données dans votre base de données et vous essayez de les mettre à jour, mais vous les récupérez maintenant.

0
Muhammad Mustafa

Merci pour vos réponses, cela m'aide beaucoup. comme je code dans Vb.Net, ce code Bolt pour 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
Exatex

Dans mon cas, j'ai un nom de colonne de table Path dont le type de données i était varchar (200). Après l'avoir mis à jour en nvarchar (max), j'ai supprimé la table d'edmx, puis ajouté à nouveau la table et elle l'a été correctement.

0
Saket Singh