Модуль дочернего контейнера MEF не инициализируется

#module #prism #mef #containers #hierarchy

#модуль #prism #mef #контейнеры #иерархия

Вопрос:

У меня есть простая иерархия контейнеров с родительским контейнером, который определяется в оболочке MefBootstrapper с использованием каталога каталогов, и дочерними контейнерами, которые создаются из родительского с отдельным каталогом.

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

Однако метод Initialize() для модулей, расположенных в дочерних контейнерах, никогда не вызывается.

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

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

i. Почему инициализация не вызывается для модулей, загруженных в дочерний контейнер ii. Как я могу «инициализировать» инициализацию из экземпляра контейнера (вне контекста модуля?) Можете ли вы выполнить итерацию по сборкам в контейнере и инициализировать инициализацию таким образом???

[из MefBootstrapper в проекте оболочки]

     protected override DependencyObject CreateShell()
    {
        ExportProvider ep = this.Container as ExportProvider;
        this.Container.ComposeExportedValue(ep);
  

[из службы, которая управляет моими сеансами (контейнерами)]

     [ImportingConstructor]
    public SessionService(ExportProvider provider)
    {
  

[конструктор для новых сеансов (контейнеров)]

     private void Init(ComposablePartCatalog catalog, ExportProvider provider, string name, int callId, bool useContextProxy)
    {
        this._Name = name;
        this._CallID = callId;
        this.startTime = DateTime.Now;
        this.appHost = new CompositionContainer(catalog, new ExportProvider[] { provider });
    }
  

=====

Было предложено включить код моего модуля, метод инициализации которого не вызывается (несмотря на то, что он загружен в рассматриваемый контейнер…Я могу даже лениво создать экземпляр модуля, но вызов Initialize() напрямую приводит к сбою операции внедрения в самом методе.

 namespace Module1
{
//, InitializationMode = InitializationMode.OnDemand
[ModuleExport("Module1.ModuleInit", typeof(Module1.ModuleInit))]
public class ModuleInit : IModule
{
    private readonly IRegionManager _regionManager;
    public IServiceLocator _serviceLocator;

    [ImportingConstructor]
    public ModuleInit(IRegionManager regionManager, IServiceLocator serviceLocator)
    {
        _regionManager = regionManager;
        _serviceLocator = serviceLocator;
    }

    #region IModule Members


    public void Initialize()
    {
        // Use View Discovery to automatically display the MasterView when the TopLeft region is displayed.
        _regionManager.RegisterViewWithRegion(RegionNames.TopLeftRegion, () => _serviceLocator.GetInstance<MasterView>());
    }

    #endregion
}
  

}

Комментарии:

1. Можете ли вы показать пример модуля с Initialize методом?

2. Конечно, довольно просто, прямо из проекта шаблона PRISM в VS2010: (см. Редактирование)

Ответ №1:

Я скачал ваш код и посмотрел на него. Я сразу же обнаружил проблему. Загрузчик фактически получает экспорт благодаря каталогу DirectoryCatalog, подобному этому:

 DirectoryCatalog catalog = new DirectoryCatalog(".");
this.AggregateCatalog.Catalogs.Add(catalog);
  

Это означает, что вы получите экспорт из сборок в этом каталоге. Поэтому вам просто нужно скопировать все сборки с экспортируемыми типами в каталог «.», то есть в каталог выполнения (Debug / bin).

Просто скопируйте Module1 и Module2 в каталог bin, и все будет составлено изящно 🙂

На самом деле я обнаружил, что события после сборки, которые должны были копировать модули в каталог bin, не работали. Может быть, потому, что вы что-то переименовали. Поэтому, если вы хотите, чтобы он автоматически копировал сборки после сборки, просто замените фактическое событие после сборки на это:

 copy "$(TargetDir)$(TargetFileName)" "$(TargetDir)......Shellbin$(ConfigurationName)"
  

Комментарии:

1. Еще раз спасибо, что нашли время, чтобы посмотреть на это. Однако, если я помещу Module1 и Module2 в корневой каталог, они будут загружены в начальный каталог и, в свою очередь, будут доступны из корневого CompositionContainer. Моя цель (возможно, плохо управляемая) состояла в том, чтобы разделить Module1 и Module2 только на дочерний CompositionContainer, чтобы корневой CompositionContainer не мог удовлетворять запросы на импорт для этих модулей… только дочерний контейнер мог. Но я начинаю думать, что это просто плохая идея, и что я должен просто загрузить все свои модули в один каталог / контейнер.

2. Да, скорее всего, все, что вы вводите, будет экспортировано в одно место. Если вам нужно инициализировать ваши модули, когда это необходимо, вы можете отложить загрузку, настроив модули как OnDemand.

Ответ №2:

У меня уже была эта проблема много раз, и решить ее действительно просто.

Удалите конструктор из вашего модуля. Модули Prism активируются не так, как для классических экспортируемых типов, поэтому модули не могут использовать ImportingConstructor для импорта необходимых вам сервисов. Вместо этого инициализируйте их с помощью ServiceLocator в методе Initialize.

Это будет работать:

 [ModuleExport("Module1.ModuleInit", typeof(Module1.ModuleInit))]
public class ModuleInit : IModule
{
    private readonly IRegionManager _regionManager;

    public void Initialize()
    {
        _regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();

        _regionManager.RegisterViewWithRegion(RegionNames.TopLeftRegion, () => _serviceLocator.GetInstance<MasterView>());
    }
}
  

Я также думаю, что такое поведение вызывает беспокойство.

Комментарии:

1. Спасибо за предложение, но даже с учетом этих изменений метод Initialize просто не вызывается. Когда (дочерний) контейнер создается вне MefBootstrapper без вызова метода ConfigureContainer, как инициализируются модули в каталоге этого контейнера? Я знаю, что здесь я упускаю что-то довольно очевидное.

2. Что вы подразумеваете под «созданным»? Он состоит внутри контейнера или просто создается?

3. Я написал, созданный в отношении дочерних контейнеров. Они создаются с использованием следующего: this.AppHost = new CompositionContainer(каталог, новый экспортпровайдер[] { поставщик });

4. Если вы хотите инициализировать этот модуль из загрузчика, переопределите метод ConfigureAggregate и вызовите это. Каталог AggregateCatalog. Каталоги. Добавить(новый каталог сборки(typeof(InitModule). Сборка)); Если ваша сборка находится в каталоге, вместо этого используйте каталог DirectoryCatalog . Если вы хотите инициализировать свой модуль вне Booostrapper, затем вызовите ServiceLocator . Текущий. getInstance<ModuleInit>() для создания модуля. Но если это не решит вашу проблему, можете ли вы предоставить пример проекта, чтобы я мог воспроизвести вашу проблему и помочь вам в этом?

5. Ссылка на zip-файл образца: skydrive.live.com/… Посмотрите, сможете ли вы заставить контейнер экземпляра первого сеанса фактически загружать Module1 и Module2 и отображать их.

Ответ №3:

У меня была такая же проблема, когда не вызывался метод инициализации моих модулей ()… Я понял, что я оставил ключевое слово «переопределить» в своем методе инициализации, который был объявлен виртуальным в базовом классе модуля, от которого наследуются все мои модули … добавил «переопределение», и это сработало!