В соответствии с традиционным подходом или теорией, ViewModel должна быть частью уровня пользовательского интерфейса. По крайней мере, имя так говорит.
Но когда вы приступаете к реализации этого самостоятельно с помощью Entity Framework, MVC, Repository и т. Д., Тогда вы понимаете что-то еще.
Кто-то должен сопоставить модели Entity / DB с ViewModels (DTO упоминается в конце). Должно ли это быть сделано на [A] уровне пользовательского интерфейса (Контроллером) или на [B] уровне Сервиса?
Я выбрал вариант B. Вариант A - это нет, нет, потому что тот простой факт, что несколько моделей сущностей объединяются в одну ViewModel. Мы не можем передавать ненужные данные на уровень пользовательского интерфейса, тогда как в варианте B служба может воспроизводить данные и передавать только требуемый / минимум на уровень пользовательского интерфейса после сопоставления (в ViewModel).
Опять же, давайте перейдем к варианту A, поместим ViewModel на уровень пользовательского интерфейса (и модель сущностей на уровень Service).
Если уровень сервиса должен отображаться на ViewModel, то слой сервиса должен иметь доступ к ViewModel на уровне пользовательского интерфейса. Какая библиотека / проект? Модель представления должна находиться в отдельном проекте на уровне пользовательского интерфейса, и на этот проект должен ссылаться сервисный уровень. Если ViewModel не находится в отдельном проекте, то есть круговая ссылка, поэтому не стоит. Выглядит неловко, когда сервисный уровень получает доступ к пользовательскому интерфейсу, но все же мы могли бы справиться с этим.
Но что, если есть другое приложение пользовательского интерфейса, использующее этот сервис? Что делать, если есть мобильное приложение? Насколько может отличаться ViewModel? Должна ли Служба получать доступ к одному и тому же проекту модели представления? Будут ли все проекты пользовательского интерфейса иметь доступ к одному и тому же проекту ViewModel или у них есть свой собственный?
После этих соображений мой ответ будет состоять в том, чтобы поместить проект Viewmodel в Service Layer. Каждый уровень пользовательского интерфейса в любом случае должен иметь доступ к уровню сервиса! И может быть много похожих ViewModel, которые они все могут использовать (следовательно, отображение становится проще для уровня обслуживания). В наши дни сопоставления выполняются через linq, что является еще одним плюсом.
Наконец, есть обсуждение DTO. А также об аннотации данных во ViewModels. Модели представления с аннотациями данных (Microsoft.Web.Mvc.DataAnnotations.dll) не могут находиться на уровне службы, вместо этого они находятся на уровне пользовательского интерфейса (но ComponentModel.DataAnnotations.dll может находиться на уровне службы). Если все проекты находятся в одном решении (.sln), то не имеет значения, какой слой вы поместите. В корпоративных приложениях каждый уровень будет иметь свое собственное решение.
Таким образом, DTO на самом деле является ViewModel, потому что в большинстве случаев между ними будет отображаться один на один (скажем, с AutoMapper). Опять же, DTO по-прежнему обладает логикой, необходимой для пользовательского интерфейса (или нескольких приложений), и находится на уровне обслуживания. А слой пользовательского интерфейса ViewModel (если мы используем Microsoft.Web.Mvc.DataAnnotations.dll) предназначен только для копирования данных из DTO с добавлением некоторых «поведений» / атрибутов.
[Теперь эта дискуссия собирается принять интересный поворот, читайте дальше ...: I]
И не думайте, что атрибуты аннотации данных предназначены только для пользовательского интерфейса. Если вы ограничите проверку с помощью System.ComponentModel.DataAnnotations.dll
затем тот же ViewModel также можно использовать для проверки внешнего интерфейса и внутреннего интерфейса (таким образом, удаляя UI-residing-ViewModel-copy-of-DTO). Более того, атрибуты также могут использоваться в моделях сущностей. Например: используя .tt, модели данных Entity Framework могут быть автоматически сгенерированы с атрибутами проверки для выполнения некоторых проверок БД, таких как максимальная длина, перед отправкой на серверную часть. Другое преимущество состоит в том, что если в БД изменяется внутренняя валидация, то .tt (читает специфику БД и создает атрибут для класса сущности) автоматически подхватит это. Это также может привести к сбою модульных тестов проверки пользовательского интерфейса, что является большим плюсом (поэтому мы можем исправить это и сообщить всем пользовательским интерфейсам / потребителям вместо того, чтобы случайно забыть и потерпеть неудачу). Да, обсуждение движется в направлении хорошего дизайна рамок. Как вы видите, все это связано: многоуровневая проверка, стратегия модульного тестирования, стратегия кэширования и т. Д.
Хотя это не имеет прямого отношения к вопросу.«ViewModel Façade», упомянутый в этом разделе, должен смотреть канал 9, ссылка также заслуживает изучения.Это начинается ровно в 11 минут 49 секунд в видео.Потому что это будет следующий шаг / мысль, как только ваш текущий вопрос, рассмотренный выше, будет рассмотрен: «Как организовать ViewModels?»
Также в вашем примере "_repository.ListContacts ()" возвращает ViewModel из хранилища.Это не зрелый путь.Хранилища должны предоставлять модели сущностей или модели БД.Это преобразуется в модели представления, и именно эта модель представления возвращается уровнем обслуживания.