В
этой статье мы рассмотрим использование библиотеки Managed
Extensibility Framework (MEF) с
таким замечательным наборов компонентов, как Prism 5. Напишем пример загрузчика, который будет
реализован на основе MefBootstrapper. Загрузчик необходим для того, чтобы инициализировать
различные компоненты и службы Prism. Совсем недавно мной была рассмотрена возможность
использования bootstrapper в статье "Введение в Prism 5. Bootstrapper". В указанной
статье рассмотрен UnityBootstrapper, использующий в качестве контейнера
инициализации IoC контейнер Unity.
Небольшая информация для тех, кто уже работал раньше с Prism 4.1, по поводу загрузчиков UnityBootstrapper и MefBootstrapper. Между версией Prism 4.1 и 5 в документации изменений не произошло, если вы читали перевод документации по Prism 4.1, например, на habrahabr.ru. Изменение только немного затронуло сборки и
пространства имен. Раньше один и второй загрузчик по умолчанию входили в Prism, и можно было выбрать
тот, который нам нужно. Сейчас же данные загрузчики поставляются отдельно, и
скачать их можно через NuGet Packages.
Приведу с habrahabr некоторые ключевые моменты для демонстрации отличия двух этих загрузчиков. По поводу варианта использования одного либо второго загрузчика рекомендую смотреть документацию.
Приведу с habrahabr некоторые ключевые моменты для демонстрации отличия двух этих загрузчиков. По поводу варианта использования одного либо второго загрузчика рекомендую смотреть документацию.
Библиотека
Prism предоставляет два DI контейнера по умолчанию: Unity и MEF. Prism
расширяема, таким образом, вы можете использовать другие контейнеры, написав
небольшое количество кода для их адаптации. И Unity, и MEF обеспечивают
одинаковую основную функциональность, необходимую для внедрения зависимостей,
даже учитывая то, что они работают сильно по-разному. Некоторые из
возможностей, предоставляемые обоими контейнерами:
- Оба позволяют регистрировать типы в контейнере.
- Оба позволяют регистрировать экземпляры в контейнере.
- Оба позволяют принудительно создавать экземпляры зарегистрированных типов.
- Оба внедряют экземпляры зарегистрированных типов в конструкторы.
- Оба внедряют экземпляры зарегистрированных типов в свойства.
- У них обоих есть декларативные атрибуты для управления типами и зависимостями.
- Они оба разрешают зависимости в графе объектов.
Unity предоставляет несколько возможностей,
которых нет в MEF:
- Разрешает конкретные типы без регистрации.
- Разрешает открытые обобщения (Generics).
- Может использовать перехват вызова методов для добавления дополнительной функциональности к целевому объекту (Interception).
MEF предоставляет несколько возможностей,
которых нет в Unity:
- Самостоятельно обнаруживает сборки в каталоге файловой системы.
- Загружает XAP файлы и ищет в них сборки.
- Проводит рекомпозицию свойств и коллекций при обнаружении новых типов.
- Автоматически экспортирует производные типы.
- Поставляется вместе с .NET Framework, начиная с четвёртой версии.
Два этих контейнера работают по-разному. Если ваша работа
в основном состоит из разработки разных расширений под ваш продукт, иными
словами, плагинов, то вам, наверное, больше подойдет загрузчик, который работает
с MEF. Если же для
вам нужно просто связать те компоненты, информация о которых вам известна, то вам подойдет загрузчик, который работает с Unity.
Примечание: загрузчик с помощью MEF работает сам по
себе не слишком быстро. Это происходит из-за специфики работы, которая на него ложится.
Если вам нужно подтянуть нужные плагины, то можете посмотреть в сторону
таких IoC контейнеров, как Autofac (загрузчик AutofacBootstrapper) и StructureMap (загрузчик StructureMapBootstrapper), у которого есть функция Scan, с помощью которой можно сканировать сборки.
Приступим к
созданию нашего WPF-приложения с использованием Prism 5 и загрузчика
MefBootstrapper. Назовем наше приложение MefBootstrapperSample.
Нужно указать .NET Framework 4.5, так как Prism 5 работает, начиная только с версии фреймворка 4.5. Следующим
этапом необходимо установить Prism 5 и Prism.MefExtensions. Сделать это мы можем с помощью NuGet Packages.
Мы будем оперировать терминами Prism. Основное окно программы, которое отображает всю
информацию, называется оболочкой (Shell). Поэтому мы также переименуем наше окно с MainWindow.xaml в Shell.xaml. Затем перейдем в файл Shell.xam.cs и изменим класс MainWindow на Shell.
public partial class Shell : Window
{
public Shell()
{
InitializeComponent();
}
}
Чтобы метод InicializeComponent не подсвечивался красным, необходимо перейти в Shell.xaml и изменить
название класса с
MainWindow на
Shell.
x:Class="MefBootstrapperSample.Shell"
Для пущей уверенности можно выполнить поиск по всему
проекту и выполнить замену для всех названий. Следующим этапом удалим наше
главное окно, чтобы оно не стартовало с App.xaml с помощью метода StartupUri. Удаляем строку с надписью
StartupUri="MainWindow.xaml"
Запуском нашего приложения будет заниматься наш
загрузчик. Чтобы наша оболочка имела приемлемый вид, добавим в нее что-либо.
<Window x:Class="MefBootstrapperSample.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MefBootstrapper example" Height="350" Width="525">
<Grid>
<TextBlock>Hello
MefBootstrapper</TextBlock>
</Grid>
</Window>
Затем добавим непосредственно сам загрузчик нам в проект.
Наследуем
наш класс Boostrapper от класса MefBootstrapper с пространства имен
Microsoft.Practices.Prism.MefExtensions.
public class Bootstrapper : MefBootstrapper
{
protected override DependencyObject CreateShell()
{
throw new NotImplementedException();
}
}
Следующим делом создадим наше окно в методе CreateShell(). Только у нас есть маленькая проблема, которую вы можете увидеть на рисунке.
Тот же загрузчик, который использует IoC контейнер Unity, автоматически
разруливает зависимость и добавляет в проект библиотеку Unity. В
загрузчике MEF нам нужно добавить его вручную, так как он идет в поставке с .NET Framework, плюс
он может обновляться параллельно, так как проект MEF сделали открытым. Список открытых проектов вы
можете посмотреть по ссылке dotnetfoundation.org. Добавляем в наш проект через Add Reference ссылку на System.ComponentModel.Composition.
После того как мы добавим ссылку на MEF, наша функция станет
доступной.
protected override DependencyObject CreateShell()
{
return Container.GetExportedValue<Shell>();
}
Только она не будет работать. Первая причина заключается в том, что для того чтобы MEF знал, что мы хотим подтянуть нашу оболочку Shell, нам
нужно пометить ее атрибутом [Export], иначе MEF не сможет ее найти. Измененный класс Shell приведен ниже.
/// <summary>
///
Interaction logic for MainWindow.xaml
/// </summary>
[Export]
public partial class Shell : Window
{
public Shell()
{
InitializeComponent();
}
}
Единственное, что в нем изменилось, ‒ это то,
что теперь он помечен атрибутом Export. Если вы не знаете принцип работы MEF, рекомендую начать из основ ("Пишем свой плагин на MEF"), чтобы понять, как работает экспорт, импорт
и композиция.
Возвращаемся к нашему загрузчику и добавим отображение нашей оболочки в методе InitializeShell().
Возвращаемся к нашему загрузчику и добавим отображение нашей оболочки в методе InitializeShell().
protected override void
InitializeShell()
{
Application.Current.MainWindow = (Window)Shell;
Application.Current.MainWindow.Show();
}
До этой части наш пример работал практически
так же, как пример, который был приведен ранее для UnityBootstrapper. Пока отличие у нас заключается в методах. В Unity код был такой:
Container.Resolve<Shell>();
А в Mef стал такой:
Container.GetExportedValue<Shell>();
В MEF мы также обязаны помечать атрибутами все те классы, которые хотим
использовать. И теперь еще одно важное отличие MEF: нам необходимо сконфигурировать каталог, чтобы наш загрузчик знал, где что искать. Для этого нужно переопределить метод ConfigureAggregateCatalog().
protected override void
ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));
}
Полный код нашего класса Bootstrapper приведен ниже.
public class Bootstrapper : MefBootstrapper
{
protected override DependencyObject CreateShell()
{
return
Container.GetExportedValue<Shell>();
}
protected override void
InitializeShell()
{
Application.Current.MainWindow = (Window)Shell;
Application.Current.MainWindow.Show();
}
protected override void
ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));
}
}
Теперь осталось использовать наш созданный
класс. Для этого перейдем в класс App.xaml.cs и переопределим метод OnStartup.
protected override void
OnStartup(StartupEventArgs e)
{
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run();
}
Когда мы проделали все эти действия, можем запустить
наш проект и посмотреть, что у нас получилось.
Надеюсь, что использование загрузчика MefBootstrapper не оказалось для вас сложнее, чем
использование UnityBootstrapper. Более сложные примеры основываются только
на вашем умении оперировать MEF для связывания реализации с интерфейсами, не более того. В следующей статье
мы постараемся рассмотреть использование класса EventAggregator.
No comments:
Post a Comment