Возможно ли внедрение зависимостей с помощью приложения WPF?

Я хочу начать использовать внедрение зависимостей в своем приложении WPF, в основном для лучшей модульной тестируемости. Мое приложение в основном построено по шаблону M-V-VM. Я ищу Autofac для своего контейнера IoC, но не думаю, что это имеет значение слишком много для этого обсуждения.

Внедрение службы в стартовое окно кажется простым, поскольку я могу создать контейнер и разрешить его в App.xaml.cs.

Я борюсь с тем, как я могу преобразовать модели просмотра и службы в пользовательские элементы управления? Пользовательские элементы управления создаются с помощью разметки XAML, поэтому нет возможности их Resolve().

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

Возможен ли полный IoC с WPF?

[править] - Prism предлагалась, но даже оценка Prism кажется большой инвестицией. Я надеюсь на что-то меньшее.

[править] вот фрагмент кода, где меня остановили

//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>();

person Scott Weinstein    schedule 12.11.2008    source источник
comment
Гленн Блок представил Prism через подкасты и сообщения в блогах - я не думаю, что в его оценку вложены большие средства.   -  person Jedidja    schedule 14.11.2008
comment
Скотт, какие крупные инвестиции вы видите? Вы догадываетесь об этом или действительно смотрели на это? Prism разработан таким образом, что вы можете использовать только те детали, которые вам нужны, без особых обязательств. Я был бы счастлив поговорить с вами об этом в автономном режиме.   -  person Glenn Block    schedule 15.11.2008


Ответы (8)


На самом деле это сделать очень просто. У нас есть примеры этого в Prism, как упоминал Джедиджа. Вы можете ввести ViewModel с View или View с ViewModel. В Prism StockTraderRI вы увидите, что мы вводим представление в модель представления. По сути, происходит то, что View (и интерфейс View) имеет свойство Model. Это свойство реализовано в коде программной части, чтобы установить для DataContext значение, например: this.DataContext = value;. В конструктор ViewModel внедряется View. Затем он устанавливает View.Model = this;, который передается как DataContext.

Вы также можете легко сделать обратное и внедрить ViewModel в View. На самом деле я предпочитаю это, потому что это означает, что ViewModel больше не имеет обратной ссылки на представление вообще. Это означает, что при модульном тестировании ViewModel у вас нет возможности даже Mock. Кроме того, он делает код более чистым, поскольку в конструкторе представления он просто устанавливает DataContext на внедренную модель представления.

Я немного подробнее говорю об этом в видеозаписи беседы о разделенных шаблонах презентации, которую мы с Джереми Миллером сделали на Kaizenconf. Первую часть можно найти здесь, https://vimeo.com/2189854.

person Glenn Block    schedule 15.11.2008
comment
Это полезно - я тоже предпочитаю второй подход, где я могу найти образец кода, который делает это? - person Scott Weinstein; 15.11.2008

Думаю, вы затронули проблему. Элементы управления необходимо внедрять в их родительский элемент, а не создавать декларативно через XAML.

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

Другой альтернативой является вызов глобального статического контейнера из конструктора элемента управления или чего-то подобного. Существует общий шаблон, в котором объявляются два конструктора, один со списком параметров для внедрения конструктора, а другой без параметров, которые делегируют:

// For WPF
public Foo() : this(Global.Container.Resolve&lt;IBar&gt;()) {}

// For the rest of the world
public Foo(IBar bar) { .. }

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

Я даже не наполовину эксперт в WPF, поэтому я ожидаю, что здесь будет здоровая подача даунмода :), но надеюсь, что это поможет. Группа Autofac (ссылка на которую находится на домашней странице) может быть еще одним местом, где можно задать этот вопрос. Примеры приложений Prism или MEF (которые включают некоторые примеры WPF) должны дать вам представление о том, что возможно.

person user30493    schedule 22.11.2008

У нас возникла аналогичная проблема. Мы с нетерпением ждем решения, которое обеспечит поддержку времени разработки в Expression Blend 2.0 (Strong Type). Кроме того, мы с нетерпением ждем решения, чтобы в разделе Expression Blend был доступен образец данных Mock + Auto-Generated.

Конечно, мы также хотим, чтобы все это работало с использованием шаблона IOC.

Paul Stovell как интересная статья для начала: http://www.paulstovell.com/blog/wpf-dependency-injection-in-xaml

Поэтому я пробую пару вещей, чтобы добавить более ценную поддержку во время разработки для привязки и имитационного объекта во время разработки, прямо сейчас у меня большая часть моей проблемы связана с получением строго типизированного соединения между представлением (кодом) и ModelView ( Xaml), я попробовал пару сценариев:

Решение 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 внутри - это поверхность дизайна, но у Xaml есть некоторые, хорошо работающие во время выполнения, но не при проектировании;

Решение 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. Наследовать ObjectDataProvider

<CWD:ServiceObjectDataProvider ObjectType="{x:Type CP:IFooView}" />
<!-- Cannot inherit from ObjectDataProvider to achive the right behavior everything is private-->

Решение 4. Создайте макет ObjectDataProvider с нуля до самого задания.

<CWD:ServiceObjectDataProvider ObjectType="{x:Type CP:IFooView }" />
<!-- Not working in Blend, quite obvious-->

Решение 5. Создайте расширение разметки (Пол Стовелл)

<CWM:ServiceMarkup MetaView="{x:Type CP:IFooView}"/>
<!-- Not working in Blend -->

Просто чтобы прояснить один момент. Когда я сказал «не работает в режиме наложения», я имел в виду, что диалоговое окно «Связывание» нельзя использовать и разработчик должен сам написать XAML от руки.

Нашим следующим шагом, вероятно, будет время, чтобы оценить возможность создания подключаемого модуля для Expression Blend.

person Community    schedule 13.11.2008
comment
это видео дает хороший способ использовать MV-VM и получить интеграцию deginer / blend - пропустите последние 10-20 минут, чтобы узнать эти подробности lab49.com/files/videos/Jason%20Dolinger%20MVVM.wmv - person Scott Weinstein; 13.11.2008

Да, мы делаем это постоянно. Вы можете «вставить» вашу ViewModel в DataContext элемента управления.

На самом деле я считаю, что WPF еще проще использовать с DI. Даже объекты и свойства зависимостей работают с ним без проблем.

person Justin Bozonier    schedule 12.11.2008

Вам следует взглянуть на Caliburn - это простая структура WPF / Silverlight MVC с поддержкой полного DI. Это выглядит действительно круто и позволяет использовать любой контейнер IoC, который вы хотите. Есть несколько примеров в вики-документации по

person mookid8000    schedule 03.12.2008

Глен Блок (см. Выше) упоминает, что общий подход заключается в разработке решения MVVM с использованием DataContext в качестве места, где вы можете «разрешить» свою модель представления в представлении. . Затем вы можете использовать расширения дизайна из Expression Blend 2008 (обратите внимание, что вам не нужно использовать инструменты проектирования Expression Blend, чтобы воспользоваться этим). Например:

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}"

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

private IMyViewModel ViewModel { get { return (IMyViewModel) DataContext; } }

Не забудьте использовать интерфейс, чтобы ваши представления было легче тестировать или чтобы помочь вам внедрить различные реализации среды выполнения.

В общем, вы не должны разрешать вещи из контейнера повсюду в вашем решении. На самом деле считается плохой практикой передавать свой контейнер в каждый конструктор или делать его глобально доступным. (Вы должны найти обсуждение того, почему стратегии "Service Locator" представляют собой "Anti-Pattern").

Создайте конструктор общедоступного представления с явными зависимостями, которые может разрешить контейнер (например, Prism Unity или MEF).

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

Глен также упоминает, что вы можете вставлять представления в модели представления или модели представления в представления. Я предпочитаю последнее, потому что есть совершенно хорошие методы для разделения всего (использование декларативного связывания, команд, агрегации событий, шаблонов посредника и т. Д.). Модель просмотра - это то место, где будет выполняться вся тяжелая работа по координации основной бизнес-логики. Если все необходимые "привязки" предусмотрены моделью представления, ей действительно не нужно знать НИЧЕГО о представлении ( который в большинстве случаев можно подключить к нему декларативно в XAML).

Если мы сделаем модель представления независимой от источника взаимодействия с пользователем, это значительно упростит тестирование (желательно сначала). И это также означает, что вы можете легко подключить ЛЮБОЕ представление (WPF, Silverlight, ASP.NET, консоль и т. Д.). Фактически, чтобы гарантировать, что соответствующее разделение было достигнуто, мы можем спросить себя, может ли архитектура «MVM» (Модель-ViewModel) работать в контексте, скажем, службы рабочего процесса. Если задуматься, большинство ваших модульных тестов, вероятно, будут построены на этой предпосылке.

person Ben Stabile    schedule 29.10.2013

Я думаю, что вы должны сначала выбрать View или Viewmodel, а затем, учитывая другой ответ, это можно решить ... Есть несколько фреймворков с открытым исходным кодом, которые делают то же самое. Я использую Caliburn, где сначала используется ViewModel, и это действительно хороший подход

person Community    schedule 14.03.2011
comment
Согласованный. Когда я писал вопрос, моим неявным предположением было сначала «Просмотр». - person Scott Weinstein; 15.03.2011

Я написал очень легкую структуру, в которой ViewModel разрешается во время выполнения с использованием IoC (Unity) в качестве расширения разметки.

Платформа позволяет писать XAML без кода, но по-прежнему позволяет иметь перенаправленные команды, привязку данных и обработчики событий.

В любом случае, я не думаю, что вам нужен свободный XAML в вашем случае, но если вы посмотрите на код (http://xtrememvvm.codeplex.com), может оказаться, что вы можете использовать часть кода для решения ваших собственных проблем с внедрением моделей и служб представления.

person Clay Ver Valen    schedule 07.03.2013
comment
Пожалуйста, не публикуйте ответы, которые просто публикуют ваш код. Ответьте на вопрос здесь (с помощью фрагмента кода) и дайте ссылку на свой код (четко обозначенный как таковой) в качестве ссылки. - person ChrisF; 07.03.2013