gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 17:41:50 35 4
gpt4 key购买 nike

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

我的问题发生在尝试对加载项进行单元测试时。为了保持插件代码与主应用程序半隔离,每个插件也将有自己的单元测试程序集。其中一个测试程序集将负责检查 IoC 容器的服务注册。在测试场景中,我不想使用 Bootstrap 来加载 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 扩展。

模块.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 测试程序集,并按类型直接引用(而不是使用目录扫描目录),因此我可以避免必须使用构建后事件将程序集复制到公共(public)文件夹以进行测试。

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

Initialization method AddInResolutionTest.TestInitialise threw exception. Microsoft.Practices.Prism.Modularity.ModuleTypeLoadingException: Microsoft.Practices.Prism.Modularity.ModuleTypeLoadingException: Failed to load type for module AddInModule.

If this error occurred when using MEF in a Silverlight application, please ensure that the CopyLocal property of the reference to the MefExtensions assembly is set to true in the main application/shell and false in all other assemblies.

Error was: Object reference not set to an instance of an object.. ---> System.NullReferenceException: Object reference not set to an instance of an object.. at Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.LoadModuleType(ModuleInfo moduleInfo) --- End of inner exception stack trace --- at Microsoft.Practices.Prism.Modularity.ModuleManager.HandleModuleTypeLoadingError(ModuleInfo moduleInfo, Exception exception) at Microsoft.Practices.Prism.Modularity.ModuleManager.IModuleTypeLoader_LoadModuleCompleted(Object sender, LoadModuleCompletedEventArgs e) at Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.RaiseLoadModuleCompleted(LoadModuleCompletedEventArgs e) at Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.RaiseLoadModuleCompleted(ModuleInfo moduleInfo, Exception error) at Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.LoadModuleType(ModuleInfo moduleInfo) at Microsoft.Practices.Prism.Modularity.ModuleManager.BeginRetrievingModule(ModuleInfo moduleInfo) at Microsoft.Practices.Prism.Modularity.ModuleManager.LoadModuleTypes(IEnumerable`1 moduleInfos) at Microsoft.Practices.Prism.Modularity.ModuleManager.LoadModulesWhenAvailable() at Microsoft.Practices.Prism.Modularity.ModuleManager.Run() at AddInResolutionTestBase.TestInitialise() in AddInResolutionTestBase.cs: line xx

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

我尝试了各种更改来解决问题,包括:

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

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

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

最佳答案

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

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
}

这个测试基类现在在某种程度上模仿了应用程序正常启动时 boostrapper 中通常发生的情况。每个插件中的(分辨率)测试现在只需要为代表插件的 Prism 模块提供(导出的)容器扩展和模块信息的列表(除了实际的分辨率测试!)

关于c# - 使用独立模块管理器加载 Prism 模块,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32419307/

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com