4: 模块化应用程序开发 Modular Application Development Using Prism Library 5.0 for WPF (英汉对照版)
A modular application is an application that is divided into a set of loosely coupled functional units (named modules) that can be integrated into a larger application. A client module encapsulates a portion of the application's overall functionality and typically represents a set of related concerns. It can include a collection of related components, such as application features, including user interface and business logic, or pieces of application infrastructure, such as application-level services for logging or authenticating users. Modules are independent of one another but can communicate with each other in a loosely coupled fashion. Using a modular application design makes it easier for you to develop, test, deploy, and maintain your application.
模块化应用程序是指由松耦合的功能单元(模块)集成在一起的大型应用。一个客户端模块封装了程序的一部分功能和相关问题。模块可以使一些相关组件的集合,例如程序功能,包括界面和业务逻辑,或是程序基础架构,例如日志或是授权用户等程序级别的服务。模块之间互相独立又可以松耦合通信。使用模块化应用程序设计是开发测试,部署和维护你的应用程序更加简单。
For example, consider a personal banking application. The user can access a variety of functions, such as transferring money between accounts, paying bills, and updating personal information from a single user interface (UI). However, behind the scenes, each of these functions is encapsulated within a discrete module. These modules communicate with each other and with back-end systems such as database servers and web services. Application services integrate the various components within each of the different modules and handle the communication with the user. The user sees an integrated view that looks like a single application.
举个例子,一个私人银行程序。用户可以访问多种功能,例如转账,支付账单,并更新个人信息。然而,场景背后,每个功能都封装到一个单独的模块。这些模块互相交换,或是和后台系统交互,例如数据服务和web服务。程序服务集成多不同模块中的各种组件,用以处理和用户的交流。用户看到的视图看起来就是一个程序。
The following illustration shows a design of a modular application with multiple modules.
下图说明了多模块的模块化应用程序设计。
Benefits of Building Modular Applications 构建模块化应用程序的好处
You are probably already building a well-architected application using assemblies, interfaces, and classes, and employing good object-oriented design principles. Even so, unless great care is taken, your application design may still be "monolithic" (where all the functionality is implemented in a tightly coupled way within the application), which can make the application difficult to develop, test, extend, and maintain.
你可能已经构建一个架构良好的程序,使用程序集,接口,和类,并采用良好的面向对象设计原则。即便如此,除非你非常小心,你的程序设计仍然是“铁板一块”(所有的功能都是以一个紧耦合的方式实现的),很难再开发,测试,扩展,维护。
The modular application approach, on the other hand, can help you to identify the large scale functional areas of your application and allow you to develop and test that functionality independently. This can make development and testing easier, but it can also make your application more flexible and easier to extend in the future. The benefit of the modular approach is that it can make your overall application architecture more flexible and maintainable because it allows you to break your application into manageable pieces. Each piece encapsulates specific functionality, and each piece is integrated through clear but loosely coupled communication channels.
模块化程序方法,另一方面,可以帮助你确定程序的大型功能区域,可以单独的开发和测试它。这让开发和测试更简单,也让程序更灵活和易于扩展。模块化方法可以使你整个应用程序架构更灵活和易维护,因为他允许把你的程序分块管理。每块都封装了特殊功能,并且能够清晰的集成,松耦合通信。
Prism's Support for Modular Application Development Prism对模块化应用程序开发的支持
Prism provides support for modular application development and for run-time module management within your application. Using Prism's modular development functionality can save you time because you don't have to implement and test your own modularity framework. Prism supports the following modular application development features:
Prism提供模块化应用程序开发支持,并且可以在运行时管理模块。使用Prism的模块化开发功能可以解决你的时间,因为你不需要实现你自己的模块框架。Prism支持以下模块化应用程序开发特性:
- A module catalog for registering named modules and each module's location; you can create the module catalog in the following ways:
- By defining modules in code or Extensible Application Markup Language (XAML)
- By discovering modules in a directory so you can load all your modules without explicitly defining in a centralized catalog
- By defining modules in a configuration file
- Declarative metadata attributes for modules to support initialization mode and dependencies
- Integration with dependency injection containers to support loose coupling between modules
- For module loading:
- Dependency management, including duplicate and cycle detection to ensure modules are loaded in the correct order and only loaded and initialized once
- On-demand and background downloading of modules to minimize application start-up time; the rest of the modules can be loaded and initialized in the background or when they are required
- 一个模块目录记录了命名的模块和每个模块的位置;你可以用以下方式创建模块目录:
- 用代码或XAML定义模块目录
- 通过发现一个路径来加载其路径下的所有模块,这样就不需要定义明确的模块目录了。
- 通过一个配置文件定义模块目录
- 为模块声明元数据以支持初始化模式和依赖项。
- 集成依赖注入容器以支持模块间松耦合
- 模块加载:
- 依赖项管理,包括重复和循环检测,确保模块按正确的顺序加载,且只加载和初始化一次
- 点播和后台下载模块,以减少应用程序的启动时间;模块可以在后台加载和初始化,或者当需要它们是加载。
Core Concepts 核心概念
This section introduces the core concepts related to modularity in Prism, including the IModule interface, the module loading process, the module catalog, communicating between modules, and dependency injection containers.
此节结束Prism模块化的核心概念,包括IModule 接口,模块加载过程,模块目录,模块间的通信,依赖注入容器。
IModule: The Building Block of Modular Applications IModule:模块化应用程序的构建块
A module is a logical collection of functionality and resources that is packaged in a way that can be separately developed, tested, deployed, and integrated into an application. A package can be one or more assemblies. Each module has a central class that is responsible for initializing the module and integrating its functionality into the application. That class implements the IModule interface.
一个模块是一个功能和资源的逻辑集合,可以单独开发测试,部署,和集成到程序里(车轱辘话)。一个包可以使一个或多个程序集。每个模块有一个中央类,它的职责是初始化模块并集成它的功能到应用程序里。这个类需实现IModule 接口。
Note: The presence of a class that implements the IModule interface is enough to identify the package as a module.
注意:有一个实现IModule 接口的类就足以说明这个包可以作为一个模块。
The IModule interface has a single method, named Initialize, within which you can implement whatever logic is required to initialize and integrate the module's functionality into the application. Depending on the purpose of the module, it can register views into composite user interfaces, make additional services available to the application, or extend the application's functionality. The following code shows the minimum implementation for a module.
IModule 接口一个单一方法,叫做Initialize,你可以在其中实现以下需要的初始化和集成模块功能到程序的逻辑。取决于模块目的,它可以注册视图到组合式界面,添加额外的服务到程序,或是扩展程序功能,下面代码展示最基本的模块实现。
public class MyModule : IModule { public void Initialize() { // Do something here. } }
Note:注意: |
---|
Instead of using the initialization mechanism provided by the IModule interface, the Stock Trader RI uses a declarative, attribute-based approach for registering views, services, and types. 处理使用IModule 接口初始化模块,股票操盘程序使用了一个声明的,基于特性的方法来注册视图,服务和类型。 |
Module Lifetime 模块生命周期
The module loading process in Prism includes the following:
Prism中模块加载过程如下:
- Registering/discovering modules. The modules to be loaded at run-time for a particular application are defined in a Module catalog. The catalog contains information about the modules to be loaded, their location, and the order in which they are to be loaded.
- Loading modules. The assemblies that contain the modules are loaded into memory. This phase may require the module to be retrieved from some remote location or local directory.
- Initializing modules. The modules are then initialized. This means creating instances of the module class and calling the Initialize method on them via the IModule interface.
- 注册/发现模块. 哪些模块被加载到程序,是定义在一个模块目录里的。木块目录包含的信息有模块加载方式,模块的位置,模块的加载顺序。
- 加载模块. 将包含模块的程序集加载到内存。模块可能从远程位置或本地路径获取。
- 初始化模块.接下来该模块初始化了.这意味着创建模块的实例并调用使用了IModule 接口的类中的Initialize 方法。
The following figure shows the module loading process.
下图展示模块加载过程。
Module Catalog 模块目录
The ModuleCatalog holds information about the modules that can be used by the application. The catalog is essentially a collection of ModuleInfo classes. Each module is described in a ModuleInfo class that records the name, type, and location, among other attributes of the module. There are several typical approaches to filling the ModuleCatalog with ModuleInfo instances:
ModuleCatalog 保持着模块的相关信息。目录基本上就是一个ModuleInfo 的集合类。每个模块都在其中记录名称,类型,位置,以及模块中的其它属性。这有一些通常的添加ModuleInfo 实例到ModuleCatalog 中的方法:
- Registering modules in code 代码注册模块
- Registering modules in XAML XAML注册模块
- Registering modules in a configuration file 配置文件注册模块
- Discovering modules in a local directory on disk 在磁盘上某一路径发现模块
The registration and discovery mechanism you should use depends on what your application needs. Using a configuration file or XAML file allows your application to not require references to the modules. Using a directory can allow an application to discover modules without having to specify them in a file.
根据我们的需求来选取不同的注册及发现模块机制。使用配置文件或XAML文件允许你的程序不引用模块,使用一个路径可以允许你的程序不用配置它们。
Controlling When to Load a Module 加载模块时控制
Prism applications can initialize modules as soon as possible, known as "when available," or when the application needs them, known as "on-demand." Consider the following guidelines for loading modules:
Prism程序可以尽快初始化模块,叫做“可用时”,或者当程序需要它们是,叫做“点播”。考虑以下方式加载模块:
- Modules required for the application to run must be loaded with the application and initialized when the application runs.
- Modules containing features that are almost always used in typical usage of the application can be loaded in the background and initialized when they become available.
- Modules containing features that are rarely used (or are support modules that other modules optionally depend upon) can be loaded and initialized on-demand.
Consider how you are partitioning your application, common usage scenarios, application start-up time, and the number and size of downloads to determine how to configure your module for downloading and initialization.
考虑你是如何分割你的应用程序,通用用法方案,程序启动时,下载的数量和大小,以确定如何配置模块用于下载和初始化。
Integrate Modules with the Application 集成模块到应用程序里
Prism provides the following classes to bootstrap your application: the UnityBootstrapper or the MefBootstrapper. These classes can be used to create and configure the module manager to discover and load modules. You can override a configuration method to register modules specified in a XAML file, a configuration file, or a directory location in a few lines of code.
Prism提供的引导类: UnityBootstrapper 或 MefBootstrapper. 这些类可以使用创建和配置模块管理器去发现和加载模块。你可以重写配置方法去注册模块,使用一个XAML文件,一个配置文件,或是一个本地路径。
Use the module Initialize method to integrate the module with the rest of the application. The way you do this varies, depending on the structure of your application and the content of the module. The following are common things to do to integrate your module into your application:
使用模块Initialize 方法去集成模块。你可以用多种方式初始化模块内容,具体取决于您的应用程序的结构和模块的内容。
- Add the module's views to the application's navigation structure. This is common when building composite UI applications using view discovery or view injection.
- Subscribe to application level events or services.
- Register shared services with the application's dependency injection container.
Communicate Between Modules 模块间的通信
Even though modules should have low coupling between each other, it is common for modules to communicate with each other. There are several loosely coupled communication patterns, each with their own strengths. Typically, combinations of these patterns are used to create the resulting solution. The following are some of these patterns:
即使模块应该有相互之间的低耦合,模块间相互通信很常见。这有一些松耦合的通信方式,各有好处。通常,可以组合他们作为解决方法。下面就是这些方式:
- Loosely coupled events. A module can broadcast that a certain event has occurred. Other modules can subscribe to those events so they will be notified when the event occurs. Loosely coupled events are a lightweight manner of setting up communication between two modules; therefore, they are easily implemented. However, a design that relies too heavily on events can become hard to maintain, especially if many events have to be orchestrated together to fulfill a single task. In that case, it might be better to consider a shared service.
- Shared services. A shared service is a class that can be accessed through a common interface. Typically, shared services are found in a shared assembly and provide system-wide services, such as authentication, logging, or configuration.
- Shared resources. If you do not want modules to directly communicate with each other, you can also have them communicate indirectly through a shared resource, such as a database or a set of web services.
Dependency Injection and Modular Applications 依赖注入和模块化应用程序
Containers like the Unity Application Block (Unity) and Managed Extensibility Framework (MEF) allow you to easily use Inversion of Control (IoC) and Dependency Injection, which are powerful design patterns that help to compose components in a loosely-coupled fashion. It allows components to obtain references to the other components that they depend on without having to hard code those references, thereby promoting better code re-use and improved flexibility. Dependency injection is very useful when building a loosely coupled, modular application. Prism is designed to be agnostic about the dependency injection container used to compose components within an application. The choice of container is up to you and will largely depend on your application requirements and preferences. However, there are two principal dependency injection frameworks from Microsoft to consider – Unity and MEF.
像Unity和MEF这样的容器允许你轻松使用控制反转和依赖注入,这是一种非常强大的设计模式,帮助你组合组件以一种松耦合的方式。它允许组件获取其他组件的引用。而不用硬编码这些引用。从而促进更好的代码重用和提高灵活性。依赖注入是非常有用的,当构建松耦合,模块化应用程序。Prism被设计和依赖注入容器组合组件到程序不相关。容器的选择权在你,这很大程度上取决于你的应用需求和喜好。然而,有来自微软的两个主要依赖注入框架值得考虑——Unity和MEF。
The patterns & practices Unity Application Block provides a fully-featured dependency injection container. It supports property-based and constructor-based injection and policy injection, which allows you to transparently inject behavior and policy between components; it also supports a host of other features that are typical of dependency injection containers.
模式和实践的Untiy应用程序块提供了一个全功能的依赖注入容器。它支持基于属性和基于构造函数注入和策略注入,它允许你透明地注入组件之间的行为和政策;它也支持许多其他功能,是典型的依赖注入容器。
MEF (which is part of .NET Framework 4.5) provides support for building extensible .NET applications by supporting dependency injection–based component composition and provides other features that support modular application development. It allows an application to discover components at run time and then to integrate those components into the application in a loosely-coupled way. MEF is a great extensibility and composition framework. It includes assembly and type discovery, type dependency resolution, dependency injection, and some nice assembly download capabilities. Prism supports taking advantage of MEF features, as well as the following:
MEF(已经是.NET Framework 4.5的一部分了)通过支持依赖注入构建可扩展的.NET应用程序——基于组件组成,并提供了支持模块化的应用程序开发其他功能。它允许一个应用程序在运行时发现组件然后以一种松耦合的方式集成这些组件到程序里。MEF是一个伟大的可扩展性和组合框架。它包括程序集和类型发现,类型依赖解析,依赖注入,和一些程序集下载功能,Prism支持MEF特性的这些优势,如下:
- Module registration through XAML and code attributes 模块也通过XAML和代码特性注册
- Module registration through configuration files and directory scans 模块可以通过配置文件和路径浏览注册
- State tracking as the module is loaded 模块加载时进行状态追踪
- Custom declarative metadata for modules when using MEF 使用MEF时,模块声明自定义元数据
Both the Unity and MEF dependency injection containers work seamlessly with Prism.
Untiy和MEF依赖注入容器都可以和Prism很好的一同工作。
Key Decisions 关键决定
The first decision you will make is whether you want to develop a modular solution. There are numerous benefits of building modular applications as discussed in the previous section, but there is a commitment in terms of time and effort that you need to make to reap these benefits. If you decide to develop a modular solution, there are several more things to consider:
第一个决定是你是否想开发一个模块化解决方案。构建模块化的应用程序如前一节中所讨论的有许多好处,但得在时间允许和你真的需要这些好处。如果你决定开发一个模块化解决方案,还有些事要考虑:
- Determine the framework you will use. You can create your own modularity framework, use Prism, MEF, or another framework.
- Determine how to organize your solution. Approach a modular architecture by defining the boundaries of each module, including what assemblies are part of each module. You can decide to use modularity to ease the development, as well as to have control over how the application will be deployed or if it will support a plug-in or extensible architecture.
- Determine how to partition your modules. Modules can be partitioned differently based on requirements, for example, by functional areas, provider modules, development teams and deployment requirements.
- Determine the core services that the application will provide to all modulesAn example is that core services could be an error reporting service or an authentication and authorization service.
- If you are using Prism, determine what approach you are using to register modules in the module catalog. For WPF, you can register modules in code, XAML, in a configuration file, or discovering modules in a local directory on disk. Determine your module communication and dependency strategy. Modules will need to communicate with each other, and you will need to deal with dependencies between modules.
- Determine your dependency injection container. Typically, modular systems require dependency injection, inversion of control, or service locator to allow the loose coupling and dynamic loading and creating of modules. Prism allows a choice between using the Unity, MEF, or another container and provides libraries for Unity or MEF-based applications.
- Minimize application startup time. Think about on-demand and background downloading of modules to minimize application startup time.
- Determine deployment requirements. You will need to think about how you intend to deploy your application.
The next sections provide details about some of these decisions.
下节提供了这些决定更详细的讨论。
Partition Your Application into Modules 分割你的程序到模块里
When you develop your application in a modularized fashion, you structure the application into separate client modules that can be individually developed, tested, and deployed. Each module will encapsulate a portion of your application's overall functionality. One of the first design decisions you will have to make is to decide how to partition your application's functionality into discrete modules.
当你用模块化方式开发你的应用程序,你构建的应用程序分离到各个客户端模块,可以独立开发,测试和部署。每个模块将封装的应用程序的整体功能的一部分。你首先要考虑的是怎样分离你的功能到各个模块。
A module should encapsulate a set of related concerns and have a distinct set of responsibilities. A module can represent a vertical slice of the application or a horizontal service layer. Large applications will likely have both types of modules.
一个模块应该封装一组相关关注点和又一堆相应职责。一个模块可以使纵向服务或是横向服务。大型应用程序两种服务都有。
An application with modules organized around vertical slices 一个用垂直服务组织的应用程序
A larger application may have modules organized with vertical slices and horizontal layers. Some examples of modules include the following:
一个大型应用可以由垂直服务和横向层组成。模块的一些例子包括以下内容:
- A module that contains a specific application feature, such as the News module in the Stock Trader Reference Implementation (Stock Trader RI)
- A module that contains a specific sub-system or functionality for a set of related use cases, such as purchasing, invoicing, or general ledger
- A module that contains infrastructure services, such as logging, caching, and authorization services, or web services
- A module that contains services that invoke line-of-business (LOB) systems, such as Siebel CRM and SAP, in addition to other internal systems
A module should have a minimal set of dependencies on other modules. When a module has a dependency on another module, it should be loosely coupled by using interfaces defined in a shared library instead of concrete types, or by using the EventAggregator to communicate with other modules via EventAggregator event types.
一个模块应该对其它模块的依赖尽量小。当一个模块要依赖其它模块时,应该有个松耦合的接口定义,取代具体类型。或是通过EventAggregator 来联系其他模块。
The goal of modularity is to partition the application in such a way that it remains flexible, maintainable, and stable even as features and technologies are added and removed. The best way to accomplish this is to design your application so that modules are as independent as possible, have well defined interfaces, and are as isolated as possible.
模块化的目标是分割程序,使其灵活,易维护,稳定,功能和技术可以自由添加删除。最好的方式来达成这些是设计你的模块尽量独立,有良好的接口设计,尽可能的分离。
Determine Ratio of Projects to Modules 确定项目模块分布
There are several ways to create and package modules. The recommended and most common way is to create a single assembly per module. This helps keep logical modules separate and promotes proper encapsulation. It also makes it easier to talk about the assembly as the module boundary as well as the packaging of how you deploy the module. However, nothing prevents a single assembly from containing multiple modules, and in some cases this may be preferred to minimize the number of projects in your solution. For a large application, it is not uncommon to have 10–50 modules. Separating each module into its own project adds a lot of complexity to the solution and can slow down Visual Studio performance. Sometimes it makes sense to break a module or set of modules into their own solution to manage this if you choose to stick to one module per assembly/Visual Studio project.
这有一些方式创建模块包。最常用的方式是为模块创建一个单一程序集。这有助于保持逻辑模块分开,促进适当封装。这样一更容易进行部署模块。然而,程序集也可以包含多个模块,一些情况下需要解决方案中的项目少些。对于一个大型项目,可能有10到50个模块,把每个模块都分离到单独的程序集会影响Visual Studio的表现。一些时候可以让一些模块由自己的解决方案以方便管理。
Use Dependency Injection for Loose Coupling 使用依赖注入实现松耦合
A module may depend on components and services provided by the host application or by other modules. Prism supports the ability to register dependencies between modules so that they are loaded and initialized in the right order. Prism also supports the initialization of modules when they are loaded into the application. During module initialization, the module can retrieve references to the additional components and services it requires, and/or register any components and services that it contains in order to make them available to other modules.
一个模块可能依赖于由宿主应用程序或由其他模块提供的组件和服务。Prism支持模块之间的依赖关系登记的能力以便于它可以以正确的方式加载和初始化。Prism还支持模块当它们被加载到应用程序时进行初始化。在模块初始化期间,模块可以检查其它需要引用其他组件和服务,注册它包含的任何组件和服务,为了他们对模块有用。
A module should use an independent mechanism to get instances of external interfaces instead of directly instantiating a concrete type, for example by using a dependency injection container or factory service. Dependency injection containers such as Unity or MEF allow a type to automatically acquire instances of the interfaces and types it needs through dependency injection. Prism integrates with both Unity and MEF to allow a module to easily use dependency injection.
一个模块应该使用独立的机制来获得外部接口实例取代直接实例化一个具体的类型,例如通过使用依赖注入容器或工厂服务。例如Untiy或MEF的依赖注入容器允许自动获取它需要通过依赖注入的接口和类型的实例。Prism集成Unity和MEF去允许模块更简单的使用依赖注入。
The following diagram shows the typical sequence of operations when modules are loaded that need to acquire or register references to the components and services.
下图展示当模块被加载时的操作顺序,加载需要或注册的组件及服务引用。
In this example, the OrdersModule assembly defines an OrdersRepository class (along with other views and classes that implement order functionality). The CustomerModule assembly defines aCustomersViewModel class which depends on the OrdersRepository, typically based on an interface exposed by the service. The application startup and bootstrapping process contains the following steps:
在这个示例中,OrdersModule 程序集定义一个OrdersRepository 类(还有其他视图和实现订单的功能的类)。CustomerModule 程序集定义了一个CustomersViewModel 类,此类依赖OrdersRepository, 通常是用基于一个接口公开的服务。程序启动和引导过程包括以下步骤:
- The bootstrapper starts the module initialization process, and the module loader loads and initializes the OrdersModule.
- In the initialization of the OrdersModule, it registers the OrdersRepository with the container.
- The module loader then loads the CustomersModule. The order of module loading can be specified by the dependencies in the module metadata.
- The CustomersModule constructs an instance of the CustomerViewModel by resolving it through the container. The CustomerViewModel has a dependency on the OrdersRepository (typically based on its interface) and indicates it through constructor or property injection. The container injects that dependency in the construction of the view model based on the type registered by the OrdersModule. The net result is an interface reference from the CustomerViewModel to the OrderRepository without tight coupling between those classes.
-
Note:注意 The interface used to expose the OrderRespository (IOrderRepository) could reside in a separate "shared services" assembly or an "orders services" assembly that only contains the service interfaces and types required to expose those services. This way, there is no hard dependency between the CustomersModule and the OrdersModule.
用于公共OrderRespository功能的IOrderRepository接口可以单独放置一个“共享服务”的程序集或一个“订单服务”程序集,此程序集可以只包含服务接口。这样就是实现CustomersModule和OrdersModule松耦合。Note that both modules have an implicit dependency on the dependency injection container. This dependency is injected during module construction in the module loader.
- 引导器开始模块初始化过程,模块加载器加载并初始化OrdersModule.
- 在OrdersModule 初始化里,它注册OrdersRepository 到容器里。
- 然后模块加载器加载CustomersModule。加载模块的顺序可以通过在模块的元数据的依赖关系来指定。
- CustomersModule 会构造一个CustomerViewModel 实例。是通过容器解析它。CustomerViewModel 有一个OrdersRepository 依赖并通过构造器或属性注入表明它。在视图模型发生的容器注入是基于OrdersModule 已经被注册。这样的好处是两模块是松耦合,依赖的是接口。
-
注意两个模块两个模块实现依赖是靠依赖注入容器。依赖的注入发生在模块加载器的模块构造时。
Core Scenarios 核心场景
This section describes the common scenarios you will encounter when working with modules in your application. These scenarios include defining a module, registering and discovering modules, loading modules, initializing modules, specifying module dependencies, loading modules on demand, downloading remote modules in the background, and detecting when a module has already been loaded. You can register and discover modules in code, in a XAML or application configuration file, or by scanning a local directory.
此节描述了当同模块工作时你将遭遇的通常场景。这些场景包括定义一个模块,注册并发现模块,加载模块,初始化模块,指定模块依赖,点播加载模块,后台下载远程模块,还有当一个模块已经加载时检查。你可以利用代码,XAML或程序配置文件,或通过本地路径注册和发现模块。
Defining a Module 定义一个模块
A module is a logical collection of functionality and resources that is packaged in a way that can be separately developed, tested, deployed, and integrated into an application. Each module has a central class that is responsible for initializing the module and integrating its functionality into the application. That class implements the IModule interface, as shown here.
一个模块是一系列功能和资源的集合。可以单独开发,测试,部署并集成到程序的。每个模块有一个中央类,复杂初始化模块和集成功能到程序。此类实现了IModule 接口,如下所示。
public class MyModule : IModule { public void Initialize() { // Initialize module } }
The way you implement the Initialize method will depend on the requirements of your application. The module class type, initialization mode, and any module dependencies are defined in the module catalog. For each module in the catalog, the module loader creates an instance of the module class, and then it calls the Initialize method. Modules are processed in the order specified in the module catalog. The runtime initialization order is based on when the modules are downloaded, available, and the dependencies are satisfied.
你实现initialize方法的方式将取决于您的应用程序的需求。模块类型,初始化模块,模块目录中定义的依赖项。在目录中的每个模块,模块加载器创建一个模块类实例,并调用Initialize 方法。模块在模块目录中指定的顺序进行处理。在运行时初始化顺序是根据当模块被下载,可用,满足依赖关系而定。
Depending on the type of module catalog that your application is using, module dependencies can be set either by declarative attributes on the module class itself or within the module catalog file. The following sections provide more details.
根据模块目录也就是应用程序正在使用的类型,模块依赖关系既可以通过设置属性声明在模块类本身或就在模块目录文件里。下章提供更多细节。
Registering and Discovering Modules 注册和发现模块
The modules that an application can load are defined in a module catalog. The Prism Module Loader uses the module catalog to determine which modules are available to be loaded into the application, when to load them, and in which order they are to be loaded.
模块在模块目录中定义。Prism模块加载器使用模块目录去决定哪个模块是可用的,什么时候被加载和以什么顺序加载。
The module catalog is represented by a class that implements the IModuleCatalog interface. The module catalog class is created by the application bootstrapper class during application initialization. Prism provides different implementations of module catalog for you to choose from. You can also populate a module catalog from another data source by calling the AddModule method or by deriving from ModuleCatalog to create a module catalog with customized behavior.
模块目录是由实现IModuleCatalog 接口的类来表示。模块目录类是在程序初始化期间有程序引导器类创建。Prism提供不同实现模块目录的方式供你选择。你可以调用AddModule方法从其它数据源填充模块目录或继承ModuleCatalog类以自定义行为创建模块目录。
Note:注意 |
---|
Typically, modules in Prism use a dependency injection container and the Common Service Locator to retrieve instances of types that are required for module initialization. Both the Unity and the MEF containers are supported by Prism. Although the overall process of registering, discovering, downloading, and initializing modules is the same, the details can vary based on whether Unity or MEF is being used. The container-specific differences between approaches are explained throughout this topic. 通常,Prism模块使用一个依赖注入容器和公共服务定义器来取出模块初始化所需类型实例。Unity和MEF容器都被Prism支持。虽然注册,发现,下载和初始化模块的整个过程是相同的,但细节略有不同。方法之间的特定容器的差异将在本篇中说明。 |
Registering Modules in Code 代码注册模块
The most basic module catalog is provided by the ModuleCatalog class. You can use this module catalog to programmatically register modules by specifying the module class type. You can also programmatically specify the module name and initialization mode. To register the module directly with the ModuleCatalog class, call the AddModule method in your application's Bootstrapper class. An example is shown in the following code.
大多数基本模块目录通过ModuleCatalog 类提供。你可以使用这个模块目录编程注册模块。也可以编程指定模块名称和初始化模式。为了注册模块到ModuleCatalog 类,调用AddModule 方法在你的应用程序Bootstrapper 类。代码示例如下。
protected override void ConfigureModuleCatalog() { Type moduleCType = typeof(ModuleC); ModuleCatalog.AddModule( new ModuleInfo() { ModuleName = moduleCType.Name, ModuleType = moduleCType.AssemblyQualifiedName, }); }
In the preceding example, the modules are directly referenced by the shell, so the module class types are defined and can be used in the call to AddModule. That is why this example uses typeof(Module) to add modules to the catalog.
在上例中,模块被直接引用到壳里。所有模块类型可被直接定义并使用在AddModule。所有利用 typeof(Module) 就可以将模块添加到目录。
Note:注意 |
---|
If your application has a direct reference to the module type, you can add it by type as shown above; otherwise you need to provide the fully qualified type name and the location of the assembly. 如果你的应用程序有一个模块类型的直接引用,你可以通过类型添加它(如上所示);否则你需要提供类型全名和程序集位置。 |
To see another example of defining the module catalog in code, see StockTraderRIBootstrapper.cs in the Stock Trader Reference Implementation (Stock Trader RI).
想看代码中定义模块目录的另一个例子,看StockTraderRIBootstrapper.cs(来自股票操盘参考实现)。
Note:注意 |
---|
The Bootstrapper base class provides the CreateModuleCatalog method to assist in the creation of the ModuleCatalog. By default, this method creates a ModuleCatalog instance, but this method can be overridden in a derived class in order to create different types of module catalog. Bootstrapper 基类提供了 CreateModuleCatalog 方法协助创建 ModuleCatalog. 默认情况下,此方法创建一个ModuleCatalog 实例,单此方法可以被重写,进而创建不同类型的模块目录。 |
Registering Modules Using a XAML File 使用一个XAML文件注册模块
You can define a module catalog declaratively by specifying it in a XAML file. The XAML file specifies what kind of module catalog class to create and which modules to add to it. Usually, the .xaml file is added as a resource to your shell project. The module catalog is created by the bootstrapper with a call to the CreateFromXaml method. From a technical perspective, this approach is very similar to defining the ModuleCatalogin code because the XAML file simply defines a hierarchy of objects to be instantiated.
你可以指定一个XAML文件来定义模块目录。XAML文件指定各种模块目录。通常xaml文件被当做一个资源添加到壳项目。目录目录通过引导器的CreateFromXaml 方法创建。从技术角度讲,此方法和在代码中定义ModuleCatalog非常相似,XAML文件只是定义了要实例化的对象的层次结构。
The following code example shows a XAML file specifying a module catalog.
下面代码示例展示一个模块目录的XAML文件。
<!-- ModulesCatalog.xaml --> <Modularity:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:Modularity="clr-namespace:Microsoft.Practices.Prism.Modularity;assembly=Microsoft.Practices.Prism"> <Modularity:ModuleInfoGroup Ref="file://DirectoryModules/ModularityWithMef.Desktop.ModuleB.dll" InitializationMode="WhenAvailable"> <Modularity:ModuleInfo ModuleName="ModuleB" ModuleType="ModularityWithMef.Desktop.ModuleB, ModularityWithMef.Desktop.ModuleB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </Modularity:ModuleInfoGroup> <Modularity:ModuleInfoGroup InitializationMode="OnDemand"> <Modularity:ModuleInfo Ref="file://ModularityWithMef.Desktop.ModuleE.dll" ModuleName="ModuleE" ModuleType="ModularityWithMef.Desktop.ModuleE, ModularityWithMef.Desktop.ModuleE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <Modularity:ModuleInfo Ref="file://ModularityWithMef.Desktop.ModuleF.dll" ModuleName="ModuleF" ModuleType="ModularityWithMef.Desktop.ModuleF, ModularityWithMef.Desktop.ModuleF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"> <Modularity:ModuleInfo.DependsOn> <sys:String>ModuleE</sys:String> </Modularity:ModuleInfo.DependsOn> </Modularity:ModuleInfo> </Modularity:ModuleInfoGroup> <!-- Module info without a group --> <Modularity:ModuleInfo Ref="file://DirectoryModules/ModularityWithMef.Desktop.ModuleD.dll" ModuleName="ModuleD" ModuleType="ModularityWithMef.Desktop.ModuleD, ModularityWithMef.Desktop.ModuleD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </Modularity:ModuleCatalog>
Note:注意 |
---|
ModuleInfoGroups provide a convenient way to group modules that are in the same assembly, are initialized in the same way, or only have dependencies on modules in the same group. ModuleInfoGroups 提供一个方法的方式了组织在相同程序集的模块,以相同方式初始化,或只对模块有依赖关系的分在同一个组。 Dependencies between modules can be defined within modules in the same ModuleInfoGroup; however, you cannot define dependencies between modules in different ModuleInfoGroups. 模块之间的依赖关系可以在在同一ModuleInfoGroup 模块中所定义; 然而,你不能在不同的ModuleInfoGroups 定义模块之间的依赖关系。 Putting modules inside module groups is optional. The properties that are set for a group will be applied to all its contained modules. Note that modules can also be registered without being inside a group. 模块内模块组是可选的。这是一组设置的属性将应用于其包含的所有模块。注意,模块也可以不作为一个组内注册。 |
In your application's Bootstrapper class, you need to specify that the XAML file is the source for your ModuleCatalog, as shown in the following code.
在你的Bootstrapper 中,你需要指定 XAML文件作为模块目录的源,如下代码所示。
protected override IModuleCatalog CreateModuleCatalog() { return ModuleCatalog.CreateFromXaml(new Uri("/MyProject;component/ModulesCatalog.xaml", UriKind.Relative)); }
Registering Modules Using a Configuration File 使用配置文件注册模块
In WPF, it is possible to specify the module information in the App.config file. The advantage of this approach is that this file is not compiled into the application. This makes it very easy to add or remove modules at run time without recompiling the application.
在WPF中,可以把指定模块信息放在App.config 文件中,这种方法的优点文件没有被编译到应用程序。这使得它很容易在运行时添加或删除模块,而无需重新编译应用程序。
The following code example shows a configuration file specifying a module catalog. If you want the module to automatically load, set startupLoaded="true".
下面代码示例展示一个配置文件指定模块目录。如果你想要模块自动加载,设置 startupLoaded="true".
<!-- ModularityWithUnity.Desktop\app.config --> <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="modules" type="Microsoft.Practices.Prism.Modularity.ModulesConfigurationSection, Microsoft.Practices.Prism"/> </configSections> <modules> <module assemblyFile="ModularityWithUnity.Desktop.ModuleE.dll" moduleType="ModularityWithUnity.Desktop.ModuleE, ModularityWithUnity.Desktop.ModuleE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ModuleE" startupLoaded="false" /> <module assemblyFile="ModularityWithUnity.Desktop.ModuleF.dll" moduleType="ModularityWithUnity.Desktop.ModuleF, ModularityWithUnity.Desktop.ModuleF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ModuleF" startupLoaded="false"> <dependencies> <dependency moduleName="ModuleE"/> </dependencies> </module> </modules> </configuration>
Note:注意 |
---|
Even if your assemblies are in the global assembly cache or in the same folder as the application, the assemblyFile attribute is required. The attribute is used to map the moduleType to the correctIModuleTypeLoader to use. 即使你的程序集是在全局程序集缓存,或在同一文件夹中的应用,assemblyFile 特性还是需要的。该特性用于将moduleType 映射到正确的IModuleTypeLoader 使用。 |
In your application's Bootstrapper class, you need to specify that the configuration file is the source for your ModuleCatalog. To do this, you use the ConfigurationModuleCatalog class, as shown in the following code.
在你的应用程序的 Bootstrapper 类中,你需要指定这个配置文件是ModuleCatalog的源。为做到这个,你可以使用ConfigurationModuleCatalog 类,如下代码所示。
protected override IModuleCatalog CreateModuleCatalog() { return new ConfigurationModuleCatalog(); }
Note:注意 |
---|
You can still add modules to a ConfigurationModuleCatalog in code. You can use this, for example, to make sure that the modules that your application absolutely needs to function are defined in the catalog. 你可以利用添加模块到ConfigurationModuleCatalog 中。可以把系统必须用的功能功能用代码添注册。 |
Discovering Modules in a Directory 在路径上发现模块
The Prism DirectoryModuleCatalog class allows you to specify a local directory as a module catalog in WPF. This module catalog will scan the specified folder and search for assemblies that define the modules for your application. To use this approach, you will need to use declarative attributes on your module classes to specify the module name and any dependencies that they have. The following code example shows a module catalog that is populated by discovering assemblies in a directory.
Prism的DirectoryModuleCatalog 类允许你知道一个本地路径作为一个模块路径。这个模块目录将扫描指定的文件夹和搜索定义模块为您的应用程序集。为了用词方法,你将需要使用声明式特性在你的模块类上,指定模块名称和任何模块依赖项。下面代码示例展示一个模块目录由路径发现的程序集填充。
protected override IModuleCatalog CreateModuleCatalog() { return new DirectoryModuleCatalog() {ModulePath = @".\Modules"}; }
Loading Modules 加载模块
After the ModuleCatalog is populated, the modules are ready to be loaded and initialized. Module loading means that the module assembly is transferred from disk into memory. The ModuleManager is responsible for coordinating the loading and initialization process.
ModuleCatalog 被填充之后,模块就准备好被加载和初始化了。模块加载意味着模块程序集成磁盘读入内存。ModuleManager 负责协调加载和初始化过程。
Initializing Modules 初始化模块
After the modules load, they are initialized. This means an instance of the module class is created and its Initialize method is called. Initialization is the place to integrate the module with the application. Consider the following possibilities for module initialization:
模块加载之后,就是初始化。这意味着模块类的实例被创建并且Initialize 方法被调用。初始化是集成模块到程序的地方。考虑模块初始化的以下可能性:
- Register the module's views with the application. If your module is participating in user interface (UI) composition using view discovery or view injection, your module will need to associate its views or view models with the appropriate region name. This allows views to show up dynamically on menus, toolbars, or other visual regions within the application.
- Subscribe to application level events or services. Often, applications expose application-specific services and/or events that your module is interested in. Use the Initialize method to add the module's functionality to those application-level events and services.
For example, the application might raise an event when it is shutting down and your module wants to react to that event. It is also possible that your module must provide some data to an application level service. For example, if you have created a MenuService (it is responsible for adding and removing menu items), the module's Initialize method is where you would add the correct menu items.
订阅应用程序级别的事件或服务。经常,应用程序公开应用程序特定的服务和事件是你的模块感兴趣的。使用Initialize方法来添加模块功能来使用这些程序级别的事件或服务。
Note:注意 Module instance lifetime is short-lived by default. After the Initialize method is called during the loading process, the reference to the module instance is released. If you do not establish a strong reference chain to the module instance, it will be garbage collected.
模块示例的生命周期是短暂的,Initialize 方法被调用之后,如果不保存个强引用,它就被垃圾回收了。
This behavior may be problematic to debug if you subscribe to events that hold a weak reference to your module, because your module just "disappears" when the garbage collector runs.
这种行为可能会产生问题,事件定义完了,模块消失了,就不执行了。 - Register types with a dependency injection container. If you are using a dependency injection pattern such as Unity or MEF, the module may register types for the application or other modules to use. It may also ask the container to resolve an instance of a type it needs.
Specifying Module Dependencies 指定模块依赖关系
Modules may depend on other modules. If Module A depends on Module B, Module B must be initialized before Module A. The ModuleManager keeps track of these dependencies and initializes the modules accordingly. Depending on how you defined your module catalog, you can define your module dependencies in code, configuration, or XAML.
模块可能依赖于其他模块。如果模块A依赖于模块B ,模块B必须在模块A初始化之前进行初始化。ModuleManager 追踪这些依赖关系从而进行初始化模块。根据你定义的模块目录,你可以利用代码,配置文件,或XAML定义依赖关系。
Specifying Dependencies in Code 用代码指定依赖关系
For WPF applications that register modules in code or discover modules by directory, Prism provides declarative attributes to use when creating a module as shown in the following code example.
对于WPF应用程序,在代码中,通过路径注册模块或发现模块,Prism提供了声明特性。代码示例如下。
// (when using Unity) [Module(ModuleName = "ModuleA")] [ModuleDependency("ModuleD")] public class ModuleA: IModule { ... }
Specify Dependencies in XAML 用XAML指定依赖关系
The following XAML shows where Module F depends on Module E.
下面XAML展示模块F依赖于模块E。
<!-- ModulesCatalog.xaml --> <Modularity:ModuleInfo Ref="file://ModularityWithMef.Desktop.ModuleE.dll" moduleName="ModuleE" moduleType="ModularityWithMef.Desktop.ModuleE, ModularityWithMef.Desktop.ModuleE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"> <Modularity:ModuleInfo Ref="file://ModularityWithMef.Desktop.ModuleF.dll" moduleName="ModuleF" moduleType="ModularityWithMef.Desktop.ModuleF, ModularityWithMef.Desktop.ModuleF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" > <Modularity:ModuleInfo.DependsOn> <sys:String>ModuleE</sys:String> </Modularity:ModuleInfo.DependsOn> </Modularity:ModuleInfo> . . .
Specify Dependencies in Configuration 在配置文件中指定依赖关系
The following example App.config file shows where Module F depends on Module E.
下面的示例App.config文件展示模块F依赖于模块E
<!-- App.config --> <modules> <module assemblyFile="ModularityWithUnity.Desktop.ModuleE.dll" moduleType="ModularityWithUnity.Desktop.ModuleE, ModularityWithUnity.Desktop.ModuleE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ModuleE" startupLoaded="false" /> <module assemblyFile="ModularityWithUnity.Desktop.ModuleF.dll" moduleType="ModularityWithUnity.Desktop.ModuleF, ModularityWithUnity.Desktop.ModuleF, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ModuleF" startupLoaded="false"> <dependencies> <dependency moduleName="ModuleE" /> </dependencies> </module> </modules>
Loading Modules on Demand 按需加载模块
To load modules on demand, you need to specify that they should be loaded into the module catalog with the InitializationMode set to OnDemand. After you do that, you need to write the code in your application that requests the module be loaded.
为实现按需加载模块,你需要设置它们在模块目录中的InitializationMode 属性设置为OnDemand 。做完这个操作之后,你需要写些代码来请求模块被加载。
Specifying On-Demand Loading in Code 代码中指定按需加载
A module is specified as on-demand using attributes, as shown in the following code example.
一个模块设置了点播属性,如下代码所示。
// Boostrapper.cs protected override void ConfigureModuleCatalog() { . . . Type moduleCType = typeof(ModuleC); this.ModuleCatalog.AddModule(new ModuleInfo() { ModuleName = moduleCType.Name, ModuleType = moduleCType.AssemblyQualifiedName, InitializationMode = InitializationMode.OnDemand }); . . . }
Specifying On-Demand Loading in XAML XAML中指定按需加载
You can specify the InitializationMode.OnDemand when you define your module catalog in XAML, as shown in the following code example.
你可以指定 InitializationMode.OnDemand ,如下所示。
<!-- ModulesCatalog.xaml --> ... <module assemblyFile="ModularityWithUnity.Desktop.ModuleE.dll" moduleType="ModularityWithUnity.Desktop.ModuleE, ModularityWithUnity.Desktop.ModuleE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ModuleE" startupLoaded="false" /> ...
Specifying On-Demand Loading in Configuration 在配置文件中指定按需加载
You can specify the InitializationMode.OnDemand when you define your module catalog in the App.config file, as shown in the following code example.
你可以指定 InitializationMode.OnDemand ,如下所示。
<!-- App.config --> <module assemblyFile="ModularityWithUnity.Desktop.ModuleC.dll" moduleType="ModularityWithUnity.Desktop.ModuleC, ModularityWithUnity.Desktop.ModuleC, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ModuleC" startupLoaded="false" />
Requesting On-Demand Loading of a Module 请求按需加载模块
After a module is specified as on demand, the application can then ask the module to be loaded. The code that wants to initiate the loading needs to obtain a reference to the IModuleManager service registered with the container by the bootstrapper.
在模块被指定为按需加载后,应用程序可以要求模块被加载。通过IModuleManager 服务可以做到。
private void OnLoadModuleCClick(object sender, RoutedEventArgs e) { moduleManager.LoadModule("ModuleC"); }
Detecting When a Module Has Been Loaded 当模块加载后检测
The ModuleManager service provides an event for applications to track when a module loads or fails to load. You can get a reference to this service through dependency injection of the IModuleManager interface.
ModuleManager 服务提供一个事件用来追踪模块加载完成或是失败,你可以获取一个引用通过 IModuleManager 接口的依赖注入。
this.moduleManager.LoadModuleCompleted += this.ModuleManager_LoadModuleCompleted;
void ModuleManager_LoadModuleCompleted(object sender, LoadModuleCompletedEventArgs e) { ... }
To keep the application and modules loosely coupled, the application should avoid using this event to integrate the module with the application. Instead, the module's Initialize method should handle integrating with the application.
保存程序和模块之间松耦合,应用程序应该避免使用事件去集成模块到程序中,应该使用模块的Initialize方法处理集成。
The LoadModuleCompletedEventArgs contains an IsErrorHandled property. If a module fails to load and the application wants to prevent the ModuleManager from logging the error and throwing an exception, it can set this property to true.
LoadModuleCompletedEventArgs 包含一个IsErrorHandled 属性,如果模块加载失败,程序想阻止记录错误抛出异常,可以将此属性设成true.
Note:注册 |
---|
After a module is loaded and initialized, the module assembly cannot be unloaded. The module instance reference will not be held by the Prism libraries, so the module class instance may be garbage collected after initialization is complete. 模块加载和初始化之后,模块程序集不能被卸载。模块示例的引用可以不被Prism库持有,那么一会它就被垃圾回收了。 |
Modules in MEF MEF中的模块
This section only highlights the differences if you choose to use MEF as your dependency injection container.
如果您选择使用MEF作为你的依赖注入容器,那么看看此节给出的一些差异。
Note:注意 |
---|
When using MEF, the MefModuleManager is used by the MefBootstrapper. It extends the ModuleManager and implements the IPartImportsSatisfiedNotification interface to ensure that the ModuleCatalog is updated when new types are imported by MEF. 当使用MEF时, MefModuleManager 使用MefBootstrapper。它扩展了ModuleManager 并实现了IPartImportsSatisfiedNotification 接口以确保 ModuleCatalog在新类型导入时更新ModuleCatalog |
Registering Modules in Code Using MEF 在代码中使用MEF注册模块
When using MEF, you can apply the ModuleExport attribute to module classes to have MEF automatically discover the types. The following is an example.
当使用MEF,你可以允许ModuleExport 特性到模块类,MEF可自动发现类型,以下是个例子。
[ModuleExport(typeof(ModuleB), InitializationMode = InitializationMode.OnDemand)] public class ModuleB : IModule { ... }
You can also use MEF to discover and load modules using the AssemblyCatalog class, which can be used to discover all the exported module classes in an assembly, and the AggregateCatalog class, that allows multiple catalogs to be combined into one logical catalog. By default, the Prism MefBootstrapper class creates an AggregateCatalog instance. You can then override the ConfigureAggregateCatalog method to register assemblies, as shown in the following code example.
你也可以使用MEF发现和加载模块通过使用AssemblyCatalog 类,可以发现所有在程序集中的导入模块。还有AggregateCatalog 类,允许多种目录组合成一个逻辑目录,默认情况下,PrismMefBootstrapper 创建一个 AggregateCatalog实例。你可以重写ConfigureAggregateCatalog 方法来注册程序集,如下代码所示。
protected override void ConfigureAggregateCatalog() { base.ConfigureAggregateCatalog(); //Module A is referenced in in the project and directly in code. this.AggregateCatalog.Catalogs.Add( new AssemblyCatalog(typeof(ModuleA).Assembly)); this.AggregateCatalog.Catalogs.Add( new AssemblyCatalog(typeof(ModuleC).Assembly)); . . . }
The Prism MefModuleManager implementation keeps the MEF AggregateCatalog and the Prism ModuleCatalog synchronized, thereby allowing Prism to discover modules added via the ModuleCatalog or theAggregateCatalog.
Prism MefModuleManager 实现保存 MEF AggregateCatalog 和Prism ModuleCatalog 同步。因此允许Prism去发现模块添加使用ModuleCatalog 或AggregateCatalog.
Note:注意 |
---|
MEF uses Lazy<T> extensively to prevent instantiation of exported and imported types until the Value property is used. ME使用 Lazy<T> 可以防止类型都被实例化,可以到此类型被使用时实例化。 |
Discovering Modules in a Directory Using MEF 使用MEF在路径下发现模块
MEF provides a DirectoryCatalog that can be used to inspect a directory for assemblies containing modules (and other MEF exported types). In this case, you override the ConfigureAggregateCatalog method to register the directory. This approach is only available in WPF.
MEF提供一个 DirectoryCatalog 可以检查路径是否包含模块程序集(或是其他MEF导出类型)。这种情况下,你重写ConfigureAggregateCatalog 方法去注册路径,此方法只在WPF中可用。
To use this approach, you first need to apply the module names and dependencies to your modules using the ModuleExport attribute, as shown in the following code example. This allows MEF to import the modules and allows Prism to keep the ModuleCatalog updated.
为了使用此方法,你首先需要申请模块名称和请求了ModuleExport 特性的依赖关系,如下代码示例所示。这允许MEF导入模块和允许PRism保持ModuleCatalog 更新。
protected override void ConfigureAggregateCatalog() { base.ConfigureAggregateCatalog(); . . . DirectoryCatalog catalog = new DirectoryCatalog("DirectoryModules"); this.AggregateCatalog.Catalogs.Add(catalog); }
Specifying Dependencies in Code Using MEF 使用MEF在代码中指定依赖关系
For WPF applications using MEF, use the ModuleExport attribute, as shown here.
对于使用MEF的WPF应用程序,使用ModuleExport 特性,如下所示。
// (when using MEF) [ModuleExport(typeof(ModuleA), DependsOnModuleNames = new string[] { "ModuleD" })] public class ModuleA : IModule { ... }
Because MEF allows you to discover modules at run time, you may also discover new dependencies between modules at run time. Although you can use MEF alongside the ModuleCatalog, it is important to remember that the ModuleCatalog validates the dependency chain when it is loaded from XAML or configuration (before any modules are loaded). If a module is listed in the ModuleCatalog and then loaded using MEF, theModuleCatalog dependencies will be used, and the DependsOnModuleNames attribute will be ignored.
由于MEF允许你运行时发现模块,你也会发现模块间新依赖关系。尽管你可以MEF的ModuleCatalog, 重要的是 ModuleCatalog在从XAML或配置文件加载时验证依赖链(在任何模块加载前)。如果一个模块在ModuleCatalog 然后使用MEF加载,ModuleCatalog 依赖关系将被使用,DependsOnModuleNames 特性就会被忽略。(没太明白)
Specifying On-Demand Loading Using MEF 使用MEF指定按需加载
If you are using MEF and the ModuleExport attribute for specifying modules and module dependencies, you can use the InitializationMode property to specify that a module should be loaded on demand, as shown here.
如果你可以使用MEF和ModuleExport 特性来指定模块和模块依赖,你可以使用InitializationMode 属性指定一个模块应该按需加载,如下所示。
[ModuleExport(typeof(ModuleC), InitializationMode = InitializationMode.OnDemand)] public class ModuleC : IModule { }
More Information 更多信息
For more information about assembly caching, see "How to: Use Assembly Library Caching" on MSDN.
更多程序集缓存信息,请看MSDN上的"How to: Use Assembly Library Caching"
To learn more about modularity in Prism, see the Modularity with MEF for WPF QuickStart or the Modularity with Unity for WPF QuickStart. For more information about the QuickStarts, see Modularity QuickStarts.
学习更多Prism模块化知识,请看MEF模块化快速入门或是Untiy模块化快速入门。更多信息请看快速入门Modularity QuickStarts.
For information about the modularity features that can be extended in the Prism Library, see Modules in Extending the Prism Library.
关于可扩展的Prism库的模块化功能的信息,请看在 Extending the Prism Library的 Modules 。