Entity Framework 4.1/4.3 之七 (DBContext 之4 數據驗證)


 

Entity Framework 4.1/4.3 之七 (DBContext 之4  數據驗證)

 

  中國男籃輸了,不過不影響我對中國男籃的喜歡。在Entity Framework 4.1/4.3 之六 (DBContext 3)中講了EF DBContext API的常用功能,今天來我們接着來講一下DBContext API的驗證。

 

三、DBContext 的驗證 (Validating with the Validation API) 

  1、定義和觸發驗證 (Defining and Triggering Validation)

    會引起DBContext去執行驗證的一些方法

    (1)、當跟蹤狀態為增加、修改時。調用執行SaveChanges()方法會執行驗證。

    (2)、DbEntityEntry.GetValidationResult() 方法將會對單獨的Object執行驗證。

    (3)、DbEntityEntry

    (4)、DbContext.GetValidationErrors 可以在DBContext進行 增加、修改實體的時候不捕獲驗證異常。

    我們來描述一下驗證的流程:

    DBContext.SaveChanges()這時候調用驗證 —> DBContext.GetValidationErrors —> DBContext.ValidateEntity(CustomLogic) —> DBEntityEntry.GetValidationResult(ValidationAttribute,IValidatableObject.Validate)

 

    2、驗證單個實體並獲取驗證結果  (Validating a Single Object on Demand with GetValidationResult)

    代碼如下,先是在實體中對屬性進行約束,我們來看一下代碼:

      [MaxLength(500)]       public string Description { get; set; }           [MaxLength(10)]           public string LastName { get; set; }       private static void ValidateNewPerson()       {         var person = new Person         {           FirstName = "Julie",           LastName = "Lerman",           Photo = new PersonPhoto { Photo = new Byte[] { 0 } }         };         using (var context = new BreakAwayContext())         {           if (context.Entry(person).GetValidationResult().IsValid)           {             Console.WriteLine("Person is Valid");           }           else           {             Console.WriteLine("Person is Invalid");           }         }       }  

輸出的結果是:Person is Valid

  代碼真是個好東西,他能讓我們一下子就明白,原來是這么回事。[MaxLength(500)]好定了內容的長度,這個特性需要引用using System.ComponentModel.DataAnnotations; 如果你試着把LastName的屬性值改為超為10長度的字符串,得到的結果會是InValidate也就是False,不信你試試。對了,GetValidationResult()是用來獲取驗證的結果。
    

  3、通過DataAnnotations來指定屬性的驗證規則 (Specifying Property Rules with ValidationAttribute DataAnnotations)

    下面列出的是DataAnnotations驗證的方式:

   可以限定字條串的長度、數值的大小、還可以自定義表達式、

      DataTypeAttribute       [DataType(DataType enum)] 
      RangeAttribute       [Range (low value, high value, error message
string)]
      RegularExpressionAttribute       [RegularExpression(@”expression”)]
      RequiredAttribute       [Required]
      StringLengthAttribute       [StringLength(max length value,       MinimumLength
=min length value)]
      CustomValidationAttribute       This attribute can be applied to a type
as well as to a property.

        

   Entity Framework 提供了MaxLengthAttribute 和 MinLengthAttribute。為了符合代碼先行的理念,DBContext 的驗證API中提代了很多方法,利用這些方法,我們就可以直接在編寫代碼的時候來對屬性設置約束。我們來看個例子: 

    modelBuilder.Entity<TestEntity>().Property(p => p.name).HasMaxLength(10);
 

   例子設定了TestEntity實體的name屬性的最大長度為10。通過這種方式,我們就可以在DBContext.OnModelCreating 方法中加入上面的代碼來代替[MaxLength]方式的特性限定。你可以試試這種新的方式,試之前記得把實體中的[MaxLength]去掉,同樣調用GetValidationResult()來獲取驗證的結果。

 

   對臨時屬性的驗證 (Validating Unmapped or “Transient” Properties)

   有這樣一種情況,在實體中有些屬性並沒有和數據庫中字段有映射關系。只是我們為了處理業務而臨時加的屬性,如果我們給這處臨時屬性加上特性約束后,Entity Framework同樣會對其進行驗證。

 

   4、檢查驗證結果的詳細信息 (Inspecting Validation Result Details)

   我們注意到GetValidationResult 在驗證失敗后並不是簡單的返回或者說拋出一個exception。而是返回一個System.Data.Entity.Validation.DbEntityValidationResult 類型的值。DbEntityValidationResult 也公來了一個ValidationErrors屬性,這個屬性包含記錄了詳細錯誤信息的DbValidationError類型集合。下圖展示了獲取驗證結果,包含IsValid值(是否驗證通過的值),和ValidationErrors屬性。

   錯誤提示:當我們把LastName的屬性值賦值超過了約束限定大小時,返回ValidationErrors驗證結果,它包含一個單獨的DbValidationError。DbValidationError有兩個屬性,一個是發生異常對應的屬性名稱,一個是錯誤信息。這里有一個疑問?錯誤消息是哪兒來的呢???我們可以自己定義錯誤消息嗎???  我們來看看代碼:

    [MaxLength(10,ErrorMessage= "Dude! Last name is too long! 10 is max.")]     public string LastName { get; set; }

   通過代碼我們看到了,錯誤信息來源於我們屬性約束值提供的錯誤消息。當然,如果你不寫也沒關系,EF會自己提供錯誤消息。以就是上圖展示的ErrorMessage。下圖將展示出我們自定義的錯誤消息:

 

   5、深入探索屬性驗證 (Exploring More Validation Attributes)

   (1)、表達式驗證

    [RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$")]     public string Country { get; set; }
    public static void ValidateDestination()     {       ConsoleValidationResults(       new Destination       {         Name = "Bei Jing City",         Country = "China",         Description = "Big city"       });     }

   如果你試着把Country的值改為 C.H.I.N.A 就會出現驗證異常。因為C.H.I.N.A與正則不匹配。
   (2)、使用自定義的屬性驗證 

    using System.ComponentModel.DataAnnotations;     namespace Model     {       public static class BusinessValidations       {         public static ValidationResult DescriptionRules(string value)         {           var errors = new System.Text.StringBuilder();           if (value != null)           {             var description = value as string;             if (description.Contains("!"))             {               errors.AppendLine("Description should not contain '!'.");             }             if (description.Contains(":)") || description.Contains(":("))             {               errors.AppendLine("Description should not contain emoticons.");             }         }         if (errors.Length > 0)           return new ValidationResult(errors.ToString());         else
          return ValidationResult.Success;       }     }    }

   提示:你可以看到難證結果此處是System.ComponentModel.DataAnnotations.ValidationResult 類型。

   好了,驗證的功能寫好了,下面我們來使用一下自己的驗證。

    [MaxLength(500)]     [CustomValidation(typeof(BusinessValidations), "DescriptionRules")]     public string Description { get; set; }

   如果你願意的話,自己寫個測試,使用GetValidationResult 來測試吧。

   (3)、在請求中驗證屬性 (Validating Individual Properties on Demand)  

   除了提供getvalidationresults方法,dbentityentry 也可以讓你進行屬性驗證:

             context.Entry(trip).Property(t => t.Description);

   它會返回一個 DbPropertyEntry 類來描述屬性。DbPropertyEntry 類有明確的方法(GetValidationErrors)來驗證特定項目。這個方法將返回ICollection<DbValidationError>,它與 DbValidationError 類型相同。我們通過下面這個例子來看看GetValidationErrors的應用。

    private static void ValidatePropertyOnDemand()     {       var trip=new Trip       {         EndDate = DateTime.Now,         StartDate = DateTime.Now,         CostUSD = 500.00M,         Description = "Hope you won't be freezing :)"       };       using (var context = new BreakAwayContext())       {         var errors = context.Entry(trip).Property(t => t.Description).GetValidationErrors();         Console.WriteLine("# Errors from Description validation: {0}",         errors.Count());       }     }

 

   (4)、使用IValidatableObject接口來進驗證

   除了ValidationAttribute,.Net 4 新增了IValidatableObject 接口供開發者進行業務邏輯驗證。IValidatableObject 提供了Validate 方法供開發者自己來擴展自定義驗證。我們來看例子:

    public class Trip : IValidatableObject     {       [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]       public Guid Identifier { get; set; }       public DateTime StartDate { get; set; }       public DateTime EndDate { get; set; }       [CustomValidation(typeof(BusinessValidations), "DescriptionRules")]       public string Description { get; set; }       public decimal CostUSD { get; set; }       [Timestamp]       public byte[] RowVersion { get; set; }       public int DestinationId { get; set; }       [Required]       public Destination Destination { get; set; }       public List<Activity> Activities { get; set; } 
      public IEnumerable<ValidationResult> Validate(         ValidationContext validationContext)         {           if (StartDate.Date >= EndDate.Date)           {             yield return new ValidationResult(               "Start Date must be earlier than End Date",               new[] { "StartDate", "EndDate" });           }         }     }

   Trip 繼續 IValidatableObject 並實現 接口方法Validate,在Validate方法中加入了自己的驗證邏輯。我們來測試一下這個驗證,下面是測試代碼:

    private static void ValidateTrip()     {       ConsoleValidationResults(new Trip       {         EndDate = DateTime.Now,         StartDate = DateTime.Now.AddDays(2),         CostUSD = 500.00M,         Destination = new Destination { Name = "Somewhere Fun" }       });     }

   當我們調用ValidateTrip, 程序會顯示 “Start Date must be earlier than End Date.”   這種Validate 驗證方式我們多在MVC和WPF編程中用到,幫綁定屬性的時候可以把錯誤提示也一便綁定了。

 

   (5)、使用CustomValidationAttributes 進行驗證 以下是代碼:代碼可以很好的說明

    public static ValidationResult TripDateValidator(           Trip trip,           ValidationContext validationContext)     {       if (trip.StartDate.Date >= trip.EndDate.Date)       {         return new ValidationResult(           "Start Date must be earlier than End Date",           new[] { "StartDate", "EndDate" });         }         return ValidationResult.Success;       } 
    public static ValidationResult TripCostInDescriptionValidator(           Trip trip,           ValidationContext validationContext)     {       if (trip.CostUSD > 0)       {         if (trip.Description.Contains(Convert.ToInt32(trip.CostUSD).ToString()))         {           return new ValidationResult(             "Description cannot contain trip cost",             new[] { "Description" });         }       }       return ValidationResult.Success;     }

這是我們自己寫的自己定義的驗證方法,下面我們來使用自定義方法:
    [CustomValidation(typeof(Trip), "TripDateValidator")]     [CustomValidation(typeof(Trip), "TripCostInDescriptionValidator")]     public class Trip: IValidatableObject

   下面是測試程序:

    private static void ValidateTrip()     {        ConsoleValidationResults(new Trip         {         EndDate = DateTime.Now,         StartDate = DateTime.Now.AddDays(2),         CostUSD = 500.00M,         Description = "You should enjoy this 500 dollar trip",         Destination = new Destination { Name = "Somewhere Fun" }         });       }


      關於DBContext驗證就先講到這里,也不知道有沒有講清楚,有效的驗證可以為我們提供安全,當然內容中提到的也並不是也好用。大家分場合使用。發揮各自優勢。我是百靈,我們下回見。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM