CHAPTER 6

Validating with the Validation API

Defining and Triggering Validation: An Overview

你可以使用以下方式设置你的验证规则:

  • 使用Data Annotations 或者 Fluent API。
  • 为属性或者类型自定义验证属性
  • 在所有实现了IValidatableObject 的类的 Validate方法中实现验证规则。
  • 在model中显式定义的关系约束。
  • 另外,你可以将验证规则注入到验证管道中

有以下方式引发DbContext 执行验证:

  • 默认情况下,在调用SaveChanges时,对所有Added或者Modified状态的实体执行验证
  • DbEntityEntry.GetValidationResult 方法将对一个单独的对象执行验证
  • DbEntityEntry 拥有一个途径来验证单独的属性
  • DbContext.GetValidationErrors 方法将遍历所有被DbContext跟踪的状态为Added或者Modified的实体,并对其执行验证

验证的执行顺序

Validating a Single Object on Demand with GetValidationResult

GetValidationResult 方法允许你显式的验证一个单独的实体,它返回一个 ValidationResult 类型的结果,

该结果包含三个重要的成员,目前,我们仅关注IsValid 属性。该属性是一个bool类型的,

当为true时,表示通过了验证,反之没有通过验证。

下面的例子通过该方法对LastName属性进行验证,LastName属性设置了以下规则:

[MaxLength(10)]
public string LastName { get; set; }

Example 6-1. Method to test validation of LastName.

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

GetValidationResult 方法调用必要的逻辑验证定义在实体属性上的 ValidationAttributes

接着会查找实体是否有CustomValidationAttribute 或者实现了IValidatableObject接口

如果有则调用其Validate方法进行验证。

Specifying Property Rules with ValidationAttribute Data Annotations

下面是可用的验证属性

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.

Validating Facets Configured with the Fluent API

Fluent API 拥有和Data Annotations同样的功能,并比它更强大。

Validating Unmapped or “Transient” Properties

按照约定,没有同时包含Getter 和 Setter的属性,将不会被映射到数据库,另外你也可用

NotMapped 或者 Ignore fluent API 来设置属性不被映射到数据库。按照约定

没有被映射的属性将不会被验证。

但是,当你对其应用了ValidationAttribute时。Entity Framework 也会验证它。

Validating Complex Types

Entity Framework 允许使用复杂类型,同时你也可用对其使用数据注解。GetValidationResult方法

将验证这些属性。

Using Data Annotations with an EDMX Model

可用使用 associated metadata class 添加数据注解。

假如你利用DbContext 生成模板从EDMX生成类Person。该类将被声明为分部类

 public partial class Person

如果你想为该类的FirstName属性添加验证属性,你可用创建一个新类,在新类中模拟

属性的声明,并添加验证属性:

class Person_Metadata
{
[MinLength(10)]
public string FirstName { get; set; }
}

接下来的工作就是让Person类知道要从 Person_Metadata 获取应用的验证属性

[MetadataType(typeof(Person_Metadata))]
public partial class Person

Inspecting Validation Result Details

GetValidationResult 方法在验证失败时,并不是简单的抛出一个异常。而是返回一个

System.Data.Entity.Validation.DbEntityValidationResult 类型的结果,无论验证规则是否

通过。并且会为IsValid属性设置一个恰当的值,为没有通过的规则设置详细信息。

DbEntityValidationResult 还暴露一个ValidationErrors 属性。这个属性是一个包含

DbValidationError 类型的集合对象。另外它还包含一个Entry属性。这看起来有点多余。

因为我们好像可以直接访问entry。但是在一些高级场合。那些无法直接访问Entry的场合,它就显得

比较有用了。

Using CustomValidationAttributes

你可以建立自己的验证逻辑,并利用 Custom ValidationAttribute 应用的需要验证的属性

上。下面的例子展示了一个静态类BusinessValidations。它包含一个单独的验证方法DescriptionRules

被用来验证model的description 属性。该规则验证属性不能包含感叹号和表情符号。

Example 6-3. Static custom validations to be used by different classes

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

Example 6-4. Applying the new DescriptionRules validation to a property

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

CustomValidation用法

CustomValidation(包含验证方法的类型,验证方法字符串名称)

Validating Individual Properties on Demand

除了使用GetValidationResults 方法,DbEntityEntry 允许你深入单独的属性,就像前面章节演示的一样

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

这返回一个代表Description 的DbPropertyEntry 类型的对象。该对象包含一个GetValidationErrors 方法,

返回一个 ICollection<DbValidationError> 类型的集合对象。

Example 6-6. Validating a property

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

Specifying Type-Level Validation Rules

有两种方式进行类型级别的验证

  1. 实现IValidatableObject 接口
  2. 为类型添加CustomValidationAttributes
Using IValidatableObject for Type Validation

为类型实现IValidatableObject 接口的 Validate方法

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

你可以在Validate方法中实现多个验证,并用yield关键字返回所有的验证结果

Using CustomValidationAttributes for Type Validation

Example 6-12. Two validation methods to be used as Trip type attributes

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

Example 6-13. Applying a type-level CustomValidationAttribute

[CustomValidation(typeof(Trip), "TripDateValidator")]
[CustomValidation(typeof(Trip), "TripCostInDescriptionValidator")]
public class Trip: IValidatableObject

Understanding How EF Combines Validations

当属性验证未通过时,类型验证不会被触发

验证执行的顺序

  1. Property-level validation on the entity and the base classes. If a property is complex its validation would also include the following:
  2. Property-level validation on the complex type and its base types
  3. Type-level validation on the complex type and its base types, including IValidata bleObject validation on the complex type
  4. Type-level validation on the entity and the base entity types, including IValidata bleObject validation

Validating Multiple Objects

除了为单独的对象调用GetValidationResult方法,你也可以调用DbContext.GetValidationErrors方法对必要的被跟踪对象

进行验证,之所以强调“必要的”,因为默认情况下GetValidationErrors 方法仅验证状态为Added 和 modified 的对象。

GetValidationErrors 不会验证关系约束!SaveChanges方法将验证关系约束。

Validating When Saving Changes

SaveChanges方法将自动执行验证

Reviewing ObjectContext. SaveChanges Workflow

  1. 默认情况下,SaveChanges方法调用 DetectChages方法来更新追踪信息。
  2. SaveChanges 迭代每个被追踪的需要被修改的对象。
  3. 检查每一个实体,确定它们的关系约束是否正常,如果不正常,抛出EntityUpdateException 异常

    并停止进一步处理。
  4. 如果所有的关系通过了验证,EF针对数据库构建并执行必要的SQL命令
  5. 如果SQL命令执行错误,抛出EntityUpdateException并停止进一步工作

因为SaveChanges 使用了DbTransaction (事务处理),如果中间任何一个环节出现了问题,所有成功的操作也将被回滚。

Understanding DbContext.SaveChanges Workflow

当你使用DbContext的SaveChanges方法时,在执行 ObjectContext.Savechanges 的工作流程之前,DbContext.SaveChanges

先调用GetValidationErrors方法,执行验证,如果没有任何错误,则调用ObjectContext.SaveChanges。因为 GetValidationErrors 方法

已经调用了DetectChanges,ObjectContext.SaveChanges将跳过这一步。

Figure 6-5 shows the execution path when your code calls DbContext.SaveChanges.

Disabling Validate Before Save

public class BreakAwayContext : DbContext
{
public BreakAwayContext()
{
Configuration.ValidateOnSaveEnabled = false;
}
... rest of class logic
}

第六章 Validating with the Validation API的更多相关文章

  1. <自动化测试方案_6>第六章、API自动化测试

    第六章.API自动化测试 (一)工具实现 目前大众接口测试的工具有:Postman.SoupUI.jmeter他们的特点介绍有人做个宏观的研究,这里进行引用:https://blog.csdn.net ...

  2. KnockoutJS 3.X API 第六章 组件(5) 高级应用组件加载器

    无论何时使用组件绑定或自定义元素注入组件,Knockout都将使用一个或多个组件装载器获取该组件的模板和视图模型. 组件加载器的任务是异步提供任何给定组件名称的模板/视图模型对. 本节目录 默认组件加 ...

  3. 读《编写可维护的JavaScript》第六章总结

    第六章 避免使用全局变量 JavaScript执行环境在很多方面都有其独特之处,全局变量就是其中之一.“全局变量”是一个神秘的对象,它表示了脚本的最外层上下文. 在浏览器中,windows对象往往重载 ...

  4. [Effective Java]第六章 枚举和注解

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  5. Laxcus大数据管理系统2.0(8)- 第六章 网络通信

    第六章 网络通信 Laxcus大数据管理系统网络建立在TCP/IP网络之上,从2.0版本开始,同时支持IPv4和IPv6两种网络地址.网络通信是Laxcus体系里最基础和重要的一环,为了能够利用有限的 ...

  6. 第六章SignalR的服务器广播

    第六章SignalR的服务器广播 1.概述: VS可以通过 Microsoft.AspNet.SignalR.Sample NuGet包来安装一个简单的模拟股票行情应用.在本教程的第一部分,您将从头开 ...

  7. Gradle 1.12 翻译——第十六章. 使用文件

    有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...

  8. OpenGL ES着色器语言之语句和结构体(官方文档第六章)内建变量(官方文档第七、八章)

    OpenGL ES着色器语言之语句和结构体(官方文档第六章) OpenGL ES着色器语言的程序块基本构成如下: 语句和声明 函数定义 选择(if-else) 迭代(for, while, do-wh ...

  9. SpringMVC强大的数据绑定(2)——第六章 注解式控制器详解

    SpringMVC强大的数据绑定(2)——第六章 注解式控制器详解 博客分类: 跟开涛学SpringMVC   6.6.2.@RequestParam绑定单个请求参数值 @RequestParam用于 ...

随机推荐

  1. [译]HIPAA要求的SSL/TLS等级

    原文链接:https://luxsci.com/blog/level-ssl-tls-required-hipaa.html 原文发表时间:2015.1.13 本博文仅仅是上述原文的翻译,仅供研究参考 ...

  2. U盘格式和对齐扇数(常识)

    一.文件系统常见的四种格式:FAT16.FAT32.NTFS.ExFAT. 虽然四种文件系统的存储方式和特性各不相同,但对于传输速度并不快的存储卡和U盘来说, 实际传输性能却大同小异,理论测试数据相差 ...

  3. HDU2473 Junk-Mail Filter 【可删除的并查集】

    HDU2473 Junk-Mail Filter Problem Description Recognizing junk mails is a tough task. The method used ...

  4. BZOJ4521 Cqoi2016 手机号码 【数位DP】

    Description 人们选择手机号码时都希望号码好记.吉利.比如号码中含有几位相邻的相同数字.不含谐音不吉利的数字等.手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出 ...

  5. (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序

    每次使用 Visual Studio 的模板创建一个 UWP 程序,我们会在项目中发现大量的项目文件.配置.应用启动流程代码和界面代码.然而这些文件在 UWP 程序中到底是如何工作起来的? 我从零开始 ...

  6. 微软 Windows 系统检测网络连通性(用于显示感叹号)竟然是通过访问一个特殊网址来实现的

    一次我走到了弱网环境中,意外地发现浏览器打开了 http://www.msftconnecttest.com/redirect 网址,随后右下角的网络图标出现了一枚“感叹号”.   吹水的推断 从直观 ...

  7. 如何最快速地将旧的 NuGet 包 (2.x, packages.config) 升级成新的 NuGet 包 (4.x, PackageReference)

    最近我将项目格式进行了升级,从旧的 csproj 升级成了新的 csproj:NuGet 包管理的方式也从 packages.config 升级成了 PackageReference.然而迁移完才发现 ...

  8. 重温CLR(十三) 定制特性

    利用定制特性,可宣告式为自己的代码构造添加注解来实现特殊功能.定制特性允许为几乎每一个元数据表记录项定义和应用信息.这种可扩展的元数据信息能在运行时查询,从而动态改变代码的执行方式.使用各种.NET技 ...

  9. Robotframework第1课--安装RF

    大家好,我是孟船长,现从事自动化测试的工作,工作用的工具就是Robotframework,现在把这“几年”的所得分享出来,希望新进入这行的朋友能够少吃点“新人苦”,能够早点入手robot framew ...

  10. 安装WampServer关闭mysql服务后打不开了

    WampServer自带了mysql精简班的数据库了 WampServer自带 的mysql和你独立安装的mysql端口号冲突了 mysql默认的端口号是3306 建议你修改WampServer的数据 ...