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属性。

Entity Framework 4.1/4.3 之七 (DBContext 之4 数据验证)_第1张图片

   错误提示:当我们把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验证就先讲到这里,也不知道有没有讲清楚,有效的验证可以为我们提供安全,当然内容中提到的也并不是也好用。大家分场合使用。发挥各自优势。我是百灵,我们下回见。

你可能感兴趣的:(framework)