Возможна ли инъекция зависимостей с помощью приложения WPF?
Я хочу начать использовать инъекцию зависимостей в моем приложении WPF, в основном для лучшей проверки работоспособности. Мое приложение в основном построено по шаблону M-V-VM.
Я смотрю autofac для моего контейнера IoC, но я не думаю, что это слишком важно для обсуждения.
Включение службы в начальное окно кажется простым, поскольку я могу создать контейнер и разрешить его в App.xaml.cs.
Чего я боюсь, так это то, как я могу использовать ViewModels и сервисы DI в пользовательских элементах управления? Пользовательские элементы управления создаются с помощью разметки XAML, поэтому нет возможности Resolve()
их.
Лучшее, что я могу придумать, - разместить контейнер в Синглтоне, а пользовательские элементы управления разрешат их ViewModels из глобального контейнера. В лучшем случае это похоже на решение на полпути, поскольку все еще требуется, чтобы мои компоненты зависели от ServiceLocator.
Возможна ли полная IoC с помощью WPF?
[править] - Призма была предложена, но даже оценка Призма кажется большой инвестицией, я надеюсь на что-то меньшее
[edit] здесь фрагмент кода, где я остановлен
//setup IoC container (in app.xaml.cs)
var builder = new ContainerBuilder();
builder.Register<NewsSource>().As<INewsSource>();
builder.Register<AViewModel>().FactoryScoped();
var container = builder.Build();
// in user control ctor -
// this doesn't work, where do I get the container from
VM = container.Resolve<AViewModel>();
// in app.xaml.cs
// this compiles, but I can't use this uc,
//as the one I want in created via xaml in the primary window
SomeUserControl uc = new SomeUserControl();
uc.VM = container.Resolve<AViewModel>();
Ответы
Ответ 1
На самом деле это очень легко сделать. У нас есть примеры этого в Призме, как упоминал джедиджа. Вы можете либо вставить ViewModel с помощью View или View, который будет введен с помощью ViewModel. В Prism StockTraderRI вы увидите, что мы вводим View в ViewModel. По сути, происходит то, что интерфейс View (и View) имеет свойство Model. Это свойство реализовано в коде, чтобы установить DataContext в значение, например: this.DataContext = value;
. В конструкторе ViewModel представление вводится. Затем он устанавливает View.Model = this;
, который передаст себя как DataContext.
Вы также можете легко сделать обратное и включить ViewModel в представление. Я действительно предпочитаю это, потому что это означает, что ViewModel больше не имеет обратной ссылки на представление вообще. Это означает, что при модульном тестировании ViewModel у вас нет представления даже для Mock. Кроме того, он делает код чище, в том, что в конструкторе View он просто устанавливает DataContext в ViewModel, который был введен.
Я немного об этом расскажу в видеозаписи раздельных презентаций, которые Джереми Миллер и я дали в Kaizenconf. Первая часть которой находится здесь http://www.vimeo.com/2189854.
Надеюсь, это поможет,
Гленн
Ответ 2
Я думаю, вы попали в проблему. Элементы управления нужно вводить в родительский, а не декларативно, через XAML.
Чтобы DI работал, контейнер DI должен создать класс, принимающий зависимости. Это означает, что родительский элемент не будет иметь экземпляров дочерних элементов управления во время разработки и будет выглядеть как оболочка в дизайнере. Вероятно, это рекомендуемый подход.
Другой "альтернативный" - иметь глобальный статический контейнер, вызываемый из конструктора управления, или нечто подобное. Существует общая схема, в которой объявляются два конструктора: один со списком параметров для вставки конструктора, а другой без параметров, которые делегируют:
// For WPF
public Foo() : this(Global.Container.Resolve<IBar>()) {}
// For the rest of the world
public Foo(IBar bar) { .. }
Я бы назвал это антипаттерном, но тот факт, что некоторые рамки не оставляют другого выбора.
Я даже не являюсь экспертом в WPF, поэтому я ожидаю, что здесь будет здоровое служение downmod:), но надеюсь, что это поможет. Группа Autofac (связанная с главной страницы) может быть другим местом, чтобы задать этот вопрос. Примеры приложений Prism или MEF (включая некоторые примеры WPF) должны дать вам представление о том, что возможно.
Ответ 3
Вы должны взглянуть на Prism из команды p & p. Вот сайт на Codeplex
Ответ 4
Хамм,
Мы сталкиваемся с аналогичной проблемой, которую мы ожидаем от решения, которое обеспечит поддержку времени разработки в Expression Blend 2.0 (Strong Type). Плюс мы с нетерпением ждем решения, чтобы иметь образец Mock + Auto-Generated data, доступный в Expression Blend.
Конечно, мы также хотим, чтобы все эти вещи работали с использованием шаблона IOC.
Пол Стовелл как интересная статья, чтобы начать с:
http://www.paulstovell.com/blog/wpf-dependency-injection-in-xaml
Итак, я попробую пару вещей, чтобы добавить более ценную поддержку времени разработки для привязки и издевательства объекта во время разработки, прямо сейчас. У меня большая часть проблемы связана с получением строго типизированного соединения между представлением (кодом) и ModelView (Xaml), я попробовал пару сценариев:
1.) Решение 1. Использование Generic для создания представления
public class MyDotNetcomponent<T> : SomeDotNetcomponent
{
// Inversion of Control Loader…
// Next step add the Inversion of control manager plus
// some MockObject feature to work under design time
public T View {Get;}
}
Это решение не работает, поскольку Blend не поддерживает Generic inside - это поверхность проектирования, но Xaml действительно имеет некоторые, хорошо работает во время выполнения, но не в дизайне;
2.) Решение 2: ObjectDataProvider
<ObjectDataProvider ObjectType="{x:Type CP:IFooView}" />
<!-- Work in Blend -->
<!—- IOC Issue: we need to use a concrete type and/or static Method there no way to achive a load on demande feature in a easy way -->
3.) Решение 3: Наследовать ObjectDataProvider
<CWD:ServiceObjectDataProvider ObjectType="{x:Type CP:IFooView}" />
<!-- Cannot inherit from ObjectDataProvider to achive the right behavior everything is private-->
4.) Решение 4. Создайте макет ObjectDataProvider с нуля для задания
<CWD:ServiceObjectDataProvider ObjectType="{x:Type CP:IFooView }" />
<!-- Not working in Blend, quite obvious-->
5.) Решение 5: Создайте расширение разметки (Paul Stovell)
<CWM:ServiceMarkup MetaView="{x:Type CP:IFooView}"/>
<!-- Not working in Blend -->
Просто, чтобы очистить одну точку, когда я сказал, что "не работает в blend", я имею в виду, что диалоговое окно Binding нельзя использовать, и дизайнеру нужно вручную переписать XAML.
Наш следующий шаг, вероятно, займет время, чтобы оценить возможность создания плагина для Expression Blend.
Ответ 5
Вы должны взглянуть на Caliburn - это простая инфраструктура MVC WPF/Silverlight MVC с поддержкой полного DI. Он выглядит действительно круто, и он позволяет вам использовать любой контейнер IoC, который вы хотите. Есть несколько примеров в документации wiki
Ответ 6
Да, мы делаем это все время. Вы можете "ввести" ваш ViewModel в DataContext элемента управления.
Я на самом деле считаю WPF еще более удобным для использования с DI. Даже объекты и свойства зависимостей работают с ним без проблем.
Ответ 7
В блоке Glen (см. выше) упоминается, что общий подход заключается в разработке решения MVVM для использования DataContext как места, где вы можете "разрешить" вашу модель просмотра в представлении. Затем вы можете использовать расширения дизайна из выражения blend 2008 (обратите внимание, что вам не нужно использовать средства разработки blend design, чтобы воспользоваться этим). Например:
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=local:MyViewModelMock, IsDesignTimeCreatable=True}"
По вашему мнению, у вас может быть свойство getter, которое отличает ваш DataContext к тому типу, который вы ожидаете (просто чтобы его было проще использовать в коде).
private IMyViewModel ViewModel { get { return (IMyViewModel) DataContext; } }
Не забудьте использовать интерфейс, чтобы ваши представления были легче протестированы или чтобы помочь вам внедрить различные реализации среды выполнения.
В общем, вы не должны разрешать вещи из контейнера повсюду в своем решении. На самом деле считается плохой практикой передавать ваш контейнер в каждом конструкторе или делать его доступным по всему миру. (Вы должны найти обсуждения о том, почему стратегии "Локатор услуг" представляют собой "Анти-шаблон" ).
Создайте конструктор открытого представления с явными зависимостями, которые может разрешить контейнер (например, Prism Unity или MEF).
При необходимости вы также можете создать конструктор по умолчанию внутри, чтобы создать макет вашей модели представления (или, в действительности, такой). Это защищает от непреднамеренного использования этого конструктора конструктора извне (в вашей "оболочке" или где угодно). Ваши тестовые проекты также могут использовать такие конструкторы, используя " InternalsVisibleToAttribute" в " AssemblyInfo". Но, конечно, это обычно не требуется, так как вы можете в любом случае вводить свои издевательства с использованием конструкторов полной зависимости, и потому, что большинство ваших тестов должно быть сосредоточено на ViewModel. Любой код в View в идеале должен быть довольно тривиальным. (Если ваш просмотр требует много тестирования, то вы можете спросить себя, почему!)
Глен также упоминает, что вы можете вводить Просмотры в Модели просмотра или Просмотр моделей в представлениях. Я предпочитаю последнее, потому что есть отличные методы для развязывания всего (использование декларативного привязки, командования, агрегирования событий, шаблонов посредника и т.д.). Модель просмотра - это то, где весь тяжелый подъем будет сделан для организации основной бизнес-логики. Если все необходимые "привязывающие" точки предоставлены View Model, ему не нужно знать НИЧЕГО о Просмотр ( который в основном может быть подключен к нему декларативно в XAML).
Если мы сделаем агностик View Model источником взаимодействия с пользователем, это значительно упростит тест (желательно сначала). А это также означает, что вы можете легко подключить ЛЮБОЙ просмотр (WPF, Silverlight, ASP.NET, консоль и т.д.). Фактически, чтобы обеспечить надлежащую развязку, мы можем спросить себя, может ли архитектура MVM (Model-ViewModel) работать в контексте, скажем, службы Workflow. Когда вы перестанете думать об этом, большинство ваших модульных тестов, вероятно, будут разработаны на этой основе.
Ответ 8
Я думаю, что вам нужно решить сначала или сначала просмотреть модель, а затем дать другой ответ, который может быть решен. Существует несколько фреймворков с открытым исходным кодом. Я использую Caliburn, где сначала выполняется ViewModel, и его действительно хороший подход
Ответ 9
Я написал очень легкую структуру, где ViewModel разрешен во время выполнения с использованием IoC (Unity) в качестве расширения разметки.
Структура позволяет записывать XAML без кода, но все же позволяет вам маршрутизировать команды, привязывать данные и обработчики событий.
В любом случае, я не думаю, что вам нужен свободный XAML в вашем случае, но если вы посмотрите на код (http://xtrememvvm.codeplex.com), может оказаться, что вы можете использовать часть кода для решения своих собственных проблем с помощью инъекции View Models and Services.