DbEntityValidationException - Hataya neyin neden olduğunu kolayca nasıl anlayabilirim?


217

Entity Framework kullanan bir projem var. SaveChangesBenim çağrı yaparken DbContext, aşağıdaki istisnayı alıyorum:

System.Data.Entity.Validation.DbEntityValidationException: Bir veya daha fazla varlık için doğrulama başarısız oldu. Daha fazla ayrıntı için 'EntityValidationErrors' özelliğine bakın.

Bu iyi ve züppe, ama bu istisna her oluştuğunda bir hata ayıklayıcı eklemek istemiyorum. Dahası, üretim ortamlarında kolayca bir hata ayıklayıcı ekleyemiyorum, bu yüzden bu hataları yeniden oluşturmak için büyük uzunluklara gitmem gerekiyor.

İçindeki gizli ayrıntıları nasıl görebilirim DbEntityValidationException?

Yanıtlar:


430

En kolay çözüm SaveChangesvarlık sınıfınızı geçersiz kılmaktır. Geliştirilmiş mesajla yakalayabilir, DbEntityValidationExceptiongerçek hataları açabilir ve yeni bir DbEntityValidationExceptionmesaj oluşturabilirsiniz.

  1. SomethingSomething.Context.cs dosyanızın yanında kısmi bir sınıf oluşturun.
  2. Bu yazının altındaki kodu kullanın.
  3. Bu kadar. Uygulamanız, herhangi bir refactor çalışması olmadan, geçersiz kılınan SaveChanges'i otomatik olarak kullanacaktır.

İstisna mesajınız şimdi şöyle görünecek:

System.Data.Entity.Validation.DbEntityValidationException: Bir veya daha fazla varlık için doğrulama başarısız oldu. Daha fazla ayrıntı için 'EntityValidationErrors' özelliğine bakın. Doğrulama hataları şunlardır: PhoneNumber alanı, maksimum uzunluğu '12' olan bir dize veya dizi türü olmalıdır; Soyadı alanı zorunludur.

Geçersiz kılınan SaveChanges'i, aşağıdakilerden devralan herhangi bir sınıfa bırakabilirsiniz DbContext:

public partial class SomethingSomethingEntities
{
    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (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);
    
            // Throw a new DbEntityValidationException with the improved exception message.
            throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors);
        }
    }
}

DbEntityValidationExceptionAyrıca doğrulama hataları neden varlıkları içerir. Daha fazla bilgiye ihtiyacınız varsa, bu varlıklar hakkında bilgi almak için yukarıdaki kodu değiştirebilirsiniz.

Ayrıca bkz: http://devillers.nl/improving-dbentityvalidationexception/


6
Oluşturulan Entities sınıfı zaten DbContext'ten miras alır, böylece kısmi sınıfa tekrar eklemenize gerek kalmaz. Kısmi sınıfa ekleyerek hiçbir şeyi kıramaz veya değiştiremezsiniz. Aslında, DbContext'den devralma eklerseniz Resharper onu kaldırmanızı önerir: "Taban türü 'DbContext' zaten diğer bölümlerde belirtilmiş."
Martin Devillers

15
Neden SaveChanges'in varsayılan davranışı bu değil?
John Shedletsky

4
"Bu neden SaveChanges'in varsayılan davranışı değil?" - Bu gerçekten iyi bir soru. Bu inanılmaz bir çözümdü, bana saatler kazandırdı! using System.Linq;
John August

1
Oluşturma Görünümü, try bloğunda bulunan base.SaveChanges () dizinindeki hataları görüntüler. Asla yakalama bloğuna atlamaz. SaveChanges üzerinde binmek için kod var ama asla hata Catch Block içine alır.
JustJohn

7
Yığın izini korumak için iç istisnayı ayarlamanız gerekir.
dotjoe

48

Martin'in belirttiği gibi DbEntityValidationResult,. Her iletide hem POCO sınıfımın adını hem de özellik adını almayı yararlı buldum ve sadece bunun için ErrorMessagetüm [Required]etiketlerime özel özellikler yazmaktan kaçınmak istedim .

Martin'in koduna yapılan aşağıdaki ince ayar benim için bu ayrıntıları ele aldı:

// Retrieve the error messages as a list of strings.
List<string> errorMessages = new List<string>();
foreach (DbEntityValidationResult validationResult in ex.EntityValidationErrors)
{
    string entityName = validationResult.Entry.Entity.GetType().Name;
    foreach (DbValidationError error in validationResult.ValidationErrors)
    {
        errorMessages.Add(entityName + "." + error.PropertyName + ": " + error.ErrorMessage);
    }
}

1
Kullanılması SelectMany and Aggregatehalinde github tarafından Cesur Kodlayıcılarda
Kiquenet

43

EntityValidationErrorsKoleksiyonu görüntülemek için, İzleme penceresine aşağıdaki İzleme ifadesini ekleyin.

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

Visual studio 2013 kullanıyorum


$ istisna parlak! hemen pencerede $ exception.EntityValidationErrors.SelectMany (x => x.ValidationErrors) yapabileceğim anlamına gelir.
chrispepper1989

13

catch {...}Blok içinde hata ayıklama modundayken "QuickWatch" penceresini ( ctrl+ alt+ q) açın ve buraya yapıştırın:

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

Bu, ValidationErrorsağaca inmenizi sağlar. Bu hataları anında anlamanın en kolay yolu.

Yalnızca ilk hatayı önemseyen ve bir catchbloğu olmayan Visual 2012+ kullanıcıları için şunları da yapabilirsiniz:

((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors.First().ValidationErrors.First().ErrorMessage

9

Hata ayıklama sırasında hatayı inceleyerek anlamlı bir hata iletisini hızlı bir şekilde bulmak için:

  • Şunlar için hızlı bir saat ekleyin:

    ((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
  • Aşağıdaki gibi EntityValidationErrors ayrıntılarına inin:

    (koleksiyon öğesi örn. [0])> ValidationErrors> (koleksiyon öğesi örn. [0])> ErrorMessage


5

Aslında, bu sadece doğrulama sorunudur, EF veritabanında herhangi bir değişiklik yapmadan önce varlık özelliklerini doğrular. Böylece EF, tabloyu tasarladığınız gibi, mülk değerinin aralık dışında olup olmadığını kontrol edecektir. Table_Column_UserName varchar (20) 'dir. Ancak, EF'de 20'den daha uzun bir değer girdiniz. Veya diğer durumlarda sütun Null değerine izin vermiyorsa. Bu nedenle, doğrulama işleminde, üzerinde değişiklik yapıp yapmayacağınıza bakılmaksızın, null olmayan sütuna bir değer ayarlamanız gerekir. Ben şahsen, Leniel Macaferi'nin cevabı gibi. Doğrulama sorunlarının ayrıntılarını gösterebilir


4

"Gerçek doğrulama hataları" hassas bilgiler içerebilir ve bu nedenle Microsoft bunları başka bir yere (özellikler) koymak seçti nedeni olabilir. Burada işaretli çözüm pratiktir, ancak dikkatle alınmalıdır.

Bir uzantı yöntemi oluşturmayı tercih ederim. Bunun için başka nedenler:

  • Orijinal yığın izini koru
  • Açık / kapalı prensibini takip edin (örneğin: Farklı günlük türleri için farklı mesajlar kullanabilirim)
  • Üretim ortamlarında DbEntityValidationException özel durumunun atılabileceği başka yerler de olabilir (başka bir deyişle, diğer dbcontext).

1

Azure İşlevleri için Microsoft.Extensions.Logging.ILogger dosyasının bu basit uzantısını kullanıyoruz

public static class LoggerExtensions
{
    public static void Error(this ILogger logger, string message, Exception exception)
    {
        if (exception is DbEntityValidationException dbException)
        {
            message += "\nValidation Errors: ";
            foreach (var error in dbException.EntityValidationErrors.SelectMany(entity => entity.ValidationErrors))
            {
                message += $"\n * Field name: {error.PropertyName}, Error message: {error.ErrorMessage}";
            }
        }

        logger.LogError(default(EventId), exception, message);
    }
}

ve örnek kullanım:

try
{
    do something with request and EF
}
catch (Exception e)
{
    log.Error($"Failed to create customer due to an exception: {e.Message}", e);
    return await StringResponseUtil.CreateResponse(HttpStatusCode.InternalServerError, e.Message);
}

0

Kodunuzda try block komutunu kullanın

try
{
    // Your code...
    // Could also be before try if you know the exception occurs in SaveChanges

    context.SaveChanges();
}
catch (DbEntityValidationException e)
{
    foreach (var eve in e.EntityValidationErrors)
    {
        Console.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)
        {
            Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
                ve.PropertyName, ve.ErrorMessage);
        }
    }
    throw;
}

Detayları burada da kontrol edebilirsiniz

  1. http://mattrandle.me/viewing-entityvalidationerrors-in-visual-studio/

  2. Bir veya daha fazla varlık için doğrulama başarısız oldu. Daha fazla ayrıntı için 'EntityValidationErrors' özelliğine bakın

  3. http://blogs.infosupport.com/improving-dbentityvalidationexception/


Üçüncü bağlantınız kabul edilen yanıtlayan blogunun bir kopyasıdır, ancak farklı bir sitedir. İkinci bağlantı, ilk bağlantınıza zaten başvuran bir yığın taşması sorusudur.
Eris

Yani, uygun referansı olan birine yardım etmeye çalışmak burada bir sorun mu var?
Atta H.

Evet, cevabınız sadece bağlantılar içermemelidir. Soruyu cevaplayan bir özet yapın ve daha sonra okumak için bağlantıyı sonuna gönderin.
ChrisO
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.