原文引自:
http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx
Offline Application Block 的设计
发布日期: 8/6/2004 | 更新日期: 8/6/2004
Microsoft Corporation
摘要:第 2 章讨论 Offline Application Block 的设计。
本页内容
Offline Application Block 是根据 .NET 框架的功能并封装智能客户端应用程序来构建的,以帮助用户在脱机时执行任务,就像用户在联机时执行任务一样简单而有效。本章说明 Offline Application Block 的设计、功能以及融入其设计的决策。
脱机挑战
智能客户端应用程序的开发人员在设计可以联机和脱机运行的程序时,必须解决许多问题。通常发生的问题包括:
• |
应用程序如何确定它处于联机状态还是脱机状态? |
• |
如果连接能够以不可预计的次数进行更改,那么应该如何通知依赖于连接状态的应用程序组件呢? |
• |
应用程序应该如何存储数据以及将数据存储在本地的什么地方,才能在脱机状态下对其进行访问?数据会变陈旧吗?应该在何时刷新数据? |
• |
当应用程序无法访问所有必需的数据或服务时,是否应采用不同的运作方式? |
• |
当应用程序处于脱机状态时,应如何存储事务性数据(消息数据),以及在何处存储这些数据? |
• |
当应用程序从脱机变为联机时,应如何将事务性数据与服务器同步? |
解决方案说明
Offline Application Block 可以提供具有脱机功能的智能客户端应用程序所需的基本功能。其基本功能包括:
• |
检测网络连接是否存在。 |
• |
当连接状态变更时通知所有已注册的组件。 |
• |
下载并缓存参考数据,以便在网络连接不可用时允许应用程序正常工作。 |
• |
当应用程序脱机时,在本地存储消息数据。 |
• |
当网络连接变为可用时,将消息数据与服务器进行同步。 |
设计目标
Offline Application Block 旨在用作要将脱机功能包括在其应用程序中的开发人员的体系结构指南。该应用程序块的设计目标为:
• |
在组件之间提供松耦合。 |
• |
抽象化应用程序的连接状态管理。 |
• |
为联机和脱机模式下的应用程序提供相同的编程模型,这样应用程序在这两种模式之间的转换不会影响用户体验。 |
• |
为诸如连接检测和排队这样的功能提供可扩展的接口。 |
• |
合并设计模式。 |
设计注意事项
应用程序块体系结构提倡使用可重复使用的封装代码组件。理想情况下,这些组件可设计为既可以互操作,又是模块化的,以便代码可以直接重复使用。在应用程序中引用某个块可以为应用程序提供该块的功能。
Offline Application Block 设计可提供逻辑和物理构建块,开发人员需要使用这些构建块将脱机功能合并到他们的应用程序中。成功支持脱机操作的能力并不是通过简单地添加构建块并调整现有的应用程序就可以获得。该构建块设计为推荐做法的一个示例 — 所提供的代码仅阐述了可能的解决方案。在设计阶段仔细计划可以确保您的 Offline Application Block 应用程序既可以重复使用又可以扩展。
模式
模式是可重复使用的解决方案,用于解决程序员可能在类似环境中遇到的重复出现的设计问题。在 Offline Application Block 的设计中使用了几种模式,如表 2.1 中所述。
Builder |
从已创建组件的应用程序逻辑中分离复杂的创建逻辑。允许应用程序类的客户端在创建块组件的过程中保持独立。 |
Factory |
构建一个用于创建对象的接口,该接口应在创建对象时允许子类选择要实例化的类。 |
Observer |
在对象之间使用一对多的关系以获得及时有效的通知。当第一个对象更改时,所有相关的对象会立即获得通知并自动进行更新。 |
Singleton |
提供对某个服务的单一、全局访问点。Singleton 可用于只允许创建特定类的一个实例,并在需要该实例时提供对该单一实例的访问。 |
Strategy |
提供定义一系列类似算法和接口(通过其访问这些算法)的能力。所使用的特定算法的实现可以在不同时更改客户端类的情况下进行更改。 |
Offline Application Block 设计
本部分说明了组成 Offline Application Block 的主要子系统的过程和体系结构。有关主要子系统如何协同工作的详细说明,请参阅本章的“请求和响应往返”部分。
注 本部分只提供每个子系统类中最重要的方法和属性的介绍。有关这些类及其成员的详细信息,请参阅该应用程序块随附的帮助文件。它位于 <安装目录>\Offline\Docs\ 文件夹中。
图 2.1 展示了构成脱机应用程序的物理元素。
图 2.1.Offline Application Block 的物理视图
表 2.1 描述了图 2.1 中的每个组件。这些组件将在本章中详细讨论。
连接检测策略 |
连接状态管理 |
检测物理连接的当前状态 |
ConnectionManager |
连接状态管理 |
管理与物理网络访问相关的连接状态服务。使用可插入的连接检测组件(通过实现 IConnectionDetectionStrategy 接口)来确定连接状态。当“连接检测策略”通知 ConnectionManager 连接状态发生了变化,它就会触发一个事件。ConnectionManager 还具有一些公共方法,应用程序可以调用这些方法来手动更改应用程序的联机/脱机状态。 |
Executor |
消息数据管理 |
在联机时,从队列中提取消息,并调用负责将消息发送到远程服务的“联机代理”。此外,它还负责将来自远程服务的响应发送回应用程序。 |
队列存储 |
消息数据管理 |
提供用于保存消息数据的数据存储区,以及将在应用程序再次联机时发送给服务器的操作。 |
QueueManager |
消息数据管理 |
用作“队列存储提供程序”的一个接口。它提供消息入列和消息出列的方法。 |
DataLoaderManager |
参考数据管理 |
提供一种工具,以允许应用程序在适当的时间请求要下载的参考数据,以便在操作过程中使用。 |
ReferenceDataCache |
参考数据管理 |
用作“参考数据管理”子系统的一个接口。它负责确保应用程序所需的数据已存储,并可以在应用程序处于脱机状态时进行本地访问(缓存)。 |
缓存块 |
参考数据管理 |
提供一个物理缓存操作的实现。 |
应用程序服务代理 |
服务代理管理 |
提供队列消息的能力。它还提供了一个将结果返回应用程序的通道。 |
Online Proxy |
服务代理管理 |
由应用程序开发人员创建的类,它负责与提供业务功能的远程服务进行通信。如果需要,“联机代理”还可以负责在缓存中存储参考数据。 |
ServiceAgent |
服务代理管理 |
提供由应用程序提供的所有服务代理所实现的基类。“服务代理”基类负责在服务代理注册表中注册服务代理。 |
ServiceAgentManager |
服务代理管理 |
处理后,“服务代理管理器”会将结果返回到相应的“服务代理”。 |
说明性方案
以下方案显示了用户所采取的操作如何转换为在智能客户端应用程序内部发生的操作。有关构成 Offline Application Block 的元素是如何协同工作的整体概述,请参阅前面的图 2.1。
准备脱机工作
David 已连接到网络,并且正在运行用于填写其责任区域的保险理赔的应用程序。他按计划到现场处理这个理赔。David 单击“Download Work Items”按钮以下载他需要完成的所有理赔。
应用程序操作
在运行时,应用程序对适当的“应用程序服务代理”进行方法调用以下载数据。然后,“应用程序服务代理”再调用 DataLoaderManager 以初始化下载。DataLoaderManager 调用 QueueManager,并将数据下载请求置于队列中。如果系统处于联机状态,则 Executor 将提取该请求,然后通过反射创建 OnlineProxy 的实例并调用指定的方法来处理该请求。然后,“联机代理”与远程服务器进行通信并获得所需的数据。它通过使用 ReferenceDataCache 将数据存储在本地计算机的缓存中。然后,Executor 调用“服务代理管理器”以返回结果。接下来,“服务代理管理器”对特定“应用程序服务代理”进行回调。最后,“应用程序服务代理”引发一个事件来通知应用程序有关已下载数据的信息。
图 2.2 阐释了应用程序用于下载参考数据的过程。
注 Offline Application Block 会保留部分缓存,并将其命名为“参考数据缓存”。该缓存只管理参考数据。您可以使用为您的组织而设计的数据存储区来创建新缓存。
图 2.2.下载参考数据时涉及的块的各个部分
脱机工作
David 随身携带了计算机。当他再次访问该应用程序时,计算机没有连接到网络,应用程序是在脱机模式下启动的。David 在与他的客户协商后完成了理赔。
应用程序操作
应用程序向 ConnectionManager 注册以接收连接状态更改事件。ConnectionManager 将使用 ConnectionDetectionStrategy 来确定计算机处于脱机状态,并触发一个事件以通知所有注册的组件。
注 组件必须向“连接管理器”注册,才能收到状态更改事件的通知。
图 2.3 阐释了应用程序用于检测与网络的连接性的过程。
图 2.3.脱机过程中涉及的块的各个组件
完成理赔
David 在应用程序中打开一个理赔,完成该理赔,然后单击“Submit”按钮。
应用程序操作
应用程序会在内部调用“应用程序服务代理”来处理工作项目。“服务代理”将创建一个包含该工作项目的 Payload。在创建 Payload 之后,“服务代理”会调用“队列管理器”将消息数据存储在队列中。QueueManager 将 Payload 封装在一个 QueueMessage 中,并将其保留在队列中。
图 2.4 阐释了应用程序用于在脱机状态下完成理赔的过程。
图 2.4.在脱机状态下完成理赔所涉及的块的各个部分
同步脱机数据
David 返回办公室,将计算机连接到网络,然后启动应用程序。
应用程序操作
一旦连接状态变为联机,就会在单独的线程上激活 Executor。然后,Executor 开始检索队列中的队列消息。对于每个 QueueMessage,Executor 会调用 OnlineProxy(这是一个使用反射创建的对象,它使用 Payload 中包含的 onlineProxyContext 信息)的指定方法,将工作项目信息提交到远程服务以便进行处理。处理工作项目后,远程服务会将结果返回到 Executor。
然后,Executor 将包含结果的 Payload 返回到“服务代理管理器”,随后该管理器会在 ServiceAgentRegistry 中查找“应用程序服务代理”。它将响应发送到该“应用程序服务代理”。“应用程序服务代理”会在更新数据时通知用户界面。所有理赔都以透明的方式进行同步。
如果消息执行失败,并且原来的“应用程序服务代理”不再可用,则“服务代理管理器”会将响应发送到 FailsafeServiceAgent。
图 2.5 阐释了“服务代理管理”子系统执行的步骤。
图 2.5.添加脱机数据时所涉及的块的各个部分
Offline Application Block 子系统
子系统的标准定义是:一起工作以完成指定目标的一组独立的类。通过将 Offline Application Block 划分为下列逻辑子系统,可以充分地理解它:
• |
连接状态管理 |
• |
服务代理管理 |
• |
消息数据管理 |
• |
参考数据管理 |
• |
实用工具服务 |
“连接状态管理”子系统监视应用程序的联机/脱机状态,并通知已注册的侦听器。“连接状态管理”可处理所有连接状态任务(那些与检测物理连接相关的任务),并且可以进行扩展,以检测并连接到某个服务,并可以检测服务在何时不可用。
“服务代理管理”子系统控制“应用程序服务代理”以及与 Executor 类的接口。
“消息数据管理”和“参考数据管理”子系统协同工作,以确保首次尝试访问数据项目时数据的可用性。
“实用工具服务”管理连接性和非池线程 — 这意味着,线程管理对开发人员是透明的。
下列各部分给出了有关每个子系统的详细信息。
连接状态管理
“连接状态管理”子系统的主要角色是:检测连接状态并通知侦听器任何状态变化。要扩展其智能客户端应用程序以包含脱机功能的开发人员,可能希望将应用程序设计为在联机工作和脱机工作时具有不同的行为。应用程序是处于联机状态还是脱机状态,由下列两个因素的其中一个决定:
• |
常规网络连接是否存在 — 应用程序是否能够访问远程资源和服务? |
• |
来自应用程序用户的明确指令。应用程序可能允许用户将连接状态设置为脱机,即使第一个因素指示应用程序处于联机状态。 |
类设计
“连接状态管理”子系统包含的类可管理连接检测和通知服务以及线程管理组件。这些类包括 ConnectionManager 类和 ConnectionDetector 类。
ConnectionManager 类
ConnectionManager 类是“连接状态管理”子系统的一个接口,并且是 Observer 模式的一个示例。它负责管理连接状态变化的检测,以及在发生变化时通知相应的对象。它还提供了用于强制应用程序脱机或联机的方法。
类成员
以下部分(“方法”)介绍了该类中最重要的成员。有关详细信息,请参阅该应用程序块中随附的帮助文件。
方法
• |
GoOffline – 强制系统脱机。 |
• |
GoOnline – 强制系统联机。 |
ConnectionDetector 类
ConnectionDetector 类管理发生的状态转换,这些转换是由派生的 IConnectionDetectionStrategy 接口实现检测到的。IConnectionDetectionStrategy 接口提供了检测物理连接当前状态的功能,而 ConnectionDetector 类提供了检测状态变化的功能。
IConnectionDetectionStrategy 接口
“连接状态管理”子系统支持不同的连接检测实现(也称为提供程序),以检测连接是否存在。该子系统使用 Strategy 模式,可允许在不更改应用程序代码的情况下更改连接检测算法。每个连接检测提供程序都必须实现 IConnectionDetectionStrategy 接口,以便该提供程序可以与应用程序块一起使用。
通过实现该接口并在应用程序的配置文件中指定必要的配置信息,应用程序开发人员可以创建他们自己的连接检测策略。
尽管可以在配置文件中定义多个策略,但只应该在配置文件中启用其中一个策略。
该应用程序块附带一个名为 WinInetDetectionStrategy 的连接检测提供程序,它使用 Windows 网络 API (WinInet) 来检测网络连接是否存在。有关详细信息,请参阅本指南随附的帮助文件。
服务代理管理
“服务代理管理”子系统执行下列操作:
• |
维护“应用程序服务代理”的注册表 |
• |
包含负责在处理请求后将结果返回给“应用程序服务代理”的组件 |
• |
提供应用程序开发人员用于实现应用程序服务代理所必需的“服务代理”基类 |
设计用于 Offline Application Block 的“服务代理”
正如第 1 章中讨论的那样,Offline Application Block 使用面向服务的方法为智能客户端应用程序提供脱机功能。使用该方法,“服务代理”可以与提供各种业务功能的不同类型的远程服务一起工作。这些业务功能可作为 Web 服务在服务器端公开。“服务代理”也需要与“缓存管理”、“队列管理”和“消息数据管理”子系统进行交互。
由于这些交互,“服务代理”可能要在这些子系统之间进行多次往返。您可以设计一个具有不同任务粒度级别的“服务代理”。您还可以在“服务代理”与远程服务进行通信时添加粒度。这些方法可以包含:
• |
与单个任务进行交互的“服务代理”。例如,它可以创建一个客户。 |
• |
与和某个实体相关的所有任务进行交互的“服务代理”。例如,它可以在客户实体上执行创建、读取、更新和删除操作。 |
• |
只处理一个特定服务的“服务代理” |
• |
处理多个服务的“服务代理” |
在 Offline Application Block 的设计中,“服务代理”的责任划分为两个类:它们是:
• |
ServiceAgent – 该类在块中本地执行所有任务。这些任务可以包括创建和排列 Payload、更新本地缓存或从本地缓存中检索满足该请求所需的所有数据。在设计新的脱机块时,开发人员应该从 Offline Application Block 提供的 ServiceAgent 类中派生一个新的“应用程序服务代理”类。 |
• |
OnlineProxy – 该类抽象了与提供业务功能的服务进行交互的功能。应用程序开发人员必须创建他们自己的“联机代理”。 |
使用两个类(而不是单个类)的原因是:当 Offline Application Block 需要与 Web 服务进行通信时,需要创建并使用多个代理。为此,这些代理应是无状态的。
类设计
“服务代理管理”子系统包括要从 Executor 中获取响应、然后将结果传回“应用程序服务代理”的类。如果出现错误并且原来的“应用程序服务代理”不可用,则错误被传递到“Failsafe 服务代理”。
ServiceAgent 类
Offline Application Block 提供了一个 ServiceAgent 类。这个基类抽象了所有“应用程序服务代理”的常见行为和数据。
ServiceAgent 基类最重要的责任是向 ServiceAgentRegistry 注册“应用程序服务代理”的所有实例。(该过程在抽象后置于 Service Agent 基类中,以增加代码的可重复使用性。)“服务代理注册表”用于在消息处理过程中,将事务的成功或失败消息返回到原始的“应用程序服务代理”。对基类的构造函数中的所有派生类透明地完成该注册。
类成员
以下部分(“属性”)介绍了该类中最重要的成员。有关详细信息,请参阅该应用程序块随附的帮助文件。
属性
GUID – ServiceAgent 类的每个实例的全局唯一标识符 (GUID)。GUID 可唯一标识 ServiceAgentRegistry 中的特定“应用程序服务代理”。
FailsafeServiceAgent 类
Offline Application Block 的设计目标是将“联机代理”执行的结果返回到原始的“应用程序服务代理”(如果该原始“应用程序服务代理”仍然存在)。原始代理可能不存在的原因有以下几个:
• |
应用程序可能显式地将其从系统中删除。 |
• |
应用程序可能被关闭然后重新开启。 |
一旦“应用程序服务代理”被损坏,则来自“联机代理”的所有错误都会丢失,这是因为原始服务代理已不再接收它们。在某些情况下,这还会导致数据丢失。为防止出现这种情况,该应用程序块提供了 FailsafeServiceAgent。
FailsafeServiceAgent 类很常用,它是“联机代理”执行过程中处理错误的最后手段。它并不是通过其正常返回错误的默认“服务代理”。如果发生了以下所有事件,则结果将被返回到 FailsafeServiceAgent:
• |
该应用程序块在 Executor 中执行“联机代理”。 |
• |
该“联机代理”失败。 |
• |
找不到原始的“应用程序服务代理”。 |
为了使该返回机制正常工作,应用程序必须满足以下要求:
• |
只使用从 OfflineBlockBuilder 检索的 FailsafeServiceAgent 的实例。 |
• |
始终维护在 FailsafeServiceAgent 类的 ErrorEvent 中注册的回调方法。 |
ServiceAgentContext 类
当“服务代理管理器”调用“应用程序服务代理”的一个方法时,会从 ServiceAgentContext 类中检索要调用的相应方法。Payload(在本章的“消息数据管理”部分中定义)可为每个请求提供“服务代理”上下文。
类成员
以下部分(“属性”)介绍了该类中最重要的成员。有关详细信息,请参阅该应用程序块随附的帮助文件。
属性
MethodToInvoke – 返回要调用的方法名称。“服务代理管理器”调用“应用程序服务代理”的这个方法,以传回请求的结果。
OnlineProxyContext 类
OnlineProxyContext 类可封装调用“联机代理”的远程服务请求行为所必需的信息。Payload(在后面的“消息数据管理”部分中定义)可为每个请求提供“联机代理”上下文。
ServiceAgentManager类
ServiceAgentManager 可将服务请求的结果返回到适当的“应用程序服务代理”。代理是由其 GUID 识别的。ServiceAgentManager 使用 GUID 从“服务代理注册表”中获得特定“应用程序服务代理”的实例 — 它会调用指定的“应用程序服务代理”方法。每次实例化一个“应用程序服务代理”,它就会得到一个新的 GUID。
ServiceAgentRegistry 类
ServiceAgentRegistry 类可作为当前活动的“应用程序服务代理”的储备库。在创建“应用程序服务代理”时,会将它们添加到注册表中。“服务代理管理器”根据 GUID 来查找“应用程序服务代理”。每个 GUID 都表示一个实例化的“应用程序服务代理”。
ThreadPoolInvoker 类
在处理请求后,“服务代理管理器”使用 ThreadPoolInvoker 类将结果传回位于线程池线程上的“应用程序服务代理”回调方法。
消息数据管理
“消息数据管理”子系统管理存储在队列中的事务性数据,并提供将同步数据的功能添加到服务器的基础结构。该子系统由下列组件构成:
• |
队列消息 – QueueMessage 类是存储在队列中的消息的容器。 |
• |
队列管理器 – QueueManager 是“消息数据管理”子系统的一个接口,它公开了将消息添加到队列以及从队列中删除消息的方法。 |
• |
Executor – Executor 类从队列中检索消息,然后将它们发送到处理该请求的适当“联机代理”。Executor 根据 QueueMessage 中的可用信息使用反射来实例化“联机代理”。 |
• |
队列存储提供程序 – 这些类抽象了与物理队列存储区的交互。该应用程序块附带了用于 Microsoft 消息队列 (MSMQ)、Microsoft SQL Server™ 桌面引擎 (MSDE)、独立的存储区以及内存的提供程序。 |
类设计
“消息数据管理”子系统支持异步处理与应用程序相关的消息:
• |
提供一致的用户体验,无论是联机状态还是脱机状态。无论连接状态如何,“消息数据管理”都能够排列要异步执行的所有消息。当应用程序处于联机状态时,就会执行消息。 |
• |
避免阻塞运行过程较长的 UI 线程。 |
QueueMessage 类
QueueMessage 类是存储在队列中的数据的容器。Payload 是“队列消息”携带的关键元素。开发人员可以将 QueueMessage 类视作基类,并对其进行扩展以添加他们的应用程序所需的任何附加功能。
类成员
以下部分(“属性”)介绍了该类中最重要的成员。有关详细信息,请参阅该应用程序块中随附的帮助文件。
属性
MessagePayload – 返回一个类型为 Payload 的对象。Payload 包含用于处理请求的数据。它还包括有关处理请求以及返回结果的组件的元数据。
QueueManager 类
QueueManager 类是“消息数据管理”子系统的一个接口,它具有以下功能:
• |
它是一个线程安全的组件,可以在多个线程上同时被访问。 |
• |
根据应用程序的配置设置,它可以实例化适当的队列存储提供程序(通过实现 IQueueStorageProvider 接口)。 |
• |
它可以公开在队列中添加和删除消息的方法。 |
为了保持消息的顺序,Offline Application Block 可在“队列管理器”中强制使用“先进先出”(FIFO) 语义。
类成员
以下部分(“方法”和“属性”)介绍了该类中最重要的成员。有关详细信息,请参阅该应用程序块中随附的帮助文件。
方法
• |
Enqueue – 将消息添加到队列中。它可以接受类型为 Payload 的参数作为输入。该方法将 Payload 输入封装在 QueueMessage 中,然后使用指定的队列存储提供程序将其存储在队列中。 |
• |
Dequeue – 使用指定的队列存储提供程序从队列中删除和返回消息。 |
属性
Size – 返回与队列大小相对应的 System.Int32 值。
Payload 类
Payload 类是处理一个消息所需的所有信息的数据容器。它包含:
• |
要调用的“联机代理”方法的名称。 |
• |
要发送到服务器的数据。 |
• |
从服务器接收的数据。 |
• |
应接收返回数据的“应用程序服务代理”的标识。 |
在大多数情况下,该类应可以满足任何开发人员的要求,但是如有必要,可以扩展它以包含其他消息数据。
类成员
以下部分(“方法”和“属性”)介绍了该类中最重要的成员。有关详细信息,请参阅该应用程序块中随附的帮助文件。
方法
RecordFailure – Payload 类上公开的方法,它可记录在处理请求的过程中所发生的任何故障。该方法可接受 Exception 作为参数,并为 Payload 类的这个实例设置 FailureReason 值。
属性
• |
PayloadGuid – 为 Payload 类的每个实例生成的全局唯一标识符 (GUID)。应用程序可以使用此信息来关联数据;但应用程序块并没有提供支持它的任何基础结构。 |
• |
ServiceAgentGuid – 为“应用程序服务代理”的每个对象所生成的全局唯一标识符 (GUID)。当结果在处理后被传回正确的“应用程序服务代理”时,在“服务代理管理器”中使用 GUID 从注册表中检索“应用程序服务代理”。 |
• |
MethodToExecute – 包含调用特定联机代理方法的 OnlineProxyContext 的实例。 |
• |
ResultCallbackTarget – 包含将结果返回到“应用程序服务代理”的 ServiceAgentContext 的实例。 |
• |
RequestData – 包含处理创建 Payload 的请求所必需的数据。它由创建 Payload 的“服务代理”填充。 |
• |
Results – 包含处理请求的结果。它由与服务进行通信的“联机代理”填充。 |
• |
FailureReason – 返回一个类型为 System.Exception 的对象,该对象包含由于处理请求中的故障而产生的异常。 |
• |
Success – 返回一个布尔值,表示请求处理是否成功。 |
Executor 类
Executor 类从队列中检索消息。它运行在自己的线程上,每秒轮询一次队列以检查是否有新消息,并持续处理消息直到队列为空。然后,它在再次检查是否存在可用消息前停止 1 秒钟。从队列中检索一个消息后,Executor 会调用实现 ICommandProcessor 接口的类的实例上的方法,来执行该消息。
SimpleCommandProcessor 类
SimpleCommandProcessor 类实现 ICommandProcessor 接口,并通过实例化适当的“联机代理”并将结果返回到指定的结果回调目标,来使用反射执行消息。该应用程序块将 ServiceAgentManager 用作返回结果的默认目标。
IQueueStorageProvider 接口
使用队列存储提供程序,可以将消息数据存储在几种不同类型的物理存储区中。每个队列存储提供程序都必须实现 IQueueStorageProvider 接口,才能用于应用程序块。QueueManagerBuilder 类可实例化队列存储提供程序,并将其传递到 QueueManager。
通过实现这个接口并在应用程序配置文件中指定必要的配置信息,应用程序开发人员可以创建他们自己的队列存储提供程序。
尽管在配置文件中可以定义多个提供程序,但是在配置文件中只应该启用其中一个提供程序。如果配置文件中的提供程序发生更改,则该应用程序块将其视为用于存储消息数据的新提供程序。该应用程序块没有将数据从以前的存储区移到新存储区的内置功能。
该应用程序块附带四个队列存储提供程序:
• |
In Memory – 该提供程序在 InMemoryQueueStorageProvider 类中实现。它将队列数据存储在一个内存数据结构 (System.Collection.Queue) 中。当应用程序重新启动时,所存储的数据将会丢失。不建议使用该提供程序存储持久性数据。 |
• |
Isolated Storage – 该提供程序在 IsolatedStorageQueueStorageProvider 类中实现。它将队列数据存储到独立存储区中。建议使用该提供程序存储持久性数据。 |
• |
MSDE – 该提供程序在 msdeQueueStorageProvider 类中实现。它将队列数据存储在 MSDE 中。建议使用该提供程序存储持久性数据。 |
• |
MSMQ – 该提供程序在 msmqQueueStorageProvider 类中实现。它将队列数据存储在“消息队列”中。该提供程序的自定义属性位于配置文件中,并且可以定义队列的名称。建议使用该提供程序存储持久性数据。 |
参考数据管理
即使处于脱机状态,智能客户端应用程序也必须为用户提供他们继续工作所需的数据。此数据通常称为参考数据,它在本地进行缓存,并且在数据过期变旧时会进行周期刷新。
参考数据通常是静态的,但有时也可能被应用程序更改。当出现这种情况时,Offline Application Block 会将数据标记为 dirty。这样会防止应用程序块在数据过期时刷新数据,并且可以避免来自其他源的旧值覆写已更新的数据。在更新与远程服务进行同步后,数据被标记为 clean,并且可以进行刷新。
“参考数据管理”子系统可为应用程序提供基础结构,用于在本地缓存数据并在数据变旧时刷新数据。通过允许加密和解密缓存的数据,它还提供了安全存储数据的能力。
该子系统使用 Caching Application Block,它由下列组件构成:
• |
ReferenceDataCache – Offline Application Block 通过该封装程序公开带有支持联机/脱机方案附加功能的缓存行为。 |
• |
ReferenceDataDefinition – 与参考缓存中的项目相关联的元数据。它控制信息的刷新方式、信息何时过期以及维护数据的 dirty 和 clean 状态标记。 |
• |
ReferenceDataRefreshController – 当某个项目过期时,该类负责将该项目添加回缓存。它与 DataLoaderManager 协作来刷新缓存项目。 |
• |
DataLoaderManager – 该类可创建 ReferenceCacheDataPayload 并将消息添加到队列中,以便下载参考数据并将其存储到缓存中。 |
• |
ReferenceCacheDataPayload – 该类可利用对 ReferenceDataDefinition(包含更新参考缓存数据所必需的元数据)的参考,来扩展 Payload 类。 |
有关“参考数据管理”子系统如何工作的进一步说明,请参阅本章的“参考数据缓存工作流”部分。
类设计
“参考数据管理”子系统提供了用于管理处理缓存、存储和刷新数据的组件的类。
ReferenceDataCache 类
该应用程序块通过该封装程序公开带有支持脱机和联机方案附加功能的缓存行为。该类实现 IReferenceDataCache,并且该类的客户端应访问这种类型的对象。
类成员
以下部分(“方法”)介绍了该类中最重要的成员。有关详细信息,请参阅该应用程序块中随附的帮助文件。
方法
• |
Store – 使用指定的缓存项来存储参考缓存数据项目。Store 不要求缓存中存在项目。 |
• |
Update – 在缓存项指定的位置更新参考缓存数据项目。Update 要求缓存中存在项目。 |
• |
Retrieve – 对于给定的缓存项,检索参考缓存数据项目。 |
• |
Remove – 对于给定的缓存项,删除参考缓存数据项目。 |
• |
IsDirty – 返回一个布尔值,表示位于指定缓存项位置的项目是否已由应用程序修改。 |
• |
MarkAsClean – 如果参考数据缓存项目存在于缓存中,它会将该项目标记为 clean。这表示作为工作流(在指定的缓存项位置修改参考数据项目)一部分而生成的消息已经进行了处理。 |
• |
ItemHasBeenExpired – 用于将项目标记为“已过期”的方法。 |
ReferenceDataDefinition 类
该类包含与参考缓存中的项目相关联的元数据。它控制信息的刷新方式、信息何时过期以及维护数据的 dirty 和 clean 状态标记。
ReferenceDataRefreshController 类
当某个项目过期时,该类负责将该项目添加回缓存。它与 DataLoaderManager 协作来刷新队列。
该功能必须作为独立的类存在,而不是嵌入到 ReferenceDataCache 本身中,它促进了应用程序块中各个元素之间的松散耦合。使 ReferenceDataCache 依赖于 DataLoaderManager 会不必要地耦合这两个子系统。通过将该责任转移给第三类,可以消除这种依存关系,并可以使整个应用程序块保持松散耦合。为了正常工作,该类需要访问 ReferenceDataCache 和 DataLoaderManager。
DataLoaderManager 类
该类负责下载参考数据。为此,它将创建 ReferenceCacheDataPayload 并将其添加到队列中,以便可以处理消息和下载参考数据。下载的参考数据存储在缓存中。
类成员
以下部分(“方法”)介绍了该类中最重要的成员。有关详细信息,请参阅该应用程序块中随附的帮助文件。
方法
• |
LoadData – 创建 ReferenceCacheDataPayload 并将其排入队列以便异步处理,来开始下载参考数据。 |
• |
RefreshData – 创建 ReferenceCacheDataPayload 并将其排入队列以便异步处理,来刷新参考数据缓存中的过期数据。 |
ReferenceCacheDataPayload 类
该类派生自 Payload 类,同时也包含 ReferenceDataDefinition。该组件是有关更新参考缓存数据所需的参考数据的元数据。
类成员
以下部分(“方法”和“属性”)介绍了该类中最重要的成员。有关详细信息,请参阅该应用程序块中随附的帮助文件。
方法
UpdateDataToReturn – 更新 Payload 中从远程服务返回的数据。
属性
• |
DataDefinition – 返回来自创建 Payload 的参考数据缓存项目的 ReferenceDataDefinition 值。 |
• |
IsRefreshMessage – 布尔值,表示是否已创建 Payload 来刷新参考数据项目。 |
缓存块和存储提供程序
“参考数据管理”子系统使用 Caching Application Block 来存储缓存的数据。缓存块支持在配置文件中定义多个提供程序,但在配置文件中只能启用其中一个提供程序。
如果配置文件中的提供程序发生更改,则该缓存块将使用新的提供程序来存储缓存数据。该块没有将数据从以前的数据存储区移到新数据存储区的内置功能。这个功能必须由开发人员添加。
除了 Caching Application Block 附带的提供程序之外,Offline Application Block 还包括一个独立的存储缓存提供程序。这些提供程序的说明如下:
• |
IsolatedStorageCacheProvider – 将缓存数据存储在支持 .NET 的独立存储区中。当项目被物理持久保存时,它针对每个缓存项目使用一个文件。文件的名称对应于用户指定的缓存项。在 Offline Application Block 中,缓存项将用作文件名,这就为缓存项创建了一个约束:缓存项不能包含被文件系统特殊处理的符号,例如,“\”、“*”或“?”。该块可确保应用程序开发人员所使用的任何项目名称都对应于“独立存储”中的有效文件名,否则它将引发一个异常。 |
• |
Caching Application Block Providers – 包括用于 SQL Server、内存存储和内存映射文件存储的缓存存储提供程序。有关详细信息,请参阅 Caching Application Block。 |
实用工具服务
“实用工具服务”提供后台服务,例如多提供程序配置处理程序、配置程序和生成器。生成器用于创建特定类的实例。生成器使用配置程序从配置文件中读取相关信息。
MultiProviderConfigHandler 类
Offline Application Block 提供为配置文件中的单个配置区段定义多个提供程序的能力。当您管理多个类型的数据存储区并需要关联来自这些不同数据存储区的数据时,多个提供程序非常有用。在这种情况下,您需要通过编程手段在读取数据和关联数据时逐个启用或禁用存储区。这些区段中只有一个必须通过将 enabled 属性设置为 true 来启用。MultiProviderConfigHandler 类负责读取配置信息,并确保满足该条件。它实现接口 IConfigurationSectionHandler,并实现该接口强制的 Create 方法。
类成员
以下部分(“方法”)介绍了该类中最重要的成员。有关详细信息,请参阅该应用程序块中随附的帮助文件。
方法
Create – 从配置文件 (App.config) 中读取指定配置区段,并返回该类的客户端可以使用的 System.Collections.Hashtable 中的信息。
如果您计划在设计中使用多个提供程序,则使用该类可以简化您的代码。
生成器
该应用程序块中的每个关键类都具有一个关联的生成器类。生成器类抽象了从生成器类的客户端创建关联类的实例的复杂性。每个生成器类都使用 Builder 模式。
对于诸如 ConnectionManager、DataLoaderManager、ServiceAgentManager、QueueManager、Executor、ReferenceDataCache 和 DataLoaderManager 这样的类,整个应用程序只需要一个类的实例,因此可以将这些类创建为 Singleton。但是,Singleton 具有一个缺点,那就是会导致强耦合。这一点抑制了块体系结构的优点,从而鼓励了可交互、可重复使用的代码的开发,这些代码的类可单独进行测试。包含生成器类的目的在于创建特定类的实例,而不是使用 Singleton。
OfflineBlockBuilder
OfflineBlockBuilder 类是应用程序开发人员需要调用以实例化所有子系统的唯一类。它通过调用相应的生成器类来完成。它是 Singleton,您可以使用该类的 Instance 属性来访问它。它将所有关键类公开为属性。下面的列表包含了由 OfflineBlockBuilder 类创建的所有类:
• |
DataLoaderManager |
• |
ReferenceDataCache |
• |
ServiceAgentManager |
• |
ServiceAgentRegistry |
• |
ConnectionManager |
• |
PayloadConsumer(队列所公开的接口,用于支持向队列中添加项目所需的功能) |
OfflineBlockBuilder 类还支持启动和停止所有子系统的 Start 和 Stop 方法。下面列出的是该类中最重要的成员。有关详细信息,请参阅该应用程序块中随附的帮助文件。
• |
Instance – 创建块生成器的实例。 |
• |
Start – 启动块。 |
• |
Stop – 停止块。 |
• |
Dispose – 停止块并处理 OfflineBlockBuilder。 |
ConnectionManagerBuilder 类
ConnectionManagerBuilder 类可构建 ConnectionManager 类并将 ConnectionManager 作为一个属性公开。
ServiceAgentManagerBuilder 类
ServiceAgentManagerBuilder 类可构建 ServiceAgentManager 类并将 ServiceAgentManager 作为一个属性公开。此外,它还可以创建 ServiceAgentRegistry 类的一个对象,并将其作为一个属性公开。
QueueManagerBuilder 类
QueueManagerBuilder 类可构建 QueueManager 类并将 QueueManager 作为一个属性公开。通过使用 QueueManagerConfigurator 类,该类可以从配置文件中获得队列存储提供程序的信息。它可创建在配置文件中指定的队列存储提供程序的实例,并在创建过程中将其传递到 QueueManager。
ExecutorBuilder 类
ExecutorBuilder 类可构建 Executor 类并将 Executor 作为一个属性公开。
ReferenceDataCacheBuilder 类
ReferenceDataCacheBuilder 类可构建 IReferenceDataCache 接口类型,并将 IReferenceDataCache 类型对象作为一个属性公开。
DataLoaderManagerBuilder 类
DataLoaderManagerBuilder 类可构建 DataLoaderManager 类并将 DataLoaderManager 作为一个属性公开。
参考数据缓存工作流
本部分介绍使用 Offline Application Block 来下载、管理、刷新和缓存参考数据的工作流。
下载参考缓存数据
如前所述,若要使应用程序在脱机状态下工作,必须下载参考数据并将其缓存到客户端上。下载参考数据是由应用程序启动的。根据连接状态,应用程序可以决定是否启动下载参考数据项目。在这方面,应用程序块不会强制执行任何操作。
协作下载参考数据的组件包括由应用程序开发人员创建的“应用程序服务代理”、应用程序块中提供的 DataLoaderManager 类以及“消息数据管理”子系统。下列步骤演练了图 2.6 中所示的过程。
应用程序创建“应用程序服务代理”的一个实例。“应用程序服务代理”为参考缓存数据项目创建一个 ReferenceDataDefinition 对象。“应用程序服务代理”指定与缓存项目相对应的项、可选的过期策略以及 OnlineProxyContext 和 ServiceAgentContext。 “服务代理”实例化 DataLoaderManager,并为它提供对应于需要下载的参考缓存数据项目的 ReferenceDataDefinition。 DataLoaderManager 使用 ReferenceDataDefinition 创建 ReferenceCacheDataPayload 的一个实例,并使用“信息数据管理”子系统将其排入队列以进行异步处理。 “消息数据管理”子系统的消息处理会导致从远程服务检索参考缓存数据项目。“联机代理”将参考数据添加到参考缓存中。 结果被返回给特定的“应用程序服务代理”。
图 2.6.排队参考数据加载请求
刷新参考缓存数据
下载的参考缓存数据会在一段时间后变旧。该应用程序块可提供为每个参考缓存数据项目指定过期策略的功能。根据该过期策略,应用程序块可刷新参考缓存数据项目。当参考缓存数据项目过期后,该应用程序块会根据 Caching Application Block 的 CacheManager 组件引发的事件来启动刷新。
协作刷新参考缓存数据的组件包括 CacheManager、ReferenceDataCache、ReferenceDataRefreshController 和 DataLoaderManager。
当参考缓存数据项目下载并添加到缓存中时,如前一部分“下载参考缓存数据”中的说明,您可以在参考数据定义中为缓存项目指定过期策略。除此之外,当某个项目过期时,Caching Application Block 还允许指定回调方法(委派)。当参考缓存数据项目存储在缓存中时,如果项目过期,ReferenceDataRefreshController 类的 CacheItemExpiredCallback 方法被指定为回调方法。图 2.7 展示了说明下列步骤的过程:
• |
CacheManager 监视缓存项目是否过期。当某个项目过期时,缓存管理器将调用的 ReferenceDataRefreshController 的指定 CacheItemExpiredCallback 方法。 |
• |
如果缓存项目已经过期,则基础 Caching Application Block 将从缓存中删除该项目。在启动下载前,ReferenceDataRefreshController 将该项目添加回缓存中 — 这样做可防止信息丢失。 |
• |
然后,ReferenceDataRefreshController 通过 DataLoaderManager 来启动下载参考数据项目。 |
• |
DataLoaderManager 使用 ReferenceDataDefinition 来创建 ReferenceCacheDataPayload 的实例。然后,它使用“消息数据管理”子系统的类 QueueManager 将 ReferenceCacheDataPayload 的对象作为消息排入队列以进行异步处理。 |
• |
“消息数据管理”子系统的消息处理会导致从远程服务检索参考缓存数据项目。“联机代理”将参考数据添加到参考缓存中。 |
• |
结果被返回给特定的“应用程序服务代理”。 |
图 2.7.缓存项目过期并获得刷新
根据设计,在“参考数据缓存”中刷新参考数据时,Offline Application Block 不会将事件提交到应用程序。如下面的说明所示,添加该功能是一项简单的任务。
当完成服务请求以及当(且仅当)相同的“服务代理”可以由 Offline Application Block 回调时,请记住将事件提交到应用程序。如果在刷新缓存中的数据时发生相同的情况,则必须创建一个长期的“服务代理”并在每次刷新数据时使用,而且还必须使该“服务代理”可用于该应用程序,以便它可以注册接收这些完成事件。该实现类似于 FailsafeServiceAgent 实现。
管理参考缓存数据
要管理参考缓存数据,您必须以某种方式对其进行标记,以便知道该数据是否需要刷新。数据可以标记为 dirty、expired 或者两者。根据标记数据的方式,参考缓存处理数据的方式可能会更改。
如果数据为 dirty,则意味着它已经在本地进行更改,但没有在服务器上进行更改。一旦在服务器上进行更新,数据会标记为 clean。请记住,我们没有办法知道消息何时在服务器上进行实际更新。它也可能在计划批作业完成之后发生。同样,如果数据更改过几次,则直到发送所有更新请求之后,它才会标记为 clean。
数据还可以在参考缓存中标记为 expired。这意味着“缓存管理器”已经确定缓存中的某个项目已经过期,导致“缓存管理器”将其从缓存中删除并对有关该过期项目的参考数据执行一个回调方法。
理解参考缓存中的“脏”(dirty) 数据如何与“过期”(expired) 数据进行交互非常重要。
• |
既不是“脏”又不是“过期”的数据被视为干净的数据。如果该数据过期,它就会被刷新。 |
• |
如果已标记为 expired 的数据再次过期,将不会为其执行另一次刷新操作。 |
• |
如果数据过期且标记为 dirty,则在参考缓存中将数据标记为 clean 之前,将不会刷新该数据。原因是,使数据在本地变脏的操作为数据提供了比刷新后的数据更好、更新的值。因此,刷新消息的结果可以忽略。 |
缓存参考数据
Caching Application Block 允许您为缓存的项目指定特定的元数据。这包括过期策略以及当项目过期时的回调方法。Offline Application Block 必须支持其他元数据,才能指定组件的上下文信息。当某个项目过期时,这些组件会检索参考数据。创建 ReferenceDataDefinition 类以存储扩展的元数据。ReferenceDataDefinition 类还包含一个到实际参考缓存项目的项。除了在需要时传递该信息的能力外,当您需要检查有关参考缓存项目的元数据时,它还提供了一个优化,这是因为不需要反序列化整个缓存项目。
当某个项目过期时,Caching Application Block 会在通知应用程序之前从缓存中永久性的删除该项目。这对于以脱机模式运行的应用程序来说有几个含义。如果删除了缓存的项目,应用程序就无法使用缓存中的数据来继续操作。由于 ReferenceDataDefinition 和缓存项目都被缓存,如果它们两者都从缓存中被删除,就无法检索它们并将它们添加回参考缓存数据中。要克服这一限制,可以在缓存的存储架构中添加一个间接层。
使用该架构,过期在第一个间接层上指定。当该项目过期时,将出现一个带有该项的值的通知。然后,为 ReferenceDataDefinition 和参考缓存项目自动生成项。这样,应用程序就可以继续访问它们。ReferenceDataRefreshController 接收通知并使用 DataLoaderManager 来刷新数据。DataLoaderManager 组件在开始下载前添加第一级架构。
Caching Application Block 已经提供了内存缓存、内存映射文件缓存以及 SQL 数据库缓存。Offline Application Block 构建在该基础结构上,通过包括独立存储缓存提供程序来提供缓存支持。该提供程序允许应用程序将特定于不同用户的数据存储在运行智能客户端应用程序的客户端计算机上的不同位置。
Caching Application Block 支持对缓存数据进行加密和解密。Offline Application Block 利用该功能根据配置信息来确保缓存数据的安全。
请求和响应往返
本部分说明了在使用 Offline Application Block 的应用程序中,请求和响应的往返过程是如何实现的。下列步骤说明了图 2.8 中阐释的往返行程。
• |
应用程序的“用户界面控制器”(UIC) 通常会创建一个执行该任务的特定“应用程序服务代理”的实例。“应用程序服务代理”将创建 Payload 以便执行该任务。Payload 包含执行该任务所需的所有数据。此外,Payload 还包含有关 OnlineProxyContext 和 ServiceAgentContext 中组件的所有上下文信息。 |
• |
使用 QueueManager 类的 Enqueue 方法,“应用程序服务代理”可将 Payload 排入队列。在使用已配置的队列存储提供程序将 Payload 存储到队列之前,Enqueue 方法将 Payload 封装在 QueueMessage 类中。 |
• |
如果应用程序处于联机状态,Executor(运行在独立线程上)将使用 QueueManager 类的 Dequeue 方法从队列中获取 QueueMessage。然后,它从 QueueMessage 中检索 Payload。 |
• |
Executor 从对应于“联机状态”的 Payload 中检索 OnlineProxyContext。通过反射使用 OnlineProxyContext 信息,它可执行指定的“联机代理”方法。“联机代理”与远程服务进行交互以完成该操作。“联机代理”还将缓存结果、执行所有请求后期处理,并处理公共事务。 |
• |
在执行请求后,“联机代理”将结果填充到 Payload 中。然后,Executor 将 Payload 返回到 ServiceAgentManager。 |
• |
ServiceAgentManager 类从 Payload 中检索对应于“应用程序服务代理”实例的 GUID。使用 GUID,它可以在服务代理注册表中找到“应用程序服务代理”。如果有对应于 GUID 的“应用程序服务代理”(原始“应用程序服务代理”),ServiceAgentManager 会检索原始的“应用程序服务代理”以返回结果;否则,FailsafeServiceAgent 类将用作“联机代理”执行过程中处理错误的最后手段。 |
• |
“服务代理管理器”将检索“应用程序服务代理”,然后使用 Payload 中的 ServiceAgentContext 信息来调用回调方法。该调用会在其他线程上进行。“应用程序服务代理”将适当地处理结果。 |
图 2.8.请求和响应往返
应用程序设计注意事项
到目前为止,本章已经阐述了 Offline Application Block 的设计。本部分介绍有关智能客户端应用程序设计的注意事项。
服务代理的生命周期
“应用程序服务代理”是生存期很长的对象,这意味着它们至少可以在消息到其远程业务功能或服务的往返时间内存在。(如果关闭应用程序,则内存中的“应用程序服务代理”会被刷新。)每个“应用程序服务代理”都会自动向“服务代理注册表”进行注册,并由唯一的 GUID 标识。这个 GUID 用于将来自远程业务功能(服务)的响应返回到原始的“应用程序服务代理”。如果原始的“应用程序服务代理”(由其唯一的 GUID 标识)不再可用,则“服务代理管理器”将使用 FailsafeServiceAgent 来报告错误。
在配置文件中定义多个提供程序
在配置文件中,您可以为单个配置区段定义多个提供程序。提供程序区段下面未启用的提供程序或元素可以选择性地在配置中使用 enabled="false",或者它们可以完全忽略已启用的属性。确保只启用一个提供程序的算法封装在 MultiProviderConfigHandler 类中。要在运行时允许动态更改配置文件,您可以使用 Configuration Management Application Block。
有关详细信息,请参阅第 4 章中有关配置文件的部分。
线程
该块可在其子系统中创建几个内部线程。它们包括:
• |
一个从应用程序代码中异步处理已排队消息的线程。 |
• |
一个轮询连接状态更改并通知已注册的组件状态发生更改的线程。 |
重要的是,从这些线程的其中一个调用的应用程序代码不能在该调用中进行长时间处理,以避免延迟块代码。例如,如果应用程序的连接状态发生更改,“连接管理器”将调用已注册对该类的 ConnectionStateChangedEvent 关注的任意类。该调用在“连接管理器”的连接检测线程中进行,因此由客户端开始的任何长时间处理都会干扰“连接管理器”监视是否存在后续的状态更改。一般来说,事件处理代码应快速运行,并且不应回调到块代码中。
数据协调
该块不处理数据协调。这是客户端代码或远程业务逻辑的责任。除了在“参考数据管理”部分中所述的“脏”或“过期”处理外,来自远程业务逻辑的结果将在本地覆写所有可用的信息。例如,如果应用程序更新了某个客户的电话号码,那么该更改会排入队列,之后发出请求,然后收到包含操作成功通知的结果。但是,如果最终在服务器端更新该信息需要几小时或几天时间,那么就存在这样的风险:在此期间向服务器发出的获取客户电话号码的请求可能会返回旧值。这已经超出了该块的范围。开发人员必须在应用程序代码或远程业务逻辑中提供解决方案。
在独立的进程中运行组件
Offline Application Block 设计为完全在单个进程中运行。但是,块中的某些组件可能会在多个进程中运行。
Executor 是最佳选择。您可以将其作为一个 Windows 服务运行,当应用程序本身关闭时允许它为挂起的消息队列提供服务。以下步骤介绍了实现方法:
• |
在生成器类中为 Executor 组件更新创建逻辑。同时还必须更新主生成器类。 |
• |
消息经过处理后返回的结果必须在联机和脱机方案中处理(将其返回到应用程序)。实现这一点可以采取几种不同的方法:您可以创建一个共享位置(例如缓存),将应用程序的结果存储在其中。您还可以获取它们或使用某些内部处理通信机制(例如远程处理),将结果返回应用程序。 |
• |
“参考数据管理”和“消息数据管理”子系统必须能够用于 Executor,以便能够处理消息并更新缓存。这会影响到与应用程序一起使用的队列和缓存存储提供程序,以及子系统本身的设计。 |
通过在独立的进程中运行块的一些组件,可以获得较高的效率,包括为多个可能相关的应用程序创建共享的缓存和队列,以及创建同步消息数据和下载/刷新参考缓存数据的通用服务。但是,您还应慎重考虑某些问题,例如缓存架构、组件之间的通信以及任何与争用相关的问题。
其他设计
Offline Application Block 的设计可使用异步行为来执行消息。异步行为可防止在 UI 线程上执行长时间操作,这样就提供了更好的用户体验。
在某些情况下,如果网络可靠并具有很高的带宽,您可以考虑其他设计,例如,仅当应用程序脱机时,才会发生“应用程序服务代理”将 Payload 排入队列的情况。一旦应用程序再次联机,它将绕过 Offline Application Block 子系统而直接调用“联机代理”。在处理请求后,“联机代理”会将结果返回到“应用程序服务代理”。这是 UI 线程上的同步调用,如果它是长时间运行的操作,则不建议使用,因为它可能会冻结 UI。您可以通过调用其他线程(而不是 UI 线程)来进行优化,并使用 .NET 中提供的异步支持。
提示 要在“服务代理”中避免使用 if-else 语句,您可以使用状态模式来根据状态作出有关处理的决定。
小结
Offline Application Block 的设计为满足应用程序以脱机模式工作的需要提供了核心基础结构。它由下列子系统组成:
• |
连接状态管理 |
• |
服务代理管理 |
• |
消息数据管理 |
• |
参考数据管理 |
• |
实用工具服务 |
“连接状态管理”子系统的主要角色是检测连接状态,并通知侦听器有关任何状态的变化。
“服务代理管理”子系统可实现下列功能
• |
维护“应用程序服务代理”的注册表 |
• |
包含负责在处理请求后将结果返回给“应用程序服务代理”的组件 |
• |
提供应用程序开发人员用于实现应用程序服务代理所必需的“服务代理”基类 |
“消息数据管理”子系统管理事务性数据,并支持异步处理与应用程序相关的消息。
“参考数据管理”子系统可为应用程序提供基础结构,用于在本地缓存数据并在数据变旧时刷新数据。通过允许加密和解密缓存的数据,它还提供了安全存储数据的能力。
“实用工具服务”子系统管理连接性和非池线程。
转到原英文页面