Сопоставление атрибутов проверки с сущности домена в DTO - PullRequest
32 голосов
/ 16 января 2010

У меня есть стандартная сущность уровня домена:

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 (он же 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, для сопоставления сущностей моего Домена с моими DTO ViewModel, разве не было бы здорово как-то перенести логику проверки для сопоставленных свойств в ViewModel?

Вопросы:

1) Это хорошая идея?

2) Если это так, можно ли это сделать? Если нет, каковы альтернативы, если таковые имеются?

Заранее спасибо за любой вклад!

Ответы [ 8 ]

11 голосов
/ 11 февраля 2010

Если вы используете что-то, поддерживающее DataAnnotations, вы должны иметь возможность использовать класс метаданных для хранения ваших атрибутов проверки:

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

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

и добавьте его в атрибут MetadataTypeAttribute для объекта домена и DTO:

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

и

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

Это не будет работать из коробки со всеми валидаторами - вам может понадобиться расширить выбранную платформу валидации для реализации аналогичного подхода.

9 голосов
/ 10 февраля 2010

Цель проверки состоит в том, чтобы гарантировать, что данные, поступающие в ваше приложение, соответствуют определенным критериям, с учетом этого единственное место, в котором имеет смысл проверять ограничения свойств, подобные тем, которые вы определили здесь, находится в точке, где вы принимаете данные из ненадежного источника (то есть пользователя).

Вы можете использовать что-то вроде «денежного шаблона», чтобы повысить валидацию в вашей системе типов доменов и использовать эти типы доменов в модели представления, где это имеет смысл. Если у вас более сложная проверка (т.е. вы выражаете бизнес-правила, которые требуют большего знания, чем те, которые выражены в одном свойстве), они относятся к методам в доменной модели, которые применяют изменения.

Короче говоря, добавьте атрибуты проверки данных в ваши модели представлений и оставьте их вне моделей вашего домена.

4 голосов
/ 20 февраля 2010

Оказывается, что AutoMapper может сделать это для нас автоматически, что является лучшим сценарием.

Пользователи AutoMapper: перенести атрибуты проверки в модель представления?
http://groups.google.com/group/automapper-users/browse_thread/thread/efa1d551e498311c/db4e7f6c93a77302?lnk=gst&q=validation#db4e7f6c93a77302

У меня не было возможности попробовать предложенные решения, но я собираюсь в ближайшее время.

3 голосов
/ 16 января 2010

Почему бы не использовать интерфейс для выражения своих намерений? Например:

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

    [NotLessThan0]
    decimal Price { get; set;}
}
1 голос
/ 18 февраля 2010

Я тоже обдумывал это некоторое время. Я полностью понимаю ответ Брэда. Однако предположим, что я хочу использовать другую платформу проверки, которая подходит для аннотирования как доменных сущностей, так и моделей представления.

Единственное решение, которое я могу придумать на бумаге, который все еще работает с атрибутами, - это создать еще один атрибут, который "указывает" на свойство объекта домена, которое вы отражаете в своей модели представления. Вот пример:

// 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, может быть расширена для обработки этого нового атрибута и запуска атрибутов проверки для свойства класса зависимостей, но со значением свойства вашей модели представления. У меня просто не было времени конкретизировать это.

Есть мысли?

1 голос
/ 16 февраля 2010

Если вы используете рукописные сущности домена, почему бы не поместить свои доменные сущности в их собственную сборку и использовать эту же сборку как на клиенте, так и на сервере. Вы можете повторно использовать те же проверки.

0 голосов
/ 12 июня 2019

Отказ от ответственности: Я знаю, что это старая дискуссия, но она была ближе всего к тому, что я искал: сохранение СУХОГО путем повторного использования атрибутов проверки. Я надеюсь, что это не слишком далеко от первоначального вопроса.

В моей ситуации я хотел, чтобы сообщения об ошибках были доступны в представлениях .NET и других моделях представления. Наши предприятия практически не имеют бизнес-логики и в основном предназначены для хранения данных. Вместо этого у нас есть большая модель представления с проверкой и бизнес-логикой, где я хочу повторно использовать сообщения об ошибках. Поскольку пользователи защищены только сообщениями об ошибках, я считаю, что это актуально, так как именно это важно поддерживать легко.

Я не смог найти реальный способ удалить логику из частичных моделей представления, но я нашел способ передать то же сообщение об ошибке, чтобы его можно было поддерживать из одной точки. Так как ErrorMessages привязаны к представлению, оно также может быть частью ViewModel. Const s считаются статическими членами, поэтому, определяя сообщения об ошибках как открытую строку const ants, мы можем получить к ним доступ вне класса.

public class LargeViewModel
{
    public const string TopicIdsErrorMessage = "My error message";

    [Required(ErrorMessage = TopicIdsErrorMessage)]
    [MinimumCount(1, ErrorMessage = TopicIdsErrorMessage)]
    [WithValidIndex(ErrorMessage = TopicIdsErrorMessage)]
    public List<int> TopicIds { get; set; }
}

public class PartialViewModel
{
    [Required(ErrorMessage = LargeViewModel.TopicIdsErrorMessage]
    public List<int> TopicIds { get; set; }
}

В нашем проекте мы использовали собственный html для выпадающих списков, так что мы не могли использовать хелпер @ Html.EditorFor в бритве, поэтому мы не могли использовать ненавязчивую проверку. Имея доступное сообщение об ошибке, мы теперь можем применить необходимые атрибуты:

    @(Html.Kendo().DropDownList()
        .Name("TopicIds")
        .HtmlAttributes(new {
            @class = "form-control",
            data_val = "true",
            data_val_required = SupervisionViewModel.TopicIdsErrorMessage
        })
    )

Предупреждение: Возможно, вам придется перекомпилировать все связанные проекты, использующие постоянные значения ...

0 голосов
/ 25 января 2010

Прежде всего, отсутствует понятие «стандартная» сущность домена. Для меня стандартная сущность домена не имеет никаких сеттеров для начала. Если вы воспользуетесь этим подходом, у вас может быть более значимый API, который фактически передает что-то о вашем домене. Таким образом, у вас может быть служба приложений, которая обрабатывает ваш DTO, создает команды, которые вы можете выполнять непосредственно против ваших доменных объектов, таких как SetContactInfo, ChangePrice и т. Д. Каждый из них может вызывать ValidationException, который, в свою очередь, вы можете собирать в своем сервисе и представлять Пользователь. Вы по-прежнему можете оставить свои атрибуты в свойствах dto для простой проверки уровня атрибута / свойства. Для чего-либо еще, обратитесь к вашему домену. И даже если это приложение CRUD, я бы не стал подвергать свои доменные сущности уровню представления.

...