#architecture #mvvm #ninject #mvvm-light
#архитектура #mvvm #ninject #mvvm-light
Вопрос:
Мне нужен совет по архитектуре приложения.
Я создаю настольное приложение .Net 4 WPF с поддержкой значков области уведомлений.
Приложение имеет несколько окон, которые отображаются при запуске, затем закрываются, и остается только значок области уведомлений.
Значок области уведомлений — это чисто элемент управления WPF, который я получил из этого примера codeproject.
Поскольку мое приложение должно оставаться запущенным, даже когда все окна закрыты, я установил
ShutdownMode="OnExplicitShutdown"
в App.xaml.
Я опишу свою идею архитектуры и механизма запуска, а вы скажите мне, где я ошибаюсь, и поправьте меня, если возможно.
В App.xaml.cs я создаю Ninject StandardKernel
, давайте назовем его appKernel
и загрузим в него модули Ninject. Сначала Ninject должен разрешить только один интерфейс — heartbeatManager
экземпляр. HeartbeatManager — это класс, который я планирую использовать для:
а) Размещение моего экземпляра NotifyIcon в качестве переменной поля, чтобы он был видимым до тех пор, пока экземпляр класса находится в памяти.
б) Реализация события завершения работы, на которое я подпишусь в app.xaml.cs, и явное завершение работы приложения, когда класс heartbeat запрашивает его.
На этом этапе создается heartbeatManager
экземпляр, который просто остается висеть в памяти.
В App.xaml я установил StartupUri="MainWindow.xaml"
, чтобы было создано и показано главное окно. Следуя шаблону MVVM Light ViewModelLocator, MainWindow пытается разрешить контекст данных из статического экземпляра ViewModelLocator, определенного в App.xaml:
<Application.Resources>
<ViewModels:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
</Application.Resources>
Создается экземпляр ViewModelLocator, и в конструкторе он инициализирует другое стандартное ядро (есть ли какой-либо другой тип ядра, который я использую на данный момент?) это будет использоваться для разрешения привязок модели представления. Модель представления MainWindow предоставляется для удовлетворения запроса Windows, и все приложение продолжает работать.
Очень важным следствием является то, что экземпляр hearbeatManger привязан к определению интерфейса в однотонной области, так что модели просмотра, которые требуют его в качестве параметра конструктора, могут разрешать и получать уже созданный экземпляр. Будет ли работать такое разрешение зависимостей, если модель представления и heartbeatManager загружены в разные ядра? Если нет, как я могу решить эту проблему?
Является ли описанный выше сценарий хорошим с точки зрения архитектуры, может ли он быть реализован в коде, или я допустил ошибку, когда продумывал архитектуру?
Комментарии:
1. Мне трудно понять, в чем здесь реальная проблема. Вы говорите о завершении работы приложения и зависании экземпляров в памяти, но вы не объяснили, почему вас это беспокоит. Класс приложения будет сохраняться, потому что он создан в сгенерированном
Main
методе, и ваш контейнер / ядро должны сохраняться, если вы поместите его в свой класс приложения, а классы, которые вы создаете, должны сохраняться до тех пор, пока контейнер работает (особенно одиночные элементы). Почему у вас есть два ядра / контейнера? Кроме того, это может быть лучше для codereview.stackexchange.com .2. Проблема заключается в наличии двух контейнеров. Я вижу это, когда я приступил к попытке реализовать все это. Но как я могу передать ядро, которое я создаю в app.xaml.cs, конструктору viewmodel locator, чтобы избежать создания второго ядра для разрешения viewmodel?
3. Я не уверен, почему вы даже позволяете какому-либо вашему коду знать о ядре. Что особенного в вашем приложении, которое требует от вас прямого использования ядра (за исключением верхнего уровня)? Похоже, вы можете использовать его в качестве локатора служб, когда он действительно предназначен для внедрения зависимостей. Просто у вас возникла проблема с выяснением того, как заставить контейнер / ядро удовлетворять
DataContext
, то естьObject
, и не позволяет вам определять атрибуты, специфичные для просмотра?4. Я использую ninject в качестве локатора службы в классе локатора viewmodel, как описано здесь: kellabyte.com/2010/08/23 /…
5. Это неправильный способ определения местоположения моих моделей просмотра? Я почти реализовал описанную выше архитектуру, остается единственная проблема, как передать ядро, которое я создаю на верхнем уровне, конструктору viewmodellocator.
Ответ №1:
…На этом этапе создается экземпляр heartbeatManager, который просто остается висеть в памяти…
Если он зарегистрирован в вашем ядре, это не какая-либо магия, которая заставляет его зависать в памяти — он будет оставаться до тех пор, пока это делает ядро / контейнер. Этот контейнер, вероятно, должен быть членом вашего класса App, поскольку ваш класс app сохраняется до тех пор, пока работает метод (сгенерированный компилятором) Main
, и вы все равно должны утилизировать его при завершении работы приложения.
…Следуя шаблону MVVM Light ViewModelLocator, MainWindow пытается разрешить контекст данных из статического экземпляра ViewModelLocator, определенного в App.xaml…
Я не уверен, что это будет хорошо сочетаться с NInject. Это контейнер для внедрения зависимостей, а не контейнер для поиска служб (хотя он использует его под капотом). Основная цель NInject (и аналогичных фреймворков) — избежать необходимости использования шаблона поиска служб и вместо этого внедрить все ваши зависимости.
…Создается экземпляр ViewModelLocator, и в конструкторе он инициализирует другое стандартное ядро…
Если ваш сценарий не является довольно сложным (что на самом деле не для этого приложения), тогда я предлагаю придерживаться одного ядра NInject.
Предлагаемое решение
Сам класс view model locator мало что делает для вас, кроме как позволяет разделить вашу логику инициализации и выполнять выборочную инициализацию в зависимости от того, находитесь ли вы в режиме разработки или в режиме выполнения. Вы могли бы достичь того же с помощью модулей NInject (которые уже используются в статье view model locator, на которую вы ссылались в комментариях).
Я предлагаю вместо использования локатора модели представления указать все свои компоненты в своем классе модуля и выполнить IsInDesignMode
логику в Load
методе.
Это может быть немного сложнее с MVVM, поскольку модель представления должна привязываться к object
свойству, которое вы не создавали, и не может аннотировать.
Есть несколько способов решить эту проблему непосредственно в NInject, вместо того, чтобы прибегать к локатору служб:
- Используйте внедрение зависимостей в свой вид, чтобы для него требовалась модель представления.
Если это не работает с использованием внедрения конструктора (как вы упомянули в своих комментариях), вместо этого используйте внедрение свойств с пользовательским свойством, которое перенаправляет его установщик наDataContext
. - Используйте методы NInject factory (
ToMethod
) для создания своего представления и привязывайте модель представления к представлению в каждом заводском методе.
Например.Bind<MainView>().ToMethod(context => new MainView() { DataContext = new MainViewModel() });
Если вы можете, я бы выбрал второй метод. Таким образом, вашему представлению не нужно обрабатывать привязку данных и не обязательно иметь какой-либо код. Это также просто для понимания, позволит вам избежать необходимости регистрировать как представления, так и модели представлений в ядре, и позволит вам избежать создания какого-либо хорошо известного интерфейса для вашей модели представления.