c# شرح - فشل التحقق من الصحة لواحد أو أكثر من الكيانات أثناء حفظ التغييرات في قاعدة بيانات خادم SQL باستخدام Entity Framework





tutorial source (14)


هذا استثناء كيان التفاف التطبيق للاستثناء بنص التفاصيل. فإنه يعالج DbEntityValidationException ، DbUpdateException ، وأخطاء النطاق datetime2 (MS SQL) ، وتشمل مفتاح كيان غير صالح في الرسالة (مفيدة عند العديد من الكيانات SaveChanges استدعاء 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);
        }
    }

فئة الاستثناءات:

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 FrameWork الأول في ASP.NET MVC 3 / C # ولكنني أواجه أخطاء. في فئة الحدث الخاص بي ، لدي أنواع البيانات DateTime و TimeSpan ولكن في قاعدة البيانات الخاصة بي ، لقد حصلت على التاريخ والوقت على التوالي. يمكن أن يكون هذا هو السبب؟ كيف يمكنني الإرسال إلى نوع البيانات المناسب في التعليمة البرمجية قبل حفظ التغييرات في قاعدة البيانات.

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 Database / 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" لمزيد من التفاصيل.]




أنا لا أحب الاستثناءات سجلت OnSaveChanges ولدي هذا

var validationErrors = model.GetValidationErrors();

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



قد يكون هذا بسبب الحد الأقصى لعدد الأحرف المسموح بها لعمود معين ، كما هو الحال في sql ، قد يكون حقل واحد يتبع نوع البيانات nvarchar(5) ولكن عدد الأحرف التي تم إدخالها من المستخدم أكبر من العدد المحدد ، وبالتالي يظهر الخطأ.




قد يحدث ذلك بسبب الخاصية التي لا يتم نشرها بالنموذج .. بدلاً من ذلك يتم ملؤها بواسطة وحدة التحكم .. والتي قد تتسبب في حدوث هذا الخطأ. حل هذا هو تعيين الخاصية قبل تطبيق التحقق من صحة ModelState. وهذا الافتراض الثاني هو. قد يكون لديك بالفعل بيانات في قاعدة البيانات الخاصة بك ومحاولة تحديثها ولكن الآن في جلبها.




أعتقد أن إضافة المحاولة / الالتقاط لكل عملية 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() الخاصة SaveChanges() وستحصل على قائمة مفصولة بفواصل تحتوي على جميع أخطاء التحقق من الكيانات.

هذا أيضا يمكن أن تتحسن ، لتسجيل الأخطاء في env الإنتاج ، بدلاً من مجرد رمي خطأ.

أتمنى أن يكون هذا مفيدًا.




يمكنك استخراج كافة المعلومات من 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);
        }
    }
}



ساعد هذا الرمز في العثور على مشكلتي عندما واجهت مشكلة مع Erg 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;
}



لا تغيير الرمز المطلوب:

أثناء وجودك في وضع التصحيح داخل كتلة catch {...} افتح النافذة "QuickWatch" ( Ctrl + Alt + Q ) والصق هناك:

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

أو:

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

إذا لم تكن في تجربة / تجربة أو لم يكن لديك حق الوصول إلى كائن الاستثناء.

سيسمح لك هذا ValidationErrors شجرة ValidationErrors . إنها الطريقة الأسهل التي وجدتها للحصول على إحصاءات فورية عن هذه الأخطاء.




كنت أتلقى هذا الخطأ اليوم ولم أستطع استخدامه لبعض الوقت ، لكنني أدركت أنه كان بعد إضافة بعض RequireAttribute إلى RequireAttribute وأن بعض بيانات بذور التنمية لم تكن تملأ جميع الحقول المطلوبة.
إذن مجرد ملاحظة أنه إذا كنت تحصل على هذا الخطأ أثناء تحديث قاعدة البيانات من خلال نوع من استراتيجية DropCreateDatabaseIfModelChanges مثل DropCreateDatabaseIfModelChanges فيجب عليك التأكد من أن بيانات البذور الخاصة بك تلبي وتفي بأي خصائص التحقق من صحة بيانات النموذج .

أعلم أن هذا يختلف قليلاً عن المشكلة في السؤال ، ولكنه سؤال شائع لذلك أعتقد أني سأضيف أكثر قليلاً إلى الإجابة للآخرين الذين لديهم نفس المشكلة مثلي.
آمل أن يساعد هذا :) :)




في حالة وجود فصول دراسية بأسماء المواقع نفسها ، فإليك امتدادًا صغيرًا لجواب برافين:

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



Thnaks لإجاباتك ، فإنه يساعدني كثيرا. كما أنا كود في 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



تأكد من أنه إذا كان لديك nvarchar (50) في صف DB فأنت لا تحاول إدخال أكثر من 50 حرفًا في ذلك. خطأ غبي لكنه أخذني 3 ساعات لأكتشفه.




لقد واجهت نفس المشكلة قبل بضعة أيام أثناء تحديث قاعدة البيانات. في حالتي ، كان هناك عدد قليل من الأعمدة الجديدة غير القابلة للإضافة التي تمت إضافتها للصيانة والتي لم يتم توفيرها في التعليمة البرمجية التي تتسبب في الاستثناء. أجد تلك الحقول وأمدنا بها بالقيم وحللتها.




أعتقد أن الإجابة السريعة والقذرة هي

  • LINQ إلى SQL هي طريقة سريعة وسهلة للقيام بذلك. هذا يعني أنك سوف تحصل على أسرع ، وتقديم أسرع إذا كنت تعمل على شيء أصغر.
  • إطار الكيان هو طريقة شاملة وغير محظورة للقيام بذلك. وهذا يعني أنك ستستغرق وقتًا أطول في التقدم ، وتتطور بشكل أبطأ ، وتزيد من المرونة إذا كنت تعمل على شيء أكبر.




c# sql asp.net-mvc entity-framework code-first