أريد حفظ التحرير الخاص بي في قاعدة البيانات وأنا أستخدم Entity FrameWork Code-First في ASP.NET MVC 3/C # لكنني أتلقى أخطاء. في فئة الأحداث الخاصة بي ، لدي أنواع بيانات DateTime و TimeSpan ، لكن في قاعدة البيانات الخاصة بي ، حصلت على Date and time على التوالي. يمكن أن يكون هذا السبب؟ كيف يمكنني الإدلاء بنوع البيانات المناسب في التعليمات البرمجية قبل حفظ التغييرات في قاعدة البيانات.
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; }
}
الطريقة في وحدة التحكم >>>> مشكلة في 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);
}
}
مع
public class EventCalendarEntities : DbContext
{
public DbSet<Event> Events { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Place> Places { get; set; }
}
SQL Server 2008 R2 قاعدة البيانات/T-SQL
EventDate (Datatype = date)
StartTime (Datatype = time)
EndTime (Datatype = time)
شكل المتشعب
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
خطأ في الخادم في التطبيق '/'.
فشل التحقق من الصحة لكيان واحد أو أكثر. راجع خاصية "EntityValidationErrors" لمزيد من التفاصيل.
الوصف: حدث استثناء غير معالج أثناء تنفيذ طلب الويب الحالي. يرجى مراجعة تتبع الرصة لمزيد من المعلومات حول الخطأ ومكان نشأته في الكود.
تفاصيل الاستثناء: System.Data.Entity.Validation.DbEntityValidationException: فشل التحقق من الصحة لواحد أو أكثر من الكيانات. راجع خاصية "EntityValidationErrors" لمزيد من التفاصيل.
خطأ المصدر:
Line 75: if (TryUpdateModel(theEvent))
Line 76: {
Line 77: storeDB.SaveChanges();
Line 78: return RedirectToAction("Index");
Line 79: }
ملف المصدر: C:\sep\MvcEventCalendar\MvcEventCalendar\Controllers\EventManagerController.cs الخط: 77
تتبع المكدس:
[DbEntityValidationException: فشل التحقق من الصحة لكيان واحد أو أكثر. راجع خاصية "EntityValidationErrors" لمزيد من التفاصيل.]
يمكنك استخراج جميع المعلومات من DbEntityValidationException
بالكود التالي (تحتاج إلى إضافة مساحات الأسماء: System.Data.Entity.Validation
و System.Diagnostics
إلى قائمة 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);
}
}
}
لا يلزم تغيير الرمز:
أثناء وجودك في وضع التصحيح داخل كتلة catch {...}
، افتح نافذة "QuickWatch" (Ctrl+Alt+Q) ولصق في هناك:
((System.Data.Entity.Validation.DbEntityValidationException)ex).EntityValidationErrors
أو:
((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
إذا لم تكن في موقع try/catch أو لا يمكنك الوصول إلى كائن الاستثناء.
سيسمح لك ذلك بالبحث في شجرة ValidationErrors
. إنها أسهل طريقة وجدتها للحصول على نظرة ثاقبة فورية لهذه الأخطاء.
في حالة وجود فئات لها نفس أسماء الممتلكات ، إليك ملحق صغير لإجابة برافين:
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);
}
}
}
كتحسين لكل من برافين وتوني ، أستخدم تجاوزًا:
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...
}
}
}
هذا الاستثناء التفاف كيان التنفيذ إلى استثناء مع نص التفاصيل. يعالج أخطاء النطاق DbEntityValidationException
، DbUpdateException
، datetime2
(MS SQL) ، ويتضمن مفتاح الكيان غير الصحيح في الرسالة (مفيد عند استحضار العديد من الكيانات في مكالمة واحدة SaveChanges
).
أولاً ، تجاوز SaveChanges
في فئة 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);
}
}
فئة 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);
}
}
ساعد هذا الرمز في العثور على مشكلتي عندما واجهت مشكلة مع Entity VAlidation Erros. لقد أخبرتني المشكلة بالضبط في تعريف الكيان الخاص بي. حاول اتباع التعليمات البرمجية حيث تريد تغطية storeDB.SaveChanges ()؛ في محاولة التالية كتلة الصيد.
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;
}
كنت أتلقى هذا الخطأ اليوم ولم أتمكن من حل المشكلة لفترة من الوقت ، لكنني أدركت أنه بعد إضافة بعض RequireAttribute
s إلى النماذج الخاصة بي وأن بعض البيانات الأولية للتطوير لم تملأ جميع الحقول المطلوبة.
لذا ، لاحظ فقط أنه إذا تلقيت هذا الخطأ أثناء تحديث قاعدة البيانات من خلال نوع من استراتيجية التهيئة مثل DropCreateDatabaseIfModelChanges
، فيتعين عليك التأكد من أن بياناتك الأولية تفي وتلبي أي سمات للتحقق من صحة بيانات النموذج .
أعلم أن هذا يختلف قليلاً عن المشكلة في السؤال ، لكنه سؤال شائع لذا اعتقدت أنني سأضيف أكثر قليلاً إلى الإجابة بالنسبة للآخرين الذين لديهم نفس المشكلة مثل نفسي.
آمل أن يساعد هذا الآخرين :)
أعتقد أن إضافة تجربة/تجربة لكل عملية SaveChanges()
ليست ممارسة جيدة ، فمن الأفضل أن تتمركز هذه:
أضف هذه الفئة إلى فئة 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);
}
}
سيؤدي هذا إلى الكتابة فوق الأسلوب SaveChanges()
الخاص بالسياق وستحصل على قائمة مفصولة بفواصل تحتوي على جميع أخطاء التحقق من الكيان.
يمكن تحسين هذا أيضًا ، لتسجيل الأخطاء في env الإنتاج ، بدلاً من مجرد إلقاء خطأ.
آمل أن يكون هذا مفيدًا.
إليك امتدادًا لملحق Tony ... :-)
بالنسبة إلى Entity Framework 4.x ، إذا كنت ترغب في الحصول على اسم وقيمة حقل المفتاح بحيث تعرف مثيل الكيان (سجل DB) الذي يواجه المشكلة ، يمكنك إضافة ما يلي. يوفر هذا الوصول إلى أعضاء الفئة ObjectContext أكثر قوة من كائن 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;
لا أحب استثناءات لقد سجلت OnSaveChanges ولدي هذا
var validationErrors = model.GetValidationErrors();
var h = validationErrors.SelectMany(x => x.ValidationErrors
.Select(f => "Entity: "
+(x.Entry.Entity)
+ " : " + f.PropertyName
+ "->" + f.ErrorMessage));
تأكد من أنه إذا كان لديك nvarchar (50) في صف DB ، فلن تحاول إدراج أكثر من 50 حرفًا فيه. خطأ غبي لكنه استغرق مني 3 ساعات لمعرفة ذلك.
يحدث هذا الخطأ أيضًا عند محاولة حفظ كيان به أخطاء في التحقق من الصحة. هناك طريقة جيدة لإحداث ذلك هي نسيان التحقق من ModelState.IsValid قبل الحفظ في قاعدة بياناتك.
لقد واجهت نفس المشكلة قبل يومين أثناء تحديث قاعدة البيانات. في حالتي ، كان هناك عدد قليل من الأعمدة غير القابلة للإلغاء الجديدة التي تمت إضافتها للصيانة والتي لم يتم توفيرها في التعليمات البرمجية التي تسبب الاستثناء. أنا معرفة هذه الحقول والقيم المقدمة لهم وحلها.
قد يكون هذا بسبب الحد الأقصى لعدد الأحرف المسموح بها لعمود معين ، كما هو الحال في الحقل sql واحد قد يتبع نوع البيانات nvarchar(5)
ولكن عدد الأحرف التي تم إدخالها من المستخدم أكبر من المحدد ، وبالتالي ينشأ الخطأ.
قد يكون سببها خاصية غير مملوءة بالنموذج .. بدلاً من ذلك يتم ملؤها بواسطة وحدة التحكم .. والتي قد تسبب هذا الخطأ .. الحل لهذا هو تعيين الخاصية قبل تطبيق التحقق من صحة ModelState. وهذا الافتراض الثاني هو. ربما يكون لديك بالفعل بيانات في قاعدة البيانات الخاصة بك وتحاول تحديثها ولكن الآن تجلبها.
شكراً لإجاباتك ، ساعدني كثيرًا. كما أنا رمز في Vb.Net ، هذا رمز بولت ل 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
في حالتي لدي اسم عمود الجدول ، المسار الذي حددته هو varchar (200). بعد تحديثه إلى nvarchar (بحد أقصى) ، قمت بحذف الجدول من edmx ثم أضفته مرة أخرى إلى الجدول و wokred بشكل صحيح بالنسبة لي.