Prism学习日记04 module的使用流程

关于Module的概述

  • Module的使用流程
  • 几个重要的接口介绍
    • IModuleInfo接口
    • IModuleCatalog接口
    • IModuleManager接口
    • IModuleInitializer接口

Module的使用流程

在这里插入图片描述
如上图所示,使用模块之前需要:注册模块》发现模块》加载模块》初始化模块

  1. 注册/发现模块:在运行时为特定应用程序加载模块,对应的目录包含要加载的模块,其位置以及加载顺序的信息。
  2. 加载模块:模块的程序集被加载到内存中,需要从某个远程位置或本地目录检索模块。
  3. 初始化模块:创建模块类的实例并通过IModule接口调用它们的Initialize方法。

几个重要的接口介绍

IModuleInfo接口

在这里插入图片描述
可以看到这个接口包含了一些关于module的基本信息,比如:

  1. 依赖项:可以获取被依赖的模块名称
  2. 初始化模式:可以设置或获取初始化的模式,如下
    在这里插入图片描述
  3. 名称:类型:都是对当前module的信息描述
  4. Ref:可以理解为加载路径
  5. 状态,当前module的状态信息,比如:在这里插入图片描述

IModuleCatalog接口

之前也简单提到这个接口,可以理解为module的目录,从源码看就是维护了一个IModuleInfo的集合
在这里插入图片描述
这里比较重要的就是初始化函数,可以先从ModuleCatalogBase类看到,所谓初始化最终是调用了load函数实现,加载module,可以从三个方式进行加载,这里按照prism学习日记3中的顺序详细补充。

  1. 通过通过动态解析xaml文件进行动态加载
    注意学习日记3中的方法一通过直接添加module达到静态加载目的,和本次的方法略有不同,此处是直接通过解析xaml文件达到动态加载的目的,无需添加模块引用。xaml示例如下:
    在这里插入图片描述
    只需要在重写的加载模块中读取该xaml即可:
    在这里插入图片描述
    这里贴一下源码
 /// /// A catalog built from a XAML file./// public class XamlModuleCatalog : ModuleCatalog{private readonly Uri _resourceUri;private const string _refFilePrefix = "file://";private int _refFilePrefixLength = _refFilePrefix.Length;/// /// Creates an instance of a XamlResourceCatalog./// /// The name of the XAML filepublic XamlModuleCatalog(string fileName): this(new Uri(fileName, UriKind.Relative)){}/// /// Creates an instance of a XamlResourceCatalog./// /// The pack url of the XAML file resourcepublic XamlModuleCatalog(Uri resourceUri){_resourceUri = resourceUri;}/// /// Loads the catalog from the XAML file./// protected override void InnerLoad(){var catalog = CreateFromXaml(_resourceUri);foreach (IModuleCatalogItem item in catalog.Items){if (item is ModuleInfo mi){if (!string.IsNullOrWhiteSpace(mi.Ref))mi.Ref = GetFileAbsoluteUri(mi.Ref);}else if (item is ModuleInfoGroup mg){if (!string.IsNullOrWhiteSpace(mg.Ref)){mg.Ref = GetFileAbsoluteUri(mg.Ref);mg.UpdateModulesRef();}else{foreach (var module in mg){module.Ref = GetFileAbsoluteUri(module.Ref);}}}Items.Add(item);}}/// protected override string GetFileAbsoluteUri(string path){//this is to maintain backwards compatibility with the old file:/// and file:// syntax for Xaml module catalog Ref propertyif (path.StartsWith(_refFilePrefix + "/", StringComparison.Ordinal)){path = path.Substring(_refFilePrefixLength + 1);}else if (path.StartsWith(_refFilePrefix, StringComparison.Ordinal)){path = path.Substring(_refFilePrefixLength);}return base.GetFileAbsoluteUri(path);}/// /// Creates a  from XAML./// ///  that contains the XAML declaration of the catalog./// An instance of  built from the XAML.private static ModuleCatalog CreateFromXaml(Stream xamlStream){if (xamlStream == null){throw new ArgumentNullException(nameof(xamlStream));}return XamlReader.Load(xamlStream) as ModuleCatalog;}/// /// Creates a  from a XAML included as an Application Resource./// /// Relative  that identifies the XAML included as an Application Resource./// An instance of  build from the XAML.private static ModuleCatalog CreateFromXaml(Uri builderResourceUri){var streamInfo = System.Windows.Application.GetResourceStream(builderResourceUri);if ((streamInfo != null) && (streamInfo.Stream != null)){return CreateFromXaml(streamInfo.Stream);}return null;}}
  1. 通过文件夹路径加载
    同prism学习日记3中的描述
  2. 通过配置文件加载
    通过一个ConfigurationStore来逐一解析Configuration下面的modules节点并逐一添加到父类中维护的Modules集合,具体步骤同prism学习日记3
    这里贴一下源码
/// /// A catalog built from a configuration file./// public class ConfigurationModuleCatalog : ModuleCatalog{/// /// Builds an instance of ConfigurationModuleCatalog with a  as the default store./// public ConfigurationModuleCatalog(){Store = new ConfigurationStore();}/// /// Gets or sets the store where the configuration is kept./// public IConfigurationStore Store { get; set; }/// /// Loads the catalog from the configuration./// protected override void InnerLoad(){if (Store == null){throw new InvalidOperationException(Resources.ConfigurationStoreCannotBeNull);}EnsureModulesDiscovered();}private void EnsureModulesDiscovered(){ModulesConfigurationSection section = Store.RetrieveModuleConfigurationSection();if (section != null){foreach (ModuleConfigurationElement element in section.Modules){IList<string> dependencies = new List<string>();if (element.Dependencies.Count > 0){foreach (ModuleDependencyConfigurationElement dependency in element.Dependencies){dependencies.Add(dependency.ModuleName);}}ModuleInfo moduleInfo = new ModuleInfo(element.ModuleName, element.ModuleType){Ref = GetFileAbsoluteUri(element.AssemblyFile),InitializationMode = element.StartupLoaded ? InitializationMode.WhenAvailable : InitializationMode.OnDemand};moduleInfo.DependsOn.AddRange(dependencies.ToArray());AddModule(moduleInfo);}}}}

IModuleManager接口

/// /// Defines the interface for the service that will retrieve and initialize the application's modules./// public interface IModuleManager{/// /// Gets all the  classes that are in the ./// IEnumerable<IModuleInfo> Modules { get; }/// /// Initializes the modules marked as  on the ./// void Run();/// /// Loads and initializes the module on the  with the name ./// /// Name of the module requested for initialization.void LoadModule(string moduleName);/// /// Raised repeatedly to provide progress as modules are downloaded./// event EventHandler<ModuleDownloadProgressChangedEventArgs> ModuleDownloadProgressChanged;/// /// Raised when a module is loaded or fails to load./// event EventHandler<LoadModuleCompletedEventArgs> LoadModuleCompleted;}

上述接口定义了

  1. IEnumerable类型的modules属性:用来描述当前IModuleManager包含的modules
  2. run方法:从下面的源码看是先对整个应用程序的module解析到ModuleCatalog中,再将这些module中初始化类型是InitializationMode.WhenAvailable 的moudle进行逐一load操作
  3. LoadModule方法:可以手动加载指定名称的module
  4. LoadModuleCompleted事件:当module加载结束就会触发此事件
  5. ModuleDownloadProgressChanged事件:用来描述当前Module如果是从远程下载的,那么通过此事件能够报告当前Module进行下载的Progress
    modulemanager的实现类代码如下:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Prism.Properties;namespace Prism.Modularity
{/// /// Component responsible for coordinating the modules' type loading and module initialization process./// public partial class ModuleManager : IModuleManager, IDisposable{private readonly IModuleInitializer moduleInitializer;private IEnumerable<IModuleTypeLoader> typeLoaders;private HashSet<IModuleTypeLoader> subscribedToModuleTypeLoaders = new HashSet<IModuleTypeLoader>();/// /// Initializes an instance of the  class./// /// Service used for initialization of modules./// Catalog that enumerates the modules to be loaded and initialized.public ModuleManager(IModuleInitializer moduleInitializer, IModuleCatalog moduleCatalog){this.moduleInitializer = moduleInitializer ?? throw new ArgumentNullException(nameof(moduleInitializer));ModuleCatalog = moduleCatalog ?? throw new ArgumentNullException(nameof(moduleCatalog));}/// /// The module catalog specified in the constructor./// protected IModuleCatalog ModuleCatalog { get; }/// /// Gets all the  classes that are in the ./// public IEnumerable<IModuleInfo> Modules => ModuleCatalog.Modules;/// /// Raised repeatedly to provide progress as modules are loaded in the background./// public event EventHandler<ModuleDownloadProgressChangedEventArgs> ModuleDownloadProgressChanged;private void RaiseModuleDownloadProgressChanged(ModuleDownloadProgressChangedEventArgs e){ModuleDownloadProgressChanged?.Invoke(this, e);}/// /// Raised when a module is loaded or fails to load./// public event EventHandler<LoadModuleCompletedEventArgs> LoadModuleCompleted;private void RaiseLoadModuleCompleted(IModuleInfo moduleInfo, Exception error){this.RaiseLoadModuleCompleted(new LoadModuleCompletedEventArgs(moduleInfo, error));}private void RaiseLoadModuleCompleted(LoadModuleCompletedEventArgs e){this.LoadModuleCompleted?.Invoke(this, e);}/// /// Initializes the modules marked as  on the ./// public void Run(){this.ModuleCatalog.Initialize();this.LoadModulesWhenAvailable();}/// /// Loads and initializes the module on the  with the name ./// /// Name of the module requested for initialization.public void LoadModule(string moduleName){var module = this.ModuleCatalog.Modules.Where(m => m.ModuleName == moduleName);if (module == null || module.Count() != 1){throw new ModuleNotFoundException(moduleName, string.Format(CultureInfo.CurrentCulture, Resources.ModuleNotFound, moduleName));}var modulesToLoad = this.ModuleCatalog.CompleteListWithDependencies(module);this.LoadModuleTypes(modulesToLoad);}/// /// Checks if the module needs to be retrieved before it's initialized./// /// Module that is being checked if needs retrieval./// protected virtual bool ModuleNeedsRetrieval(IModuleInfo moduleInfo){if (moduleInfo == null)throw new ArgumentNullException(nameof(moduleInfo));if (moduleInfo.State == ModuleState.NotStarted){// If we can instantiate the type, that means the module's assembly is already loaded into// the AppDomain and we don't need to retrieve it.bool isAvailable = Type.GetType(moduleInfo.ModuleType) != null;if (isAvailable){moduleInfo.State = ModuleState.ReadyForInitialization;}return !isAvailable;}return false;}private void LoadModulesWhenAvailable(){var whenAvailableModules = this.ModuleCatalog.Modules.Where(m => m.InitializationMode == InitializationMode.WhenAvailable);var modulesToLoadTypes = this.ModuleCatalog.CompleteListWithDependencies(whenAvailableModules);if (modulesToLoadTypes != null){this.LoadModuleTypes(modulesToLoadTypes);}}private void LoadModuleTypes(IEnumerable<IModuleInfo> moduleInfos){if (moduleInfos == null){return;}foreach (var moduleInfo in moduleInfos){if (moduleInfo.State == ModuleState.NotStarted){if (this.ModuleNeedsRetrieval(moduleInfo)){this.BeginRetrievingModule(moduleInfo);}else{moduleInfo.State = ModuleState.ReadyForInitialization;}}}this.LoadModulesThatAreReadyForLoad();}/// /// Loads the modules that are not initialized and have their dependencies loaded./// protected virtual void LoadModulesThatAreReadyForLoad(){bool keepLoading = true;while (keepLoading){keepLoading = false;var availableModules = this.ModuleCatalog.Modules.Where(m => m.State == ModuleState.ReadyForInitialization);foreach (var moduleInfo in availableModules){if ((moduleInfo.State != ModuleState.Initialized) && (this.AreDependenciesLoaded(moduleInfo))){moduleInfo.State = ModuleState.Initializing;this.InitializeModule(moduleInfo);keepLoading = true;break;}}}}private void BeginRetrievingModule(IModuleInfo moduleInfo){var moduleInfoToLoadType = moduleInfo;IModuleTypeLoader moduleTypeLoader = this.GetTypeLoaderForModule(moduleInfoToLoadType);moduleInfoToLoadType.State = ModuleState.LoadingTypes;// Delegate += works differently between SL and WPF.// We only want to subscribe to each instance once.if (!this.subscribedToModuleTypeLoaders.Contains(moduleTypeLoader)){moduleTypeLoader.ModuleDownloadProgressChanged += this.IModuleTypeLoader_ModuleDownloadProgressChanged;moduleTypeLoader.LoadModuleCompleted += this.IModuleTypeLoader_LoadModuleCompleted;this.subscribedToModuleTypeLoaders.Add(moduleTypeLoader);}moduleTypeLoader.LoadModuleType(moduleInfo);}private void IModuleTypeLoader_ModuleDownloadProgressChanged(object sender, ModuleDownloadProgressChangedEventArgs e){this.RaiseModuleDownloadProgressChanged(e);}private void IModuleTypeLoader_LoadModuleCompleted(object sender, LoadModuleCompletedEventArgs e){if (e.Error == null){if ((e.ModuleInfo.State != ModuleState.Initializing) && (e.ModuleInfo.State != ModuleState.Initialized)){e.ModuleInfo.State = ModuleState.ReadyForInitialization;}// This callback may call back on the UI thread, but we are not guaranteeing it.// If you were to add a custom retriever that retrieved in the background, you// would need to consider dispatching to the UI thread.this.LoadModulesThatAreReadyForLoad();}else{this.RaiseLoadModuleCompleted(e);// If the error is not handled then I log it and raise an exception.if (!e.IsErrorHandled){this.HandleModuleTypeLoadingError(e.ModuleInfo, e.Error);}}}/// /// Handles any exception occurred in the module typeloading process,/// and throws a ./// This method can be overridden to provide a different behavior./// /// The module metadata where the error happened./// The exception thrown that is the cause of the current error./// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1")]protected virtual void HandleModuleTypeLoadingError(IModuleInfo moduleInfo, Exception exception){if (moduleInfo == null)throw new ArgumentNullException(nameof(moduleInfo));if (!(exception is ModuleTypeLoadingException moduleTypeLoadingException)){moduleTypeLoadingException = new ModuleTypeLoadingException(moduleInfo.ModuleName, exception.Message, exception);}throw moduleTypeLoadingException;}private bool AreDependenciesLoaded(IModuleInfo moduleInfo){var requiredModules = this.ModuleCatalog.GetDependentModules(moduleInfo);if (requiredModules == null){return true;}int notReadyRequiredModuleCount =requiredModules.Count(requiredModule => requiredModule.State != ModuleState.Initialized);return notReadyRequiredModuleCount == 0;}private IModuleTypeLoader GetTypeLoaderForModule(IModuleInfo moduleInfo){foreach (IModuleTypeLoader typeLoader in this.ModuleTypeLoaders){if (typeLoader.CanLoadModuleType(moduleInfo)){return typeLoader;}}throw new ModuleTypeLoaderNotFoundException(moduleInfo.ModuleName, string.Format(CultureInfo.CurrentCulture, Resources.NoRetrieverCanRetrieveModule, moduleInfo.ModuleName), null);}private void InitializeModule(IModuleInfo moduleInfo){if (moduleInfo.State == ModuleState.Initializing){this.moduleInitializer.Initialize(moduleInfo);moduleInfo.State = ModuleState.Initialized;this.RaiseLoadModuleCompleted(moduleInfo, null);}}#region Implementation of IDisposable/// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources./// /// Calls ./// 2public void Dispose(){this.Dispose(true);GC.SuppressFinalize(this);}/// /// Disposes the associated s./// /// When , it is being called from the Dispose method.protected virtual void Dispose(bool disposing){foreach (IModuleTypeLoader typeLoader in this.ModuleTypeLoaders){if (typeLoader is IDisposable disposableTypeLoader){disposableTypeLoader.Dispose();}}}#endregion}
}

modulemanager在构造函数中包含了两个部分,IModuleInitializer接口和IModuleCatalog接口(上文已介绍过)

IModuleInitializer接口

该接口的定义:

    /// /// Declares a service which initializes the modules into the application./// public interface IModuleInitializer{/// /// Initializes the specified module./// /// The module to initializevoid Initialize(IModuleInfo moduleInfo);}

可以看出是通过传入moduleinfo参数实现对module的初始化
具体实现过程如下:

using System;
using System.Globalization;
using Prism.Ioc;namespace Prism.Modularity
{/// /// Implements the  interface. Handles loading of a module based on a type./// public class ModuleInitializer : IModuleInitializer{private readonly IContainerExtension _containerExtension;/// /// Initializes a new instance of ./// /// The container that will be used to resolve the modules by specifying its type.public ModuleInitializer(IContainerExtension containerExtension){this._containerExtension = containerExtension ?? throw new ArgumentNullException(nameof(containerExtension));}/// /// Initializes the specified module./// /// The module to initialize[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Catches Exception to handle any exception thrown during the initialization process with the HandleModuleInitializationError method.")]public void Initialize(IModuleInfo moduleInfo){if (moduleInfo == null)throw new ArgumentNullException(nameof(moduleInfo));IModule moduleInstance = null;try{moduleInstance = this.CreateModule(moduleInfo);if (moduleInstance != null){moduleInstance.RegisterTypes(_containerExtension);moduleInstance.OnInitialized(_containerExtension);}}catch (Exception ex){this.HandleModuleInitializationError(moduleInfo,moduleInstance?.GetType().Assembly.FullName,ex);}}/// /// Handles any exception occurred in the module Initialization process,/// This method can be overridden to provide a different behavior./// /// The module metadata where the error happened./// The assembly name./// The exception thrown that is the cause of the current error./// public virtual void HandleModuleInitializationError(IModuleInfo moduleInfo, string assemblyName, Exception exception){if (moduleInfo == null)throw new ArgumentNullException(nameof(moduleInfo));if (exception == null)throw new ArgumentNullException(nameof(exception));Exception moduleException;if (exception is ModuleInitializeException){moduleException = exception;}else{if (!string.IsNullOrEmpty(assemblyName)){moduleException = new ModuleInitializeException(moduleInfo.ModuleName, assemblyName, exception.Message, exception);}else{moduleException = new ModuleInitializeException(moduleInfo.ModuleName, exception.Message, exception);}}throw moduleException;}/// /// Uses the container to resolve a new  by specifying its ./// /// The module to create./// A new instance of the module specified by .protected virtual IModule CreateModule(IModuleInfo moduleInfo){if (moduleInfo == null)throw new ArgumentNullException(nameof(moduleInfo));return this.CreateModule(moduleInfo.ModuleType);}/// /// Uses the container to resolve a new  by specifying its ./// /// The type name to resolve. This type must implement ./// A new instance of .protected virtual IModule CreateModule(string typeName){Type moduleType = Type.GetType(typeName);if (moduleType == null){throw new ModuleInitializeException(string.Format(CultureInfo.CurrentCulture, Properties.Resources.FailedToGetType, typeName));}return (IModule)_containerExtension.Resolve(moduleType);}}
}

核心是根据当前moduleinfo构造出一个Imodule对象,使用的时候需要调用imodule中定义的RegisterTypes和OnInitialized方法。如下所示:
在这里插入图片描述


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部