c# - 一個或多個實體的驗證失敗。 有關更多詳細信息,請參閱“EntityValidationErrors”屬性




entity-framework entity-framework-4 (15)

使用代碼優先方法播種我的數據庫時遇到此錯誤。

一個或多個實體的驗證失敗。 有關更多詳細信息,請參閱“EntityValidationErrors”屬性。

說實話,我不知道如何檢查驗證錯誤的內容。 Visual Studio顯示它是一個包含8個對象的數組,因此有8個驗證錯誤。

這是與我以前的模型,但我做了一些改變,我在下面解釋:

  • 我有一個名為Status的枚舉,我將其更改為一個名為Status的類
  • 我將類ApplicantsPositionHistory更改為有兩個外鍵給同一個表

請原諒我的長碼,但我必須將其全部粘貼。 以下代碼的最後一行引發異常。

namespace Data.Model
{  
    public class Position
    {
        [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]   
        public int PositionID { get; set; }

        [Required(ErrorMessage = "Position name is required.")]
        [StringLength(20, MinimumLength = 3, ErrorMessage = "Name should not be longer than 20 characters.")]
        [Display(Name = "Position name")]              
        public string name { get; set; }

        [Required(ErrorMessage = "Number of years is required")] 
        [Display(Name = "Number of years")]        
        public int yearsExperienceRequired { get; set; }

        public virtual ICollection<ApplicantPosition> applicantPosition { get; set; }
    }

    public class Applicant
    {
        [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]      
        public int ApplicantID { get; set; }

        [Required(ErrorMessage = "Name is required")] 
        [StringLength(20, MinimumLength = 3, ErrorMessage="Name should not be longer than 20 characters.")]
        [Display(Name = "First and LastName")]
        public string name { get; set; }

        [Required(ErrorMessage = "Telephone number is required")] 
        [StringLength(10, MinimumLength = 3, ErrorMessage = "Telephone should not be longer than 20 characters.")]
        [Display(Name = "Telephone Number")]
        public string telephone { get; set; }

        [Required(ErrorMessage = "Skype username is required")] 
        [StringLength(10, MinimumLength = 3, ErrorMessage = "Skype user should not be longer than 20 characters.")]
        [Display(Name = "Skype Username")]
        public string skypeuser { get; set; }

        public byte[] photo { get; set; }

        public virtual ICollection<ApplicantPosition> applicantPosition { get; set; }
    }

    public class ApplicantPosition
    {
        [Key]
        [Column("ApplicantID", Order = 0)]
        public int ApplicantID { get; set; }

        [Key]
        [Column("PositionID", Order = 1)]
        public int PositionID { get; set; }

        public virtual Position Position { get; set; }

        public virtual Applicant Applicant { get; set; }

        [Required(ErrorMessage = "Applied date is required")] 
        [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
        [Display(Name = "Date applied")]     
        public DateTime appliedDate { get; set; }

        [Column("StatusID", Order = 0)]
        public int StatusID { get; set; }

        public Status CurrentStatus { get; set; }

        //[NotMapped]
        //public int numberOfApplicantsApplied
        //{
        //    get
        //    {
        //        int query =
        //             (from ap in Position
        //              where ap.Status == (int)Status.Applied
        //              select ap
        //                  ).Count();
        //        return query;
        //    }
        //}
    }

    public class Address
    {
        [StringLength(20, MinimumLength = 3, ErrorMessage = "Country should not be longer than 20 characters.")]
        public string Country { get; set; }

        [StringLength(20, MinimumLength = 3, ErrorMessage = "City  should not be longer than 20 characters.")]
        public string City { get; set; }

        [StringLength(50, MinimumLength = 3, ErrorMessage = "Address  should not be longer than 50 characters.")]
        [Display(Name = "Address Line 1")]     
        public string AddressLine1 { get; set; }

        [Display(Name = "Address Line 2")]
        public string AddressLine2 { get; set; }   
    }

    public class ApplicationPositionHistory
    {
        [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
        public int ApplicationPositionHistoryID { get; set; }

        public ApplicantPosition applicantPosition { get; set; }

        [Column("oldStatusID")]
        public int oldStatusID { get; set; }

        [Column("newStatusID")]
        public int newStatusID { get; set; }

        public Status oldStatus { get; set; }

        public Status newStatus { get; set; }

        [StringLength(500, MinimumLength = 3, ErrorMessage = "Comments  should not be longer than 500 characters.")]
        [Display(Name = "Comments")]
        public string comments { get; set; }

        [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
        [Display(Name = "Date")]     
        public DateTime dateModified { get; set; }
    }

    public class Status
    {
        [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
        public int StatusID { get; set; }

        [StringLength(20, MinimumLength = 3, ErrorMessage = "Status  should not be longer than 20 characters.")]
        [Display(Name = "Status")]
        public string status { get; set; }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using System.IO;

namespace Data.Model
{
    public class HRContextInitializer : DropCreateDatabaseAlways<HRContext>
    {
        protected override void Seed(HRContext context)
        {
            #region Status
            Status applied = new Status() { status = "Applied" };
            Status reviewedByHR = new Status() { status = "Reviewed By HR" };
            Status approvedByHR = new Status() { status = "Approved by HR" };
            Status rejectedByHR = new Status() { status = "Rejected by HR" };
            Status assignedToTechnicalDepartment = new Status() { status = "Assigned to Technical Department" };
            Status approvedByTechnicalDepartment = new Status() { status = "Approved by Technical Department" };
            Status rejectedByTechnicalDepartment = new Status() { status = "Rejected by Technical Department" };

            Status assignedToGeneralManager = new Status() { status = "Assigned to General Manager" };
            Status approvedByGeneralManager = new Status() { status = "Approved by General Manager" };
            Status rejectedByGeneralManager = new Status() { status = "Rejected by General Manager" };

            context.Status.Add(applied);
            context.Status.Add(reviewedByHR);
            context.Status.Add(approvedByHR);
            context.Status.Add(rejectedByHR);
            context.Status.Add(assignedToTechnicalDepartment);
            context.Status.Add(approvedByTechnicalDepartment);
            context.Status.Add(rejectedByTechnicalDepartment);
            context.Status.Add(assignedToGeneralManager);
            context.Status.Add(approvedByGeneralManager);
            context.Status.Add(rejectedByGeneralManager); 
            #endregion    

            #region Position
            Position netdeveloper = new Position() { name = ".net developer", yearsExperienceRequired = 5 };
            Position javadeveloper = new Position() { name = "java developer", yearsExperienceRequired = 5 };
            context.Positions.Add(netdeveloper);
            context.Positions.Add(javadeveloper); 
            #endregion

            #region Applicants
            Applicant luis = new Applicant()
            {
                name = "Luis",
                skypeuser = "le.valencia",
                telephone = "0491732825",
                photo = File.ReadAllBytes(@"C:\Users\LUIS.SIMBIOS\Documents\Visual Studio 2010\Projects\SlnHR\HRRazorForms\Content\pictures\1.jpg")
            };

            Applicant john = new Applicant()
            {
                name = "John",
                skypeuser = "jo.valencia",
                telephone = "3435343543",
                photo = File.ReadAllBytes(@"C:\Users\LUIS.SIMBIOS\Documents\Visual Studio 2010\Projects\SlnHR\HRRazorForms\Content\pictures\2.jpg")
            };

            context.Applicants.Add(luis);
            context.Applicants.Add(john); 
            #endregion

            #region ApplicantsPositions
            ApplicantPosition appicantposition = new ApplicantPosition()
            {
                Applicant = luis,
                Position = netdeveloper,
                appliedDate = DateTime.Today,
                StatusID = 1
            };

            ApplicantPosition appicantposition2 = new ApplicantPosition()
            {
                Applicant = john,
                Position = javadeveloper,
                appliedDate = DateTime.Today,
                StatusID = 1
            };        

            context.ApplicantsPositions.Add(appicantposition);            
            context.ApplicantsPositions.Add(appicantposition2); 
            #endregion

            context.SaveChanges(); --->> Error here
        }
    }
}

說實話,我不知道如何檢查驗證錯誤的內容。 Visual Studio顯示它是一個包含8個對象的數組,因此有8個驗證錯誤。

實際上,如果您在調試期間在Visual Studio中鑽取該數組,則應該會看到這些錯誤。 但是,您也可以捕獲異常,然後將錯誤寫入某個日誌存儲或控制台:

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

EntityValidationErrors是一個集合,它表示無法成功ValidationErrors實體,每個實體的內部集合ValidationErrors是屬性級別的錯誤列表。

這些驗證消息通常有助於找到問題的根源。

編輯

一些小的改進:

有問題的屬性的可以包含在內部循環中,如下所示:

        foreach (var ve in eve.ValidationErrors)
        {
            Console.WriteLine("- Property: \"{0}\", Value: \"{1}\", Error: \"{2}\"",
                ve.PropertyName,
                eve.Entry.CurrentValues.GetValue<object>(ve.PropertyName),
                ve.ErrorMessage);
        }

調試Debug.Write可能比Console.WriteLine更可取,因為它可以在所有類型的應用程序中使用,而不僅僅是控制台應用程序(感謝@Bart在下面的註釋中提供了他的註釋)。

對於正在生產並使用Elmah進行異常日誌記錄的Web應用程序來說,它對於我創建自定義異常並覆蓋SaveChanges以引發此新異常非常有用。

自定義異常類型如下所示:

public class FormattedDbEntityValidationException : Exception
{
    public FormattedDbEntityValidationException(DbEntityValidationException innerException) :
        base(null, innerException)
    {
    }

    public override string Message
    {
        get
        {
            var innerException = InnerException as DbEntityValidationException;
            if (innerException != null)
            {
                StringBuilder sb = new StringBuilder();

                sb.AppendLine();
                sb.AppendLine();
                foreach (var eve in innerException.EntityValidationErrors)
                {
                    sb.AppendLine(string.Format("- Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
                        eve.Entry.Entity.GetType().FullName, eve.Entry.State));
                    foreach (var ve in eve.ValidationErrors)
                    {
                        sb.AppendLine(string.Format("-- Property: \"{0}\", Value: \"{1}\", Error: \"{2}\"",
                            ve.PropertyName,
                            eve.Entry.CurrentValues.GetValue<object>(ve.PropertyName),
                            ve.ErrorMessage));
                    }
                }
                sb.AppendLine();

                return sb.ToString();
            }

            return base.Message;
        }
    }
}

SaveChanges可以通過以下方式覆蓋:

public class MyContext : DbContext
{
    // ...

    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbEntityValidationException e)
        {
            var newException = new FormattedDbEntityValidationException(e);
            throw newException;
        }
    }
}

幾句話:

  • Elmah在Web界面或發送的電子郵件中顯示的黃色錯誤屏幕(如果已配置)現在直接在消息的頂部顯示驗證詳細信息。

  • 覆蓋自定義異常中的Message屬性而不是覆蓋ToString()的好處是,標準ASP.NET“死亡黃色屏幕(YSOD)”也會顯示此消息。 與Elmah相比,YSOD顯然不使用ToString() ,但都顯示Message屬性。

  • 將原始DbEntityValidationException包裝為內部異常,以確保原始堆棧跟踪仍然可用並顯示在Elmah和YSOD中。

  • 通過在行上設置斷點throw newException; 您可以簡單地將newException.Message屬性作為文本進行檢查,而不是鑽取到驗證集合中,這有點尷尬,而且對於每個人來說都不容易(見下面的註釋)。


@Slauma的答案非常好,但是我發現當ComplexType屬性無效時它不起作用。

例如,假設您有一個複雜類型PhoneNumber的屬性Phone 。 如果AreaCode屬性無效,則ve.PropertyNames的屬性名稱為“Phone.AreaCode”。 這會導致調用eve.Entry.CurrentValues<object>(ve.PropertyName)失敗。

為了解決這個問題,你可以在每個分割屬性名稱. ,然後遍歷所得到的屬性名稱數組。 最後,當你到達鏈條的底部時,你可以簡單地返回屬性的值。

下面是@ Slauma的FormattedDbEntityValidationException類,它支持ComplexTypes。

請享用!

[Serializable]
public class FormattedDbEntityValidationException : Exception
{
    public FormattedDbEntityValidationException(DbEntityValidationException innerException) :
        base(null, innerException)
    {
    }

    public override string Message
    {
        get
        {
            var innerException = InnerException as DbEntityValidationException;
            if (innerException == null) return base.Message;

            var sb = new StringBuilder();

            sb.AppendLine();
            sb.AppendLine();
            foreach (var eve in innerException.EntityValidationErrors)
            {
                sb.AppendLine(string.Format("- Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
                    eve.Entry.Entity.GetType().FullName, eve.Entry.State));
                foreach (var ve in eve.ValidationErrors)
                {
                    object value;
                    if (ve.PropertyName.Contains("."))
                    {
                        var propertyChain = ve.PropertyName.Split('.');
                        var complexProperty = eve.Entry.CurrentValues.GetValue<DbPropertyValues>(propertyChain.First());
                        value = GetComplexPropertyValue(complexProperty, propertyChain.Skip(1).ToArray());
                    }
                    else
                    {
                        value = eve.Entry.CurrentValues.GetValue<object>(ve.PropertyName);
                    }
                    sb.AppendLine(string.Format("-- Property: \"{0}\", Value: \"{1}\", Error: \"{2}\"",
                        ve.PropertyName,
                        value,
                        ve.ErrorMessage));
                }
            }
            sb.AppendLine();

            return sb.ToString();
        }
    }

    private static object GetComplexPropertyValue(DbPropertyValues propertyValues, string[] propertyChain)
    {
        var propertyName = propertyChain.First();
        return propertyChain.Count() == 1 
            ? propertyValues[propertyName] 
            : GetComplexPropertyValue((DbPropertyValues)propertyValues[propertyName], propertyChain.Skip(1).ToArray());
    }
}

以下是如何在Visual Studio中檢查EntityValidationErrors的內容(無需編寫任何額外的代碼),即在IDE中進行調試期間。

問題?

你是對的,Visual Studio調試器的View Details Popup不會在EntityValidationErrors集合中顯示實際的錯誤。

解決方案!

只需在快速監視窗口中添加以下表達式,然後單擊重新評估

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

在我的情況下,看看我能夠擴展到EntityValidationErrors集合中的ValidationErrors List

參考文獻: http://mattrandle.me/viewing-entityvalidationerrors-in-visual-studio/ , @ yoel的回答


使用@Slauma的答案,我製作了一個代碼片斷(帶有片段的環繞聲)以便更好地使用。

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <SnippetTypes>
        <SnippetType>SurroundsWith</SnippetType>
      </SnippetTypes>
      <Title>ValidationErrorsTryCatch</Title>
      <Author>Phoenix</Author>
      <Description>
      </Description>
      <HelpUrl>
      </HelpUrl>
      <Shortcut>
      </Shortcut>
    </Header>
    <Snippet>
      <Code Language="csharp"><![CDATA[try
{
    $selected$ $end$
}
catch (System.Data.Entity.Validation.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;
}]]></Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

在try catch中捕獲異常,然後快速觀察或按Ctrl + d和ctrl + q,您可以深入到EntityValidationErrors。


在調試中,您可以在QuickWatch表達式求值器輸入字段中輸入:

context.GetValidationErrors()

對於任何在VB.NET

Try
Catch ex As DbEntityValidationException
    For Each a In ex.EntityValidationErrors
        For Each b In a.ValidationErrors
            Dim st1 As String = b.PropertyName
            Dim st2 As String = b.ErrorMessage
        Next
    Next
End Try

您可以在調試期間從Visual Studio中完成,無需編寫任何代碼,甚至不需要任何代碼。

只需添加一個名稱為:

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

監視表達式$exception會顯示當前上下文中拋出的任何異常,即使它沒有被捕獲並分配給變量。

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


我之前遇到過這個錯誤

當我嘗試在實體框架中更新模型中的特定字段時

Letter letter = new Letter {ID = letterId, ExportNumber = letterExportNumber,EntityState = EntityState.Modified};
LetterService.ChangeExportNumberfor(letter);
//----------


public int ChangeExportNumber(Letter letter)
    {
        int result = 0;
        using (var db = ((LettersGeneratorEntities) GetContext()))
        {
            db.Letters.Attach(letter);
            db.Entry(letter).Property(x => x.ExportNumber).IsModified = true;
            result += db.SaveChanges();
        }
        return result;
    }

並根據以上的答案

我發現驗證消息The SignerName field is required.

它指向我的模型中的字段

當我檢查我發現的數據庫模式時

所以關閉coure ValidationException有權提出

根據這個領域,我希望它是可空的(我不知道我是如何搞砸的)

所以我改變了這個字段以允許Null,通過這個我的代碼不會再給我這個錯誤

所以如果您使您的數據庫的數據完整性失效,則可能會發生此錯誤


我發現了什麼...當我得到'EntityValidationErrors'錯誤是....我有一個字段在我的數據庫'db1'在表'tbladdress'作為'地址1'大小為100(即地址varchar(100)空),我傳遞值超過100個字符..這導致錯誤,同時保存數據到數據庫....

所以你必須檢查你傳遞給現場的數據。


正如其他文章中提到的,只需在DbEntityValidationException類中捕獲異常即可。 在錯誤的情況下,這會讓你滿足你的需求。

 try
 {
  ....
 }
 catch(DbEntityValidationException ex)
 {
  ....
 }

為了快速查看第一個錯誤而不添加手錶,您可以將其粘貼到即時窗口中:

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

請檢查您傳遞的字段值是否有效,並根據數據庫字段。 例如,在特定字段中傳遞的字符數少於數據庫表字段中定義的字符數。


請注意, Entity.GetType().BaseType.Name給出您指定的類型名稱,而不是名稱中包含所有十六進制數字的名稱。


這對我有用。

var modelState = ModelState.Values;
if (!ModelState.IsValid)
{
    return RedirectToAction("Index", "Home", model);
}

在if語句上放置一個斷點。 然後你可以在調試窗口中檢查模型狀態。 在每個值上你都可以看到是否有錯誤,甚至是錯誤信息。 而已。 當你不再需要它時,只需刪除或註釋該行。

我希望這將有所幫助。

如果詢問,我可以在調試窗口中提供詳細的屏幕截圖。





entity-framework-4.1