Mapping Validation Attributes From Domain Entity t

2019-01-21 11:02发布

I have a standard Domain Layer entity:

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

    public string Name { get; set; }

    public decimal Price { get; set;}
}

which has some kind of validation attributes applied:

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

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

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

As you can see, I have made up these attributes completely. Which validation framework (NHibernate Validator, DataAnnotations, ValidationApplicationBlock, Castle Validator, etc) in use here is not important.

In my client layer, I also have a standard setup where I don't use the Domain entities themselves, but instead map them to ViewModels (aka DTO) which my view layer uses:

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

    public string Name { get; set; }

    public decimal Price { get; set;}
}

Let's then say that I want my client/view to be able to perform some basic property-level validations.

The only way I see I can do this is to repeat the validation definitions in the ViewModel object:

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

This is clearly not satisfactory, as I have now repeated business logic (property-level validation) in the ViewModel (DTO) layer.

So what can be done?

Assuming that I use an automation tool like AutoMapper to map my Domain entities to my ViewModel DTOs, wouldn't it also be cool to somehow transfer the validation logic for the mapped properties to the ViewModel as well?

The questions are:

1) Is this a good idea?

2) If so, can it be done? If not, what are the alternatives, if any?

Thank you in advance for any input!

7条回答
ゆ 、 Hurt°
2楼-- · 2019-01-21 11:16

First of all, there is no notion of "standard" domain entity. For me, standard domain entity does not have any setters to begin with. If you take that approach, you can have more meaningful api, that actually conveys something about your domain. So, you can have application service that processes your DTO, creates commands that you can execute directly against you domain objects, like SetContactInfo, ChangePrice etc. Each one of these can raise ValidationException, which in turn you can collect in your service and present to the user. You can still leave your attributes on the properties of dto for simple attribute/property level validation. For anything else, consult your domain. And even if this is CRUD application, i would avoid exposing my domain entities to presentation layer.

查看更多
兄弟一词,经得起流年.
3楼-- · 2019-01-21 11:20

It turns out that AutoMapper may be able to do this for us automagically, which is the best case scenario.

AutoMapper-users: Transfer validation attributes to the viewmodel?
http://groups.google.com/group/automapper-users/browse_thread/thread/efa1d551e498311c/db4e7f6c93a77302?lnk=gst&q=validation#db4e7f6c93a77302

I haven't got around to trying out the proposed solutions there, but intend to shortly.

查看更多
霸刀☆藐视天下
4楼-- · 2019-01-21 11:26

The purpose of validation is to ensure that data coming into your application meets certain criteria, with that in mind, the only place it makes sense to validate property constraints, like those you have identified here, is at the point where you accept data from an untrusted source ( i.e. the user ).

You can use something like the "money pattern" to elevate validation into your domain type system and use these domain types in the view model where it makes sense. If you have more complex validation (i.e. you are expressing business rules that require greater knowledge than that expressed in a single property), these belong in methods on the domain model that apply the changes.

In short, put data validation attributes on your view models and leave them off your domain models.

查看更多
Anthone
5楼-- · 2019-01-21 11:27

Why not use an interface to express your intent? Eg:

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

    [NotLessThan0]
    decimal Price { get; set;}
}
查看更多
三岁会撩人
6楼-- · 2019-01-21 11:33

If you use hand-written domain entities, why not put your domain entities in their own assembly and use that same assembly both on the client and server. You can reuse the same validations.

查看更多
成全新的幸福
7楼-- · 2019-01-21 11:38

If you're using something supporting DataAnnotations, you should be able to use a metadata class to contain your validation attributes:

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

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

and add it in the MetadataTypeAttribute on both the domain entity & DTO:

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

and

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

This won't work out of the box with all validators - you may need to extend your validation framework of choice to implement a similar approach.

查看更多
登录 后发表回答