MVVM дизайн кажется слишком громоздким, я делаю это неправильно? - PullRequest
7 голосов
/ 08 декабря 2011

Я недавно начал создавать приложение WPF и просто надеюсь, что кто-то может подтвердить мне, что я правильно строю свою общую архитектуру системы, или исправить меня, если я иду куда-то не в ту сторону. Тем более, что я пытаюсь создать MVVM, в нем задействовано много слоев, и я не уверен, что все делаю правильно.

Вот упрощенное описание системы:

Данные хранятся в базе данных SQL Server, доступ к которой осуществляется через Linq to SQL. Допустим, база данных содержит две таблицы USERS и USER_GROUPS. Каждая таблица имеет автоматически сгенерированный класс Linq to SQL, DB_USER и DB_USER_GROUP.

Теперь в приложении я хочу отобразить ListBox с каждым ListBoxItem, содержащим различные элементы пользовательского интерфейса для отображения / изменения информации пользователя, что выполняется с помощью DataTemplate.

У меня есть класс модели представления для окна, который использует запрос Linq to SQL (соединяющий две таблицы) для заполнения ObservableCollection<User> с именем UserList, который ListBox в окне связал как ItemsSource. User - это класс, реализующий INotifyPropertyChanged, который обрабатывает все форматирование / получение / настройку данных базы данных в соответствии с требованиями элементов управления WPF. Раздел обработки кода это что-то вроде:

DBDataContext db = new DBDataContext();

var allUsers = from user in db.USERs
                   .Where(u => u.ENABLED == true)
               from group in db.USER_GROUPs
                   .Where(g => g.GROUPID == u.GROUPID)
                   .DefaultIfEmpty()
               select new { user, group };

foreach (var user in allUsers)
{
    User u = new User(db, user.user, user.group);
    UserList.Add(u);
}

Таким образом, класс User создается с частными свойствами для DB_USER, DB_USER_GROUP и класса базы данных DataContext. Все открытые свойства User в основном обертывают соответствующие столбцы, их методы get возвращают значения для использования WPF и set изменяют столбцы и затем вызывают SubmitChanges() для закрытого DataContext свойство для обновления базы данных.

Это все работает нормально, но кажется немного громоздким, поэтому мне просто интересно, пропустил ли я что-нибудь, что сделало бы его чище. В частности, хранение DataContext внутри каждого элемента UserList кажется странным, но я не был уверен в лучшем способе обновления базы данных при изменении данных в пользовательском интерфейсе.

Любые отзывы приветствуются, и, пожалуйста, дайте мне знать, если что-то неясно, я не уверен, насколько хорошо я это объяснил.

Ответы [ 4 ]

6 голосов
/ 08 декабря 2011

Начнем с того, что давайте сделаем несколько ярлыков о том, что вы здесь делаете: DB_USER - это ваша модель, а User - это ваша модель представления (я бы предпочел UserViewModel для последней, чтобы было более понятно, что происходит. на).

Одна вещь, которая сразу становится очевидной, это то, что для вашей ViewModel не совсем правильно иметь функциональность, подходящую для вашей модели, то есть, что DataContext не относится к тому месту, где он находится в данный момент. Это часть информации, которая должна быть либо в вашей модели, либо альтернативно инкапсулирована в некоторый класс DataStore / DataService (на ваш выбор). Тогда ваша ViewModel будет ответственна, когда придет время сохранять любые изменения, чтобы сообщить DataStore «вот обновленный снимок этой модели, пожалуйста, сохраните его для меня» (это, скорее всего, будет доступно пользовательскому интерфейсу через ICommand). Это выглядит чище и подчеркивает идею о том, что ваша ViewModel - это слой, который адаптирует реалии вашей модели к выбранному вами интерфейсу.

Кроме вышесказанного, в том, что вы описываете, нет ничего, что, по моему мнению, нуждается в "исправлении". Тем не менее, я могу предложить несколько советов относительно того, о чем вы не разработали.

Предоставление данных из модели через ViewModel всегда можно реализовать разными способами. При рассмотрении того, какой подход выбрать, вы должны принять во внимание возможность того, что одна и та же модель будет представлена ​​в разных представлениях одновременно. В этом случае, IMHO, предпочтительный подход состоит в том, чтобы иметь отдельную ViewModel для каждого View (представления могут быть разных типов, поэтому они могут иметь разные ожидания от адаптера ViewModel, указывая, таким образом, на несколько типов ViewModel), так что вы потребуется использовать шаблон, который позволяет передавать изменения из одной ViewModel в другие в «реальном времени».

Один из способов сделать это состоит в том, чтобы заставить ваши Модели реализовать INotifyPropertyChanged самим, и каждый ViewModel подключается к своей Модели для уведомлений, поэтому, когда происходит изменение, ViewModel A передает изменение в Модель, и Модель уведомляет ViewModel B .

Однако лично мне не нравится загрязнять мои Модели тем, что по сути является кодом, который обслуживает только потребности пользовательского интерфейса, поэтому необходим другой подход. Это означало бы, что DataService, о котором я упоминал выше, предоставляет функциональность (методы и события), с помощью которой ViewModel A может сообщить службе «эй, модель, которую я упаковываю, имеет некоторые изменения»; обратите внимание, что это отличается от "Я хочу, чтобы вы сохранили текущий снимок этой модели". ViewModel B уже подключен к подходящему событию ModelChanged, поэтому он получает уведомление и получает обновленную информацию из службы. Это дает дополнительное преимущество, заключающееся в том, что если в любое время служба обнаружит, что хранилище данных базы данных было обновлено источником, внешним по отношению к текущему процессу, существует готовый механизм для передачи «Вызов всех моделей представления: модель X была обновлена, любой заинтересованные стороны, пожалуйста, поговорите со мной об изучении деталей "сообщение.

Прежде всего, всегда имейте в виду, что не существует «единого истинного стиля MVVM» и существует множество возможных подходов. Какой из них выбрать, зависит не только от неопровержимых фактов и текущей позиции ползунка по шкале YAGNI / HyperEngineering, но и, скажем так, от вашего вкуса .

3 голосов
/ 08 декабря 2011

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

То, как я подхожу к ViewModel, является скорее оберткой для моей базовой модели данных, чем улучшением моего View. Это помогает представлению, предоставляя INotifyPropertyChanged и все такое, но я пытаюсь сделать ViewModel полезным для любого представления, в котором оно окажется. Это главное отличие IMO между MVC и MVVM. То, что у вас есть, это больше MVC с вашим объектом User, действующим в качестве контроллера для View.

Кроме того, я бы, вероятно, покончил с UserList и использовал бы ListBox.ItemsSource для управления списком после его привязки один раз. На самом деле, в WPF я привык привыкать к CollectionViewSource и привязывать к нему элементы управления UI. Упрощает отслеживание выбранных элементов, добавление к ним и удаление из них не слишком обременительно.

Для доступа к данным я бы вообще не вводил их в объект. Доступ к данным принадлежит вашей модели, и сохраняющиеся изменения должны обрабатываться там. Платформа .Net довольно хорошо справляется с управлением соединениями до такой степени, что повторное создание экземпляров и разборка - это следующая лучшая вещь в мире, не требующая затрат. Это позволяет держать вещи в узде и оборачивать соединения данных при использовании предложений. Если вы действительно чувствуете, что должны сохраняться в базе данных при каждом изменении свойства, попросите вашу модель подписаться на событие PropertyChanged & mdash; в конце концов это то, для чего оно нужно. :)

2 голосов
/ 08 декабря 2011

Я обычно придерживаюсь этого подхода с некоторыми отличиями, что вызывает большое удивление :) В более крупных проектах я не люблю оборачивать или включать свои доменные сущности в мои ViewModels. Это то, что я перенес из ASP.NET MVC в книги действий, где вы отображаете (AutoMapper) доменные сущности для отображения моделей до того, как представление их получит. Приятно, когда существует импеданс между сущностью (ями) домена и тем, что должно отображаться.

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

Так что в некоторых случаях у меня будут мои модели сущностей (User / UserGroup), моя DisplayModel (Пользователь) и моя ViewModel (UserViewModel).

Как уже упоминалось в других ответах, я стараюсь не использовать данные в ViewModel. Я создам репозиторий или службу какого-либо типа и добавлю ее в мои ViewModels. Так что в моей ViewModel я мог бы вызвать var user = UserRepository.GetUsers(); и затем отобразить результаты в моей DisplayModel. Или UerRepository.InsertOrUpdate(user); UserRepository.Save();

Не имеет отношения, AutoMapper - отличный инструмент, а также Caliburn Micro или MVVM-Light.

2 голосов
/ 08 декабря 2011

Обычно я держу свои модели настолько глупыми, насколько это возможно (POCO). Моя viewmodel имеет тяжелую логику запросов к базе данных и установки ее свойств, которые связаны с пользовательским интерфейсом. В этом макете у модели представления есть единственный экземпляр вашего DataContext, и нет необходимости передавать его внутри моделей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...