使用独立模块管理器加载Prism模块



我正在开发的WPF应用程序将有许多插件来扩展其功能。每个外接程序将由一个或多个程序集组成(通常是一个"主"外接程序集,具有用于表示层组件和视图的独立程序集),但将被应用程序视为单个Prism模块。Prism将用于发现和加载外接程序,MEF用于确保Prism模块可以访问外接程序相关程序集中的依赖项。Prism模块的Initialize方法将负责配置IoC容器(在本例中是Unity)。在已部署的场景中,这些都将在启动时通过MefBootstrapper加载和管理。

在尝试对外接程序进行单元测试时出现问题。为了使外接程序代码与主应用程序保持半隔离,每个外接程序还将具有自己的单元测试程序集。其中一个测试程序集将负责检查使用IoC容器的服务注册情况。在测试场景中,我不想使用引导程序来加载Prism模块,因为它们具有我不想引入到测试程序集中的依赖项。因此,我已经编写了我的测试夹具基类,这样它就创建了自己的MefModuleManager来加载要测试的模块。

ResolutionTestBase.cs

public abstract class ResolutionTestBase
{
[ClassInitialize]
public static void TestFixtureInitialise(TestContext context)
{
// Create the main resolution container.
var container = new UnityContainer();
// Install the service locator.
var locator = new UnityServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => locator);
}
// Here go some helper methods for performing resolution tests.
protected IUnityContainer Container
{
get { return ServiceLocator.Current.GetService(typeof(IUnityContainer)) as IUnityContainer; }
}
}

AddInResolutionTestBase.cs

public abstract class AddInResolutionTestBase:ResolutionTestBase
{
static AddInResolutionTestBase()
{
Logger = new EmptyLogger();
}
[TestInitialize]
public virtual void TestInitialise()
{
// Create MEF catalog.
var aggregateCatalog = new AggregateCatalog();
foreach (var testAssembly in TestAssemblies)
{
aggregateCatalog.Catalogs.Add(new AssemblyCatalog(testAssembly));
}
// Load module manager.
var container = new CompositionContainer(aggregateCatalog);
var serviceLocator = new MefServiceLocatorAdapter(container);
var parts = new DownloadedPartCatalogCollection();
var moduleInitialiser = new MefModuleInitializer(serviceLocator, Logger, parts, aggregateCatalog);
var moduleManager = new MefModuleManager(moduleInitialiser, ModuleCatalog, Logger);
moduleManager.ModuleTypeLoaders = new[] { new MefFileModuleTypeLoader() };
moduleManager.Run();
}
protected static ILoggerFacade Logger { get; private set; }
protected abstract IModuleCatalog ModuleCatalog { get; }
protected abstract IEnumerable<Assembly> TestAssemblies { get; }
}

对于插件,它们在主程序集中有一个独立的类来实现Prism模块的需求,以及Unity扩展来配置容器。

Module.cs

[ModuleExport("AddInModule", typeof(Module), InitializationMode = InitializationMode.OnDemand)]
public class Module : IModule
{
private readonly IEnumerable<IUnityContainerExtensionConfigurator> _extensions;
[ImportingConstructor]
public Module([ImportMany]IEnumerable<IUnityContainerExtensionConfigurator> extensions)
{
_extensions = extensions;
}
public void Initialize()
{
// Load the dependency injection container.
var container = ServiceLocator.Current.GetService(typeof(IUnityContainer)) as IUnityContainer;
if (container != null)
{
foreach (var extension in _extensions)
{
container.AddExtension((UnityContainerExtension) extension);
}
}
}
}

ContainerInstallerExtension.cs(在外接程序的主程序集中)

[Export(typeof(IUnityContainerExtensionConfigurator))]
public class ContainerInstallerExtension : UnityContainerExtension
{
protected override void Initialize()
{
// perform container configuration here.
}
}

PresentationInstallerExtension.cs(在外接程序的表示程序集中)

[Export(typeof(IUnityContainerExtensionConfigurator))]
public class PresentationInstallerExtension:UnityContainerExtension
{
protected override void Initialize()
{
// perform container configuration here.
}
}

AddInResolutionTest.cs(在外接程序的IoC测试程序集中)

[TestClass]
public class AddInResolutionTest : AddInResolutionTestBase
{
private IEnumerable<Assembly> _testAssemblies;
private IModuleCatalog DoGetModuleCatalog()
{
var moduleInfo = new ModuleInfo("AddInModule", typeof (Module).AssemblyQualifiedName)
{
InitializationMode = InitializationMode.WhenAvailable,
Ref = typeof (Module).Assembly.CodeBase
};
return new ModuleCatalog(new[] {moduleInfo});
}
protected override IModuleCatalog ModuleCatalog
{
get { return DoGetModuleCatalog(); }
}
protected override IEnumerable<Assembly> TestAssemblies
{
get { return _testAssemblies ?? (_testAssemblies = new[] { typeof(ContainerInstallerExtension).Assembly, typeof(PresentationInstallerExtension).Assembly }); }
}
[TestMethod]
public void ResolveSomeService()
{
// perform resolution test here.
}
}

值得注意的是,在分辨率测试装置中,"测试程序集"被链接到带有项目引用的IoC测试程序集,并通过类型直接引用(而不是使用目录扫描编目),因此我可以避免使用构建后事件将程序集复制到公共文件夹进行测试。

当我运行单元测试(按原样)时,我得到一个异常,指示模块管理器未能加载Prism模块:

初始化方法AddInResolutionTest。testinitialize抛出异常。Microsoft.Practices.Prism.Modularity.ModuleTypeLoadingException:为模块AddInModule加载类型失败。

如果在Silverlight应用程序中使用MEF时发生此错误,请确保对MefExtensions程序集的引用的CopyLocal属性在主应用程序/shell中设置为true,在所有其他程序集中设置为false。

错误是:对象引用未设置为对象的实例..——>系统。NullReferenceException:对象引用没有设置为对象的实例。在Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader。LoadModuleType (ModuleInfo ModuleInfo)——内部异常堆栈跟踪结束——在Microsoft.Practices.Prism.Modularity.ModuleManager。HandleModuleTypeLoadingError(ModuleInfo ModuleInfo, Exception Exception)在Microsoft.Practices.Prism.Modularity.ModuleManager。(对象发送方,LoadModuleCompletedEventArgs e)在Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader。RaiseLoadModuleCompleted (LoadModuleCompletedEventArgs e)在Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader。RaiseLoadModuleCompleted(ModuleInfo ModuleInfo, Exception error)在Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader。LoadModuleType (ModuleInfo ModuleInfo)在Microsoft.Practices.Prism.Modularity.ModuleManager。BeginRetrievingModule (ModuleInfo ModuleInfo)在Microsoft.Practices.Prism.Modularity.ModuleManager。LoadModuleTypes (IEnumerable 1 moduleInfos)在Microsoft.Practices.Prism.Modularity.ModuleManager.LoadModulesWhenAvailable ()在Microsoft.Practices.Prism.Modularity.ModuleManager.Run ()在AddInResolutionTestBase.cs: line xx

中的addinresolutiontestbase . testinitialize ()

在调用moduleManager.Run()时,我的代码中没有任何内容是空的,所以我不清楚"真正的"问题是什么。

我已经尝试了各种改变来解决这个问题,包括:

  • 在AddInResolutionTestBase.cs中调用moduleManager.LoadModule()代替moduleManager.Run()
  • 操纵AddInResolutionTest中创建的ModuleInfoState以绕过模块管理器中的问题

我所做的任何其他更改都导致了不同的错误,但仍然表明模块管理器试图加载Prism模块的问题。

是否需要一些额外的步骤来正确配置模块管理器,以便能够以这种方式加载模块,记住单元测试不需要一些通常的开销(例如日志记录器)?

在反编译器的帮助下,我能够找出"缺失的部分"并进行一些更改,以确保所有必需的组件都已注册/安装,以便模块管理器能够初始化模块以进行测试。对于感兴趣的人:

public abstract class AddInResolutionTestBase:ResolutionTestBase
{
private CompositionContainer _container;
private IModuleCatalog _moduleCatalog;
private IEnumerable<object> _testEntities;
private IEnumerable<ModuleInfo> _testModuleInformation;
static AddInResolutionTestBase()
{
Logger = new EmptyLogger();
}
[TestInitialize]
public virtual void TestInitialise()
{
// Create MEF catalog.
AggregateCatalog = CreateAggregateCatalog();
ConfigureAggregateCatalog();
AggregateCatalog = DefaultPrismServiceRegistrar.RegisterRequiredPrismServicesIfMissing(AggregateCatalog);
ConfigureContainer();
// Initialise modules to be tested.
CompositionContainer.GetExportedValue<IModuleManager>().Run();
}
#region Protected Methods
protected virtual void ConfigureAggregateCatalog()
{
var testAssemblies = TestEntities.OfType<Assembly>();
foreach (var testAssembly in testAssemblies)
{
AggregateCatalog.Catalogs.Add(new AssemblyCatalog(testAssembly));
}
if (TestEntities.Any(entity => entity is System.Type))
{
var catalog = new TypeCatalog(TestEntities.OfType<System.Type>());
AggregateCatalog.Catalogs.Add(catalog);
}
}
protected virtual void ConfigureContainer()
{
CompositionContainer.ComposeExportedValue<ILoggerFacade>(Logger);
CompositionContainer.ComposeExportedValue<IModuleCatalog>(ModuleCatalog);
CompositionContainer.ComposeExportedValue<IServiceLocator>(new MefServiceLocatorAdapter(CompositionContainer));
CompositionContainer.ComposeExportedValue<AggregateCatalog>(AggregateCatalog);
}
protected virtual AggregateCatalog CreateAggregateCatalog()
{
return new AggregateCatalog();
}
protected virtual CompositionContainer CreateContainer()
{
return new CompositionContainer(AggregateCatalog);
}
protected virtual IModuleCatalog CreateModuleCatalog()
{
return new ModuleCatalog(TestModuleInformation);
}
protected abstract IEnumerable<object> GetTestEntities();
protected abstract IEnumerable<ModuleInfo> GetTestModuleInformation(); 
#endregion
#region Protected Properties
protected AggregateCatalog AggregateCatalog { get; set; }
protected CompositionContainer CompositionContainer
{
get { return _container ?? (_container = CreateContainer()); }
}
protected static ILoggerFacade Logger { get; private set; }
protected IModuleCatalog ModuleCatalog
{
get { return _moduleCatalog ?? (_moduleCatalog = CreateModuleCatalog()); }
}
protected IEnumerable<object> TestEntities
{
get { return _testEntities ?? (_testEntities = GetTestEntities()); }
}
protected IEnumerable<ModuleInfo> TestModuleInformation
{
get { return _testModuleInformation ?? (_testModuleInformation = GetTestModuleInformation()); }
} 
#endregion
}

这个测试基类现在在一定程度上模拟了应用程序正常启动时引导程序中通常发生的事情。每个外接程序中的(分辨率)测试现在只需要为表示外接程序的Prism模块提供(导出的)容器扩展和模块信息列表(除了实际的分辨率测试之外!)

最新更新