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



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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值