领域驱动设计之代码优先-领域层设计-8 (翻译)

来源:互联网 发布:c语言点滴pdf微盘 编辑:程序博客网 时间:2024/05/16 08:57


3.9.-  领域实体的”数据注解“


  目前为止我们让EF使用默认的协定发现模型,但有时候当我们的类不按照协定,我们需要进行更多的配置。
就像我们提到的,有两个选择;我们先看数据注解然后看”Fluent API“。
  假设我们的BankAccount实体的Id属性不是”BankAccountId‟而是叫做“BankAccountNumber‟的属性。我们
试着运行应用但会得到InvalidOperationException,提示实体类型'BankAccount'没有定义键。因为EF不知道
”BankAccountNumber‟应该是主键。


  为了解决这个问题,这里我们使用“数据注解”,需要添加一个引用:


项目 -> 添加引用...
选择 .NET 选项卡
选择 “System.ComponentModel.DataAnnotations”
点击 确定


在.cs文件顶添加using声明:


using System.ComponentModel.DataAnnotations; 


  考虑到这个命名空间不是EF而是.NET框架的一部分,“数据注解“也可以用在其他的技术,不只是EF。
如果EF需要其他的属性,可以在EntityFramework.dll找到,但是之后,这一步对持久化透明不利。
  现在我们在”BankAccountNumber‟属性上加上主键的注解:
//POCO Domain Entity using ‘Data Annotations’ // public class BankAccount : Entity {     //Attributes     //     [Key]     public int BankAccountNumber { get; set; }          public string BankAccountNumber { get; set; }         public decimal Balance { get; set; }     public int CustomerId { get; set; }     public bool Locked { get; set; }     //      //Domain Entity Logic     //             ...     ...     ... } 

现在,我们可以使用BankAccountNumber作为数据库的主键。
  
  注意:“数据注解”不是EF中的新概念。我们也可以使用在ASP.NET Dynamic Data 和“„WCF RIA Services‟中。
       实际上,这些技术也使用相同的程序集和命名空间:System.ComponentModel.DataAnnotations


  有很多其他的数据注解属性。下面展示了数据注解类的所有属性:


  
3.10.- 实体验证


  从EF 4.1开始,这是EF第一次提供实体的验证。这个主意可能看起来很简单,但其实不是,它的实现对于
大多数从头开始的项目有很大的影响。
  区分实体模型的验证和业务概念的验证很重要。一般来说,实体模型验证可以省去很多不必要的返工。
  实体可以由几种方法实现,下面是主要的:
-  数据注解(EF) 
-  Fluent API (EF) 
-  实现IValidatableObject 


3.10.1.- 使用实体验证的数据注解
  
  与其他任何主题的一样,尽管数据注解非常有吸引力,这也是一种使实体不纯净的方法,所以在领域驱动
设计中我们不推荐使用数据注解。我们推荐使用IValidatableObject或者“Fluent API‟。
  下面是一个使用数据注解的一个实体示例:

//Initial Customer Entity // public class Customer : Entity {    public int CustomerId { get; set; }        [Required()]    [MaxLength(20)]    public string FirstName { get; set; }        [Required()]    [StringLength(20)]    public string LastName { get; set; }    public string City { get; set; }    public string Street { get; set; }    public string ZipCode { get; set; }    } 


  这段代码的问题是当我们使用在EF程序集中定义的属性时,这样我们的实体就不是持久化透明了。
另一方面,如果我们使用.NET程序集中的属性数据注解时,就没有对EF的直接依赖。


  如果我们不想再验证属性中有EF或MVC的依赖,我们会使用派生自ValidationAttribute的属性。
所以我们可以用下面的属性:


表 14.- System.ComponentModel.DataAnnotations 验证注解
 
验证属性 目的


StringLengthAttribute 字符串的最大曾度


RequiredAttribute 必须的元素


RegularExpressionAttribute 需要匹配指定的正则表达式


RangeAttribute 检查在一定区间的值


DataTypeAttribute 指定关联一个字段的额外类型的名字


CustomValidationAttribute 自定义验证


  CustomValidationAttribute提供了自定义验证的代理;因此这会是领域没有外部依赖的一部分。
  例如,下面我们定义信用卡号的自定义验证。
public static ValidationResult ValidateCCNumber(string creditCardNumber) {     bool result;      //TODO: Validate DNI     ... CC validation algorithm ...  ...    if (!result)  {    return new ValidationResult("Invalid CC number", new string[] { "CC" });  }  else    return null; } 
然后,我们会这样修改实体。

[CustomValidation(typeof(OrderValidation),"ValidateCCNumber")] public string CreditCardNumber { get; set; } 

总的来说,最解耦的方法是在领域模型层使用IvalidatableObject或在数据访问层实现”Fluent API‟。


3.10.2.- 使用IValidatableObject实体验证


  这个方法是非常持久化透明的,在领域模型层的验证代码没有外部依赖。
  该方法尤其适用于需要验证多个实体状态的业务验证。通常,这种验证视为“类级别验证”。
  为了实现这样的验证,EF产品组给予POCO代码优先实体IValidatableObject的支持,所以我们的实体可以
实现该接口来做更复杂的验证。
  对于这样验证重要的一点是只有在没有"注解级别“错误时才会执行。


  下面的代码展示了实现IvalidatableObject的验证
public abstract class Product     :Entity,IValidatableObject { //... //Ommitted Product Entity properties and methods //... //...  public IEnumerable<ValidationResult> Validate(ValidationContext                                                                validationContext) {         var validationResults = new List<ValidationResult>();          if (String.IsNullOrEmpty(Title) || String.IsNullOrWhiteSpace(Title))             validationResults.Add(new ValidationResult(Messages.                                     validation_ProductTitleCannotBeNullOrEmpty,                                      new string[] { "Title" }));          if (String.IsNullOrEmpty(Description)|| String.                                                IsNullOrWhiteSpace(Description))             validationResults.Add(new ValidationResult(                      Messages.validation_ProductDescriptionCannotBeNullOrEmpty,                                  new string[] { "Description" }));                        if (AmountInStock < 0)             validationResults.Add(new ValidationResult(Messages.                                           validation_ProductAmountLessThanZero,                                              new string[] { "AmountInStock" }));                     if (UnitPrice < 0)             validationResults.Add(new ValidationResult(Messages.                                        validation_ProductUnitPriceLessThanZero,                                                  new string[] { "UnitPrice" }));                      return validationResults;     } } 

原创粉丝点击