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.]
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);
}
}
}
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.
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);
}
}
}
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...
}
}
}
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);
}
}
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 :)
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.
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;
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));
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.
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.
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.
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 .
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.
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
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.