首页 文章

将验证属性从域实体映射到DTO

提问于
浏览
31

我有一个标准的Domain Layer实体:

public class Product
{
    public int Id { get; set; }

    public string Name { get; set; }

    public decimal Price { get; set;}
}

它具有某种验证属性:

public class Product
{
    public int Id { get; set; }

    [NotEmpty, NotShorterThan10Characters, NotLongerThan100Characters]
    public string Name { get; set; }

    [NotLessThan0]
    public decimal Price { get; set;}
}

如您所见,我已经完全弥补了这些属性 . 这里使用的验证框架(NHibernate Validator,DataAnnotations,ValidationApplicationBlock,Castle Validator等)并不重要 .

在我的客户端层,我也有一个标准设置,我不使用域实体本身,而是将它们映射到我的视图层使用的ViewModels(aka DTO):

public class ProductViewModel
{
    public int Id { get; set; }

    public string Name { get; set; }

    public decimal Price { get; set;}
}

然后我们说我希望我的客户端/视图能够执行一些基本的属性级验证 .

我看到我能做到这一点的唯一方法是重复ViewModel对象中的验证定义:

public class ProductViewModel
{
    public int Id { get; set; }

    // validation attributes copied from Domain entity
    [NotEmpty, NotShorterThan10Characters, NotLongerThan100Characters]
    public string Name { get; set; }

    // validation attributes copied from Domain entity
    [NotLessThan0]
    public decimal Price { get; set;}
}

这显然不令人满意,因为我现在在ViewModel(DTO)层中重复了业务逻辑(属性级验证) .

那可以做些什么呢?

假设我使用AutoMapper之类的自动化工具将我的Domain实体映射到我的ViewModel DTO,那么以某种方式将映射属性的验证逻辑转移到ViewModel也不是很酷吗?

问题是:

1)这是个好主意吗?

2)如果是这样,可以吗?如果没有,有什么替代方案,如果有的话?

提前感谢您的任何输入!

7 回答

  • 0

    如果您正在使用支持DataAnnotations的东西,您应该能够使用元数据类来包含验证属性:

    public class ProductMetadata 
    {
        [NotEmpty, NotShorterThan10Characters, NotLongerThan100Characters]
        public string Name { get; set; }
    
        [NotLessThan0]
        public decimal Price { get; set;}
    }
    

    并将其添加到域实体和DTO上的MetadataTypeAttribute中:

    [MetadataType(typeof(ProductMetadata))]
    public class Product
    

    [MetadataType(typeof(ProductMetadata))]
    public class ProductViewModel
    

    这不会与所有验证器一起开箱即用 - 您可能需要扩展您选择的验证框架以实现类似的方法 .

  • 4

    验证的目的是确保进入您的应用程序的数据符合某些标准,考虑到这一点,验证属性约束唯一有意义的地方,例如您在此处确定的那些,就是您接受来自不可信的来源(即用户) .

    您可以使用“money pattern”之类的东西将验证提升到您的域类型系统,并在视图模型中使用这些域类型 . 如果您有更复杂的验证(即您表达的业务规则需要比单个属性中表达的知识更多的知识),则这些属于应用更改的域模型上的方法 .

    简而言之,将数据验证属性放在视图模型上,并将它们从域模型中删除 .

  • 1

    事实证明,AutoMapper可以自动为我们这样做,这是最好的情况 .

    AutoMapper-users:将验证属性传递给viewmodel?
    http://groups.google.com/group/automapper-users/browse_thread/thread/efa1d551e498311c/db4e7f6c93a77302?lnk=gst&q=validation#db4e7f6c93a77302

    我没有在那里尝试提出的解决方案,但打算很快 .

  • 1

    为什么不使用界面来表达您的意图?例如:

    public interface IProductValidationAttributes {
        [NotEmpty, NotShorterThan10Characters, NotLongerThan100Characters]
        string Name { get; set; }
    
        [NotLessThan0]
        decimal Price { get; set;}
    }
    
  • 9

    如果您使用手写域实体,为什么不将您的域实体放在它们自己的程序集中,并在客户端和服务器上使用相同的程序集 . 您可以重复使用相同的验证 .

  • 3

    我一直在考虑这个问题 . 我完全理解布拉德的回答 . 但是,我们假设我想使用另一个适用于注释域实体和视图模型的验证框架 .

    我可以在纸上提出的仍然可以使用属性的唯一解决方案是创建另一个属性,该属性“指向”您正在视图模型中镜像的域实体的属性 . 这是一个例子:

    // In UI as a view model.
    public class UserRegistration {
      [ValidationDependency<Person>(x => x.FirstName)]
      public string FirstName { get; set; }
    
      [ValidationDependency<Person>(x => x.LastName)]
      public string LastName { get; set; }
    
      [ValidationDependency<Membership>(x => x.Username)]
      public string Username { get; set; }
    
      [ValidationDependency<Membership>(x => x.Password)]
      public string Password { get; set; }
    }
    

    像xVal这样的框架可以扩展为处理这个新属性并在依赖类的属性上运行验证属性,但是使用视图模型的属性值 . 我只是没有时间更多地充实这一点 .

    有什么想法吗?

  • 11

    首先,没有“标准”域实体的概念 . 对我来说,标准域实体没有任何开头的setter . 如果你采用这种方法,你可以拥有更有意义的api,它实际上传达了你的域名 . 因此,您可以拥有处理DTO的应用程序服务,创建可以直接针对您的域对象执行的命令,例如SetContactInfo,ChangePrice等 . 其中每一个都可以引发ValidationException,反过来您可以在服务中收集并呈现给用户 . 您仍然可以将属性保留在dto的属性上,以进行简单的属性/属性级别验证 . 其他任何事情,请咨询你的域名 . 即使这是CRUD应用程序,我也会避免将我的域实体暴露给表示层 .

相关问题