Export (0) Print
Expand All
This topic has not yet been rated - Rate this topic

Smart Client Offline Application Block

Retired Content
This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.
 

patterns & practices Developer Center

Design of the Offline Application Block

Microsoft Corporation

February 2004

Summary: Chapter 2 discusses the design of the offline application block.

Contents

Offline Challenges

Solution Description

Design Goals

Offline Application Block Design

Offline Application Block Subsystems

Connection State Management

Service Agent Management

Message Data Management

Reference Data Management

Utility Services

Reference Data Cache Work Flow

Request and Response Round Trip

Application Design Considerations

Summary

The Offline Application Block builds on the capabilities of the .NET Framework and the encapsulating smart client application to help users perform tasks when offline as easily and efficiently as they can perform them online. This chapter describes the design of the Offline Application Block, its capabilities, and the decisions that went into its design.

Offline Challenges

Developers of smart client applications must resolve many issues when designing a program that will operate both online and offline. Questions that typically arise include:

  • How does the application determine if it is online or offline?
  • If the connection can change at unpredictable times, how should the application components that depend upon the connection state be notified?
  • How and where should the application store data locally so that it can be accessed while offline? Can that data become stale? When should it be refreshed?
  • Should the application behave differently when it does not have access to all the requisite data or services?
  • How and where should transactional data (message data) be stored while the application is offline?
  • How should transactional data be synchronized with the server when the application goes from offline to online?

Solution Description

The Offline Application Block provides the basic functions required by smart client applications with offline capabilities. Its essential features include:

  • Detecting the presence or absence of network connectivity.
  • Notifying all registered components when the connection state changes.
  • Downloading and caching the reference data that allows the application to function when the network connection is not available.
  • Storing message data locally while the application is offline.
  • Synchronizing message data with the server when the network connection becomes available.

Design Goals

The Offline Application Block is intended to serve as an architectural guideline for developers who want to include offline capabilities in their applications. The design goals of the block are to:

  • Provide loose coupling between components.
  • Abstract the management of connection state from the application.
  • Provide the same programming model for the application in both online and offline modes so that the application transitions between them without affecting the user experience.
  • Provide extensible interfaces for capabilities such as connection detection, and queuing.
  • Incorporate design patterns.

Design Considerations

Block architecture promotes the use of reusable, encapsulated components of code. Ideally, these components are designed to be both interoperable and modular so that code reuse is straightforward. Referencing a block in an application provides the application with that block's capabilities.

The Offline Application Block design provides the logical and physical building blocks necessary for developers to incorporate offline capabilities into their applications. The ability to successfully support offline operations is not obtained by simply adding the block and tweaking an existing application. This block was designed as an example of recommended practices—the supplied code merely illustrates possible solutions. Careful planning at the design stage can ensure that your Offline Application Block application is both reusable and extensible.

Patterns

Patterns are reusable solutions to recurring design problems that a programmer may encounter in similar contexts. Several patterns were used in the design of the Offline Application Block and are described in Table 2.1.

Table 2.1: The Types of Patterns used by the Offline Application Block

PatternDescription
BuilderSeparates complex creational logic from the application logic of the created components. Allows clients of the application classes to remain isolated from the process of creating the block components.
FactoryModels an interface for creating objects that at creation time lets the subclasses choose which class to instantiate.
ObserverUses a one-to-many relationship between objects for efficient and effective notification. When the first object changes, all dependent objects are immediately notified and updated automatically.
SingletonProvides a single, global point of access to a service. A singleton can be used to allow only one instance of a particular class to be created, and provide access to that single instance wherever it is needed.
StrategyProvides the ability to define a family of similar algorithms and an interface through which they can be accessed. The particular algorithm implementation used can be changed without client classes being changed as well.

Offline Application Block Design

This section explains the processes and architecture of the major subsystems that make up the Offline Application Block. For a detailed explanation of how the major subsystems work together, see the section, "Request and Response Round Trip," in this chapter.

Note   This section provides a look at only the most critical methods and properties in each of the subsystem's classes. For more information about these classes and their members, refer to the Help file that ships with this block. It is located in your <installation directory>\Offline\Docs\ folder.

Figure 2.1 illustrates the physical elements that make up the Offline Application.

Ff650491.f02offline01(en-us,PandP.10).gif

Figure 2.1. Physical view of the Offline Application Block

Table 2.1 describes each of the components in Figure 2.1. These components are discussed in detail in this chapter.

Table 2.2 Smart Client Application Elements with their Subsystems and Descriptions

ElementsSubsystemDescription
Connection Detection StrategyConnection State ManagementDetects the current state of the physical connection.
ConnectionManagerConnection State ManagementManages connection state services related to physical network access. Uses pluggable connection detection components (by implementing the IConnectionDetectionStrategy interface) to determine the connection state. The ConnectionManager fires an event when the Connection Detection Strategy notifies it that a connection state change has occurred. The ConnectionManager also has public methods that the application may call to manually change the online/offline state of the application.
ExecutorMessage Data ManagementWhen online, takes messages off the queue and calls the Online Proxy responsible for sending them to the remote service. Additionally, sends responses from the remote service back to the application.
Queue StorageMessage Data ManagementProvides the data store used to hold message data and operations to be delivered to the server when the application is again online.
QueueManagerMessage Data ManagementBehaves as a façade for the Queue Storage Providers. It provides methods for enqueuing and dequeuing the messages.
DataLoaderManagerReference Data ManagementProvides a facility to allow the application to request reference data to be downloaded at an appropriate time for use during operations.
ReferenceDataCacheReference Data ManagementBehaves as a façade for the Reference Data Management subsystem. Responsible for ensuring that data required by the application is stored and can be accessed locally (cached) while the application is offline.
Cache BlockReference Data ManagementProvides an implementation of physical cache operations.
Application Service AgentService Agent ManagementProvides the ability to queue messages. It also provides a channel for getting results back to the application.
Online ProxyService Agent ManagementClass created by the application developer that has the responsibility of communicating with the remote service that provides the business capability. The Online Proxy also has the responsibility of storing the reference data in the cache if required.
ServiceAgent Service Agent ManagementProvides the base class implemented by all application-supplied Service Agents. The Service Agent base class is responsible for registering the service agent with the service agent registry.
ServiceAgentManagerService Agent ManagementThe Service Agent Manager returns the results back to the appropriate Service Agents after processing.

Illustrative Scenario

The following scenario shows how the actions taken by a user translate into the actions that occur within the smart client application. For an overall view of how the elements that make up the Offline Application Block fit together, refer back to Figure 2.1

Preparing for Offline Work

David is connected to the network and is running the application he uses to fill out the insurance claims for his region. He is scheduled to go on the road to follow up on his claims. David clicks the Download Work Items button to download all the claims that he needs to complete.

Application Action

At runtime, the application makes method calls to the appropriate Application Service Agents to download data. The Application Service Agent in turn calls the DataLoaderManager for initiating the download. The DataLoaderManager calls the QueueManager and puts the data download request in the queue. If the system is online, the Executor picks up the request and processes it by creating an instance of OnlineProxy through reflection and by calling the specified method. The Online Proxy then communicates with the remote server and gets the required data. It stores the data in the cache on the local computer by using the ReferenceDataCache. The Executor then calls the Service Agent Manager to return the results. Next, the Service Agent Manager gives a callback to the specific Application Service Agent. Finally, the Application Service Agent raises an event to inform the application about the downloaded data.

Figure 2.2 illustrates the process that the application uses to download reference data.

Note   The Offline Application Block reserves part of the cache and names it Reference Data Cache. This cache only manages reference data. You can create a new cache with a data store designed for your organization.

Ff650491.f02offline02(en-us,PandP.10).gif

Figure 2.2. The portions of the block involved in downloading the reference data

Working Offline

David takes the computer on the road. When he accesses the application again, the computer is not connected to a network, and the application starts in offline mode. David completes the claims during the day in consultation with his clients.

Application Action

The application registers with the ConnectionManager to receive connection state change events. The ConnectionManager uses the ConnectionDetectionStrategy to determine that the computer is offline and fires an event to notify all the registered components.

Note   Components must register with the Connection Manager to be notified of state change events.

Figure 2.3 illustrates the process that the application uses to detect connectivity with the network.

Ff650491.f02offline03(en-us,PandP.10).gif

Figure 2.3. The components of the block involved in taking the process offline

Completing Claims

David opens a claim in the application, completes the claim, and clicks the Submit button.

Application Action

Internally, the application calls Application Service Agent to process the work item. The Service Agent creates a Payload that includes the work item. After creating the Payload, the Service Agent calls the Queue Manager to store the message data in the queue. The QueueManager wraps the Payload in a QueueMessage and persists it in the queue.

Figure 2.4 illustrates the process that the application uses to complete the claim when offline.

Ff650491.f02offline04(en-us,PandP.10).gif

Figure 2.4. The portions of the block involved in completing a claim when offline

Synchronizing Offline Data

David returns to his office, connects the computer to the network, and starts the application.

Application Action

As soon as the connection state changes to online, the Executor is activated on a separate thread. The Executor then begins retrieving the queue messages off the queue. For each QueueMessage, the Executor calls the specified method of the OnlineProxy (this is an object, created using reflection, that uses the onlineProxyContext information contained in a Payload) to submit the work item information to the remote service for processing. The remote service, after processing the work item, returns the results back to the Executor.

The Executor then returns the Payload containing the results to the Service Agent Manager, which in turn looks for Application Service Agent in the ServiceAgentRegistry. It sends the response to this Application Service Agent. The Application Service Agent informs the user interface when the data is updated. All of the claims are synchronized transparently.

If the message execution fails and the original Application Service Agent is no longer available, the Service Agent Manager sends the response to the FailsafeServiceAgent.

Figure 2.5 illustrates the steps that the Service Agent Management subsystem performs.

Ff650491.f02offline05(en-us,PandP.10).gif

Figure 2.5. The portions of the block involved in adding offline data

Offline Application Block Subsystems

A classic definition of a subsystem is that it is a set of independent classes working together to accomplish a specified goal. The Offline Application Block is best understood by partitioning the block into the following logical subsystems:

  • Connection State Management
  • Service Agent Management
  • Message Data Management
  • Reference Data Management
  • Utility Services

The Connection State Management subsystem monitors the online/offline state of an application and notifies registered listeners. Connection State Management handles all connection state tasks (those concerned with detecting a physical connection) and can be extended for detecting and connecting to a service and detecting when a service is not available.

The Service Agent Management subsystem controls Application Service Agents and interfaces with the Executor class.

The Message Data Management and Reference Data Management subsystems work together to ensure that data is available when an attempt to access a data item first occurs.

The Utility Services subsystem manages connectivity and non-pool threads—this means that thread management is transparent to the developer.

The following sections give detailed information about each subsystem.

Connection State Management

The primary role of the Connection State Management subsystem is to detect the connection state and notify listeners of any state changes. Developers who want to extend their smart client applications to include offline capabilities may want to design their application to have different behavior for online and offline work. Whether an application is online or offline is determined by one of two factors:

  • The presence of general networking—does the application have the ability to reach remote resources and services?
  • An explicit directive from the application user. Applications may allow the user to set the connection state to offline, even when factor 1 indicates that the application is online.

Class Design

The Connection State Management subsystem includes classes that manage connection detection and notification services, as well as thread management components. These classes include the ConnectionManager class and the ConnectionDetector class.

ConnectionManager Class

The ConnectionManager class is an interface to the Connection State Management subsystem and is an example of the observer pattern. It is responsible for managing detection of connection state changes, and for notifying the appropriate objects when a change occurs. It also provides methods for forcing the application offline or online.

Class Members

The following subsection, "Methods," describes the most important members in this class. For specific information, see the Help file included with this application block.

Methods

  • GoOffline–Forces the system offline.
  • GoOnline–Forces the system online.

ConnectionDetector Class

The ConnectionDetector class manages the state transitions that occur, as detected by the derived IConnectionDetectionStrategy interface implementation. The IConnectionDetectionStrategy interface provides detection capability for the current state of the physical connection, whereas the ConnectionDetector class provides detection capability for state changes.

IConnectionDetectionStrategy Interface

The Connection State Management Subsystem supports different connection detection implementations (also known as providers) to detect the presence or absence of a connection. This subsystem uses the strategy pattern to allow the connection detection algorithm to be changed without changing application code. Each connection detection provider must implement the IConnectionDetectionStrategy interface so that the provider can be used with the application block.

Application developers can create their own connection detection strategy by implementing this interface and specifying the necessary configuration information in the application's configuration file.

Although multiple strategies can be defined in the configuration file, only one of the strategies should be enabled in the configuration file.

The block ships with a connection detection provider called WinInetDetectionStrategy that uses the Windows networking API (WinInet) for detecting the presence or absence of the network. For more information, see the Help file that accompanies this guide.

Service Agent Management

The Service Agent Management subsystem does the following:

  • Maintains a registry of Application Service Agents
  • Contains the components responsible for passing the results back to the Application Service Agent after a request has been processed
  • Provides a Service Agent base class that the application developer must use to implement application service agents

Designing a Service Agent for the Offline Application Block

As discussed in Chapter 1, the Offline Application Block uses the service-oriented approach to provide offline capabilities to a smart client application. With this approach, the Service Agent has the ability to work with different types of remote services that provide various business capabilities. These business capabilities are exposed on the server side as Web services. The Service Agent also needs to interact with the Cache Management, Queue Management, and Message Data Management subsystems.

Because of these interactions, a Service Agent may make several roundtrips among the subsystems. You can design a Service Agent with different levels of granularity at the task level. You can also add granularity when the Service Agent communicates with remote services. The approaches can include:

  • A Service Agent that interacts with a single task. For example, it can create a customer.
  • A Service Agent that interacts with all the tasks related to an entity. For example, it can perform create, read, update, and delete operations on the customer entity.
  • A Service Agent that works with only one specific service
  • A Service Agent that works with multiple services

In the design of the Offline Application Block, the responsibilities of a Service Agent have been factored into two classes. They are:

  • ServiceAgent–This class performs any tasks locally, within the block. These tasks can include creating and queuing the Payload, updating the local cache or retrieving all the data necessary to satisfy the request from the local cache. When designing a new offline block, developers should derive a new Application Service Agent class from the ServiceAgent class provided by the Offline Application Block.
  • OnlineProxy–This class abstracts the capability to interact with the service providing the business capability. Application developers must create their own Online Proxy.

The reasons for using two classes (rather than a single class) is that the Offline Application Block needs to create and use proxies as and when needed to communicate with the Web service. For this the proxies need to be stateless.

Class Design

The Service Agent Management Subsystem includes classes that take responses from the Executor and pass results back to the Application Service Agent. If there is an error and the original Application Service Agent is not available, the error is passed to Failsafe Service Agent.

ServiceAgent Class

The Offline Application Block provides a ServiceAgent class. This base class abstracts the common behavior and data for all Application Service Agents.

The most important responsibility of the ServiceAgent base class is to register all instances of Application Service Agent with the ServiceAgentRegistry. (This process is abstracted and placed in the Service Agent base class to increase the reusability of the code.) The Service Agent Registry is used during message processing to return the success or failure of a transaction back to the originating Application Service Agent. This registration is done transparently to all derived classes in the constructor of the base class.

Class Members

The following subsection, "Properties," describes the most important member in this class. For specific information, see the Help file included with this application block.

Properties

GUID–A globally unique identifier (GUID) for each instance of the ServiceAgent class. The GUID uniquely identifies the specific Application Service Agents in the ServiceAgentRegistry.

FailsafeServiceAgent Class

A design goal of the Offline Application Block was to return the results of an Online Proxy execution to the original Application Service Agent, if that original Application Service Agent were still in existence. There are several reasons why the original agent may not exist:

  • The application may explicitly remove it from the system.
  • The application may be closed and reopened.

Whenever an Application Service Agent is destroyed, any errors from the Online Proxy are lost because the original service agent is no longer there to receive them. In certain circumstances, this could also result in data loss. To prevent this, the block provides FailsafeServiceAgent.

The FailsafeServiceAgent class, which is entirely generic, acts as the destination of last resort for errors that occur during Online Proxy execution. It is not meant to be the default Service Agent through which errors normally return. If all of the following events occur, the results are returned to the FailsafeServiceAgent:

  • The block executes an Online Proxy in the Executor.
  • That Online Proxy fails.
  • The original Application Service Agent cannot be found.

For this fallback mechanism to work, the application must:

  • Use only the instance of FailsafeServiceAgent that is retrieved from the OfflineBlockBuilder.
  • Always maintain a callback method registered with the ErrorEvent of the FailsafeServiceAgent class.

ServiceAgentContext Class

When the Service Agent Manager calls a method of the Application Service Agent, it retrieves the appropriate method to call from the ServiceAgentContext class. The Payload (defined in the section, "Message Data Management," in this chapter) carries the Service Agent context for each request.

Class Members

The following subsection, "Properties," describes the most important member in this class. For specific information, see the Help file included with this application block.

Properties

MethodToInvoke–Returns the method name to be invoked. The Service Agent Manager invokes this method of the Application Service Agent to pass back the results of the request.

OnlineProxyContext Class

The OnlineProxyContext class encapsulates the information necessary to invoke the remote service request behavior of an Online Proxy. The Payload (defined later in the Message Data Management section) carries the Online Proxy context for each request.

ServiceAgentManager Class

The ServiceAgentManager returns the results of the service request to the appropriate Application Service Agent. The agent is identified by its GUID. The ServiceAgentManager uses the GUID to get the instance of the specific Application Service Agent from the Service Agent Registry—it calls the specified method of the Application Service Agent. Each time an Application Service Agent is instantiated, it gets a new GUID.

ServiceAgentRegistry Class

The ServiceAgentRegistry class acts as a repository for currently active Application Service Agents. The Application Service Agents are added to the registry when they are created. The Service Agent Manager performs a look-up for Application Service Agent by GUID. Each GUID represents an instantiated Application Service Agent.

ThreadPoolInvoker Class

After a request has been processed, the Service Agent Manager uses the ThreadPoolInvoker class to pass back the results to the Application Service Agent callback method on a thread pool thread.

Message Data Management

The Message Data Management subsystem manages transactional data stored in a queue and provides the infrastructure for adding the ability to synchronize data to the server. The subsystem is comprised of the following components:

  • Queue Message–The QueueMessage class is a container for the message stored in the queue.
  • Queue Manager–The QueueManager is an interface to the Message Data Management subsystem, exposing methods to add messages into the queue and to remove messages from the queue.
  • Executor–The Executor class retrieves messages from the queue and sends them to the appropriate Online Proxy that processes the request. The Executor uses reflection to instantiate the Online Proxy based on the information available in the QueueMessage.
  • Queue Storage Providers–These classes abstract the interaction with physical queue stores. The block ships with providers for Microsoft Message Queuing (MSMQ), Microsoft SQL Server™ Desktop Engine (MSDE), isolated storage, and in-memory.

Class Design

The Message Data Management subsystem supports processing messages asynchronously with respect to the application to:

  • Provide a consistent user experience, whether online or offline. The Message Data Management queues all messages to be executed asynchronously regardless of the connection state. The messages are executed when the application is online.
  • Avoid blocking of the UI thread for long running processes.

QueueMessage Class

The QueueMessage class is a container for the data stored in the queue. The Payload is the key element that the Queue Message carries. Developers can treat QueueMessage class as a base class and extend it to add any additional capabilities needed by their applications.

Class Members

The following subsection, "Properties," describes the most important member in this class. For specific information, see the Help file included with this application block.

Properties

MessagePayload–Returns an object of the type Payload. The Payload contains the data for processing the request. It also contains metadata about the components that process the request and return the results.

QueueManager Class

The QueueManager class is an interface for the Message Data Management subsystem, and has the following functionality:

  • It is a thread-safe component that can be accessed simultaneously on multiple threads.
  • It instantiates the appropriate queue storage provider (by implementing the IQueueStorageProvider interface) based on the configuration setting for the application.
  • It exposes methods to add and remove messages from the queue.

To preserve the order of messages, the Offline Application Block enforces First In /First Out (FIFO) semantics in the Queue Manager.

Class Members

The following sybsections, "Methods" and "Properties," describe the most important members in this class. For specific information, see the Help file included with this application block.

Methods

  • Enqueue–Adds a message to the queue. It accepts a parameter of type Payload as the input. The method wraps the Payload input in a QueueMessage and stores it in the queue using the specified queue storage provider.
  • Dequeue–Removes and returns a message from the queue by using the specified queue storage provider.

Properties

Size–Returns the System.Int32 value corresponding to the size of the queue.

Payload Class

The Payload class is a data container for all information needed to process a message. It contains:

  • The name of the Online Proxy method to call.
  • Data to be sent to the server.
  • Data received from the server.
  • The identity of the Application Service Agent that should receive the returned data.

In most cases, this class should be sufficient for any developer's purposes, but, if necessary, it can be extended to contain additional message data.

Class Members

The following subsections, "Methods" and "Properties," describe the most important members in this class. For specific information, see the Help file included with this application block.

Methods

RecordFailure–Method exposed on the Payload class to record any failures that occurred during the processing of the request. This method accepts an Exception as the parameter and sets the FailureReason value for this instance of the Payload class.

Properties

  • PayloadGuid–Globally unique identifier (GUID) generated for each instance of the Payload class. The application can use this information to correlate data; however the application block does not provide any infrastructure to support it.
  • ServiceAgentGuid–Globally unique identifier (GUID) generated for each object of the Application Service Agent. This is used in the Service Agent Manager to retrieve the Application Service Agent from the registry when the results are being passed back to the correct Application Service Agent after processing.
  • MethodToExecute–Contains the instance of the OnlineProxyContext that calls the method of the specific online proxy.
  • ResultCallbackTarget–Contains the instance of the ServiceAgentContext that returns results to the Application Service Agent.
  • RequestData–Contains the data required for processing the request for which the Payload was created. This is populated by the Service Agent that created the Payload.
  • Results–Contains the results of processing the request. It is populated by the Online Proxy that communicates with the service.
  • FailureReason–Returns an object of type System.Exception that contains the exception generated because of the failure in processing the request.
  • Success–Returns a Boolean value indicating whether or not the processing of the request was successful.

Executor Class

The Executor class retrieves messages from the queue. It runs on its own thread, polling the queue for new messages every second and continuing to process the messages until the queue is empty. It then sleeps for one second before checking for available messages again. After retrieving a message from the queue, the Executor calls a method on an instance of the class implementing the ICommandProcessor interface to execute the message.

SimpleCommandProcessor Class

The SimpleCommandProcessor class implements the ICommandProcessor interface and uses reflection to execute a message by instantiating the appropriate Online Proxy and returning the result back to the specified result call back target. The block uses the ServiceAgentManager as the default target for returning the results.

IQueueStorageProvider Interface

Message data can be stored in several different types of physical stores using the queue storage providers. Each queue storage provider must implement the IQueueStorageProvider interface in order to be used with the application block. The QueueManagerBuilder class instantiates the queue storage provider and passes it to the QueueManager.

Application developers can create their own queue storage providers by implementing this interface and specifying the necessary configuration information in the application's configuration file.

Although multiple providers can be defined in the configuration file, only one of the providers should be enabled in the configuration file. If the provider is changed in the configuration file, the block treats it as the new provider for storing the message data. The block does not have built-in capabilities to move the data from the previous store to the new store.

The block ships with four queue storage providers:

  • In Memory–This provider is implemented in the InMemoryQueueStorageProvider class. It stores the queue data in an in- memory data structure, System.Collection.Queue. The data stored is lost when the application is restarted. This provider is not recommended for storing persistent data.
  • Isolated Storage–This provider is implemented in the IsolatedStorageQueueStorageProvider class. It stores queue data into isolated storage. This provider is recommended for storing persistent data.
  • MSDE–This provider is implemented in the msdeQueueStorageProvider class. It stores queue data in MSDE. This provider is recommended for storing persistent data.
  • MSMQ–This provider is implemented in the msmqQueueStorageProvider class. It stores queue data in Message Queuing. The custom property for this provider is in the configuration file and defines the name of the queue. This provider is recommended for storing persistent data.

Reference Data Management

Even when offline, the smart client application must provide users with the data they need to continue working. This data is typically referred to as reference data and is cached locally and refreshed periodically as the data ages and becomes stale.

Reference data is typically static, but it may occasionally be changed by the application. When this happens, the Offline Application Block marks the data as dirty. This prevents the block from refreshing the data as it ages and avoids overwriting updated data with older values from other sources. After the update is synched with the remote service, the data is marked clean, and is eligible for refreshing.

The Reference Data Management subsystem provides infrastructure for the application to cache the data locally and refresh the data when it becomes stale. It also provides the ability to store the data securely by allowing encryption and decryption of cached data.

The subsystem uses the Caching Application Block and consists of the following components:

  • ReferenceDataCache–The wrapper through which the Offline Application Block exposes the caching behavior with added capabilities to support online/offline scenarios.
  • ReferenceDataDefinition–The metadata associated with items in the reference cache. It controls how the information is refreshed, when it expires, and maintains a dirty versus clean status flag for the data.
  • ReferenceDataRefreshController–The class responsible for adding an item back to the cache when the item expires. It collaborates with the DataLoaderManager to refresh the cache items.
  • DataLoaderManager–The class that creates the ReferenceCacheDataPayload and adds the message to the queue, so that the reference data can be downloaded and stored in the cache.
  • ReferenceCacheDataPayload–The class that extends the Payload class with a reference to the ReferenceDataDefinition that contains the metadata required to update the reference cache data.

For an extended explanation of how the Reference Data Management Subsystem works, see the section, "Reference Data Cache Work Flow," in this chapter:

Class Design

The Reference Data Management subsystem provides classes to manage components that handle caching, storing, and refreshing data.

ReferenceDataCache Class

This is the wrapper through which the block exposes the caching behavior with added capabilities to support offline and online scenarios. This class implements IReferenceDataCache and the clients of this class should access an object of this type.

Class Members

The following subsection, "Methods," describes the most important members in this class. For specific information, see the Help file included with this application block.

Methods

  • Store–Stores the reference cache data item using the specified cache key. Store does not require that there be an item in the cache.
  • Update–Updates the reference cache data item at the location specified by the cache key. Update requires that there be an item in the cache.
  • Retrieve–Given the cache key, retrieves the reference cache data item.
  • Remove–Given the cache key, removes the reference cache data item.
  • IsDirty––Returns a Boolean value indicating if the item at specified cache key location has been modified by the application.
  • MarkAsClean–If the reference data cache item exists in the cache, it marks the item as clean. This indicates that the message that was generated as part of workflow that modified the reference data item at the specified cache key location has been processed.
  • ItemHasBeenExpired–Method used to mark the item as expired.

ReferenceDataDefinition Class

This class contains the metadata associated with items in the reference cache. It controls how the information is refreshed, when it expires, and maintains a dirty versus clean status flag for the data.

ReferenceDataRefreshController Class

This class is responsible for adding an item back to the cache when the item expires. It collaborates with the DataLoaderManager to refresh the queue.

This functionality must exist as a separate class—rather than being embedded inside the ReferenceDataCache itself—to promote loose coupling between elements of the block. Having the ReferenceDataCache be dependent upon the DataLoaderManager would unnecessarily couple these two subsystems. By offloading this responsibility to a third class, this dependency can be eliminated and the overall block can remain loosely coupled. To function, the class needs access to the ReferenceDataCache and the DataLoaderManager.

DataLoaderManager Class

This class is responsible for downloading the reference data. To do this, it creates the ReferenceCacheDataPayload and adds it to the queue so that the message can be processed and the reference data can be downloaded. The downloaded reference data is stored in the cache.

Class Members

The following subsection, "Methods," describes the most important members in this class. For specific information, see the Help file included with this application block.

Methods

  • LoadData–Creates the ReferenceCacheDataPayload and queues it for asynchronous processing to initiate the download of the reference data.
  • RefreshData–Creates the ReferenceCacheDataPayload and queues it for asynchronous processing to refresh expired data in the reference data cache.

ReferenceCacheDataPayload Class

This class derives from the Payload class and also contains ReferenceDataDefinition. This component is the metadata about the reference data that will be required to update the reference cache data.

Class Members

The following subsections, "Methods" and "Properties," describe the most important members in this class. For specific information, see the Help file included with this application block.

Methods

UpdateDataToReturn–Updates the data in the payload that was returned from the remote service.

Properties

  • DataDefinition–Returns the value of the ReferenceDataDefinition from the reference data cache item for which the payload was created.
  • IsRefreshMessage–Boolean value indicating whether the payload has been created to refresh a reference data item.

Cache Block and Storage Providers

The Reference Data Management subsystem uses the Caching Application Block to store the cached data. The caching block supports defining multiple providers in the configuration file, but only one of those providers should be enabled in the configuration file.

If the provider is changed in the configuration file, the block will use the new provider to store the cache data. The block does not have built-in capabilities to move the data from the previous data store to the new data store. This capability must be added by the developer.

In addition to the providers that ship with the Caching Application Block, the Offline Application Block includes an isolated storage cache provider. These providers are described below:

  • IsolatedStorageCacheProvider–Stores the cache data in .NET–supported isolated storage. It uses one file per cache item when the item is persisted physically. The names of the files correspond to the user-specified cache key. In the Offline Application Block, the cache key is used as the filename, which creates a constraint on the cache key: the cache key cannot have characters that are treated as special by the file system, such as "\" or "*" or "?". The block ensures that any key name used by an application developer corresponds to a valid file name in Isolated Storage, or it throws an exception.
  • Caching Application Block Providers–Includes cache storage providers for SQL Server, in-memory storage, and memory-mapped file storage. For more information refer to the Caching Application Block.

Utility Services

Utility Services provides background services such as multi-provider configuration handlers, configurators, and builders. Builders are used for creating instances of particular classes. Configurators are used by builders to read relevant information from the configuration file.

MultiProviderConfigHandler Class

The Offline Application Block provides the ability to define multiple providers for a single configuration section in the configuration file. Multiple providers are useful when you are managing more than one type of data store and need to correlate data from these different data stores. In this situation, you would programmatically enable or disable one store after another as its data is read and correlated. Only one of these sections must be enabled by setting the enabled attribute value to true. The MultiProviderConfigHandler class has the responsibility for reading the configuration information and also ensuring that this condition is met. It implements the interface IConfigurationSectionHandler and implements the Create method as mandated by the interface.

Class Members

The following subsection, "Methods," describes the most important member in this class. For specific information, see the Help file included with this application block.

Methods

Create–Reads the specified configuration section from the configuration file (App.config) and returns the information in a System.Collections.Hashtable that the client of this class can use.

If you plan on using multiple providers in your design, using this class can simplify your code.

Builders

Each of the key classes in the block has an associated builder class. The builder classes abstract the complexity of creating instances of the associated classes from the client of the builder class. Each of the builder classes uses the builder pattern.

Classes such as ConnectionManager, DataLoaderManager, ServiceAgentManager, QueueManager, Executor, ReferenceDataCache and DataLoaderManager, where we need only one instance of the class for the entire application, could have been created as singletons. However, singletons have a disadvantage because they lead to strong coupling. This defeats the advantage of block architecture, which encourages the development of interoperable, reusable code whose classes can be tested individually. Rather than using singletons, builder classes were included whose purpose is to create instances of particular classes.

OfflineBlockBuilder

The OfflineBlockBuilder class is the only class that the application developer needs to call to instantiate all of the subsystems. It does this by calling the corresponding builder classes. It is a singleton, which you can access by using the Instance property of the class. It exposes all of the key classes as properties. The following list includes all of the classes created by the OfflineBlockBuilder class:

  • DataLoaderManager
  • ReferenceDataCache
  • ServiceAgentManager
  • ServiceAgentRegistry
  • ConnectionManager
  • PayloadConsumer (an interface that the queue exposes to support functionality required for adding an item to the queue)

The OfflineBlockBuilder class also supports Start and Stop methods that start and stop all of the subsystems. Listed below are the most important members in this class. For specific information, see the Help file included with this application block.

  • Instance–Creates an instance of a block builder.
  • Start–Starts the block.
  • Stop–Stops the block.
  • Dispose–Stop the block and disposes of the OfflineBlockBuilder.

ConnectionManagerBuilder Class

The ConnectionManagerBuilder class constructs the ConnectionManager class and exposes the ConnectionManager as a property.

ServiceAgentManagerBuilder Class

The ServiceAgentManagerBuilder class constructs the ServiceAgentManager class and exposes the ServiceAgentManager as a property. In addition, it creates an object of the ServiceAgentRegistry class and exposes that as a property.

QueueManagerBuilder Class

The QueueManagerBuilder class constructs the QueueManager class and exposes the QueueManager as a property. The class gets the queue storage provider information from the configuration file by using the QueueManagerConfigurator class. It creates an instance of the queue storage provider specified in the configuration file, and passes it to the QueueManager during creation.

ExecutorBuilder Class

The ExecutorBuilder class constructs the Executor class and exposes the Executor as a property.

ReferenceDataCacheBuilder Class

The ReferenceDataCacheBuilder class constructs the IReferenceDataCache interface type and exposes the IReferenceDataCache type object as a property.

DataLoaderManagerBuilder Class

The DataLoaderManagerBuilder class constructs the DataLoaderManager class and exposes the DataLoaderManager as a property.

Reference Data Cache Work Flow

This section describes the work flow for downloading, managing, refreshing, and caching reference data using the Offline Application Block.

Downloading Reference Cache Data

As mentioned previously, for the application to work while offline, reference data must be downloaded and cached on the client. Downloading the reference data is initiated by the application. Based on the connection state, the application can make the decision to initiate the download of the reference data items or not. The block does not enforce any behavior in this regard.

The components that collaborate to download the reference data include the Application Service Agent created by the application developer, the DataLoaderManager class provided in the block, and the Message Data Management subsystem. The following steps walk through the process shown in Figure 2.6.

  1. The application creates an instance of the Application Service Agent. The Application Service Agent creates a ReferenceDataDefinition object for the reference cache data item. The Application Service Agent specifies the key that corresponds to the cache item, an optional expiration policy, and the OnlineProxyContext and ServiceAgentContext.
  2. The Service Agent instantiates the DataLoaderManager and gives it the ReferenceDataDefinition corresponding to the reference cache data item that needs to be downloaded.
  3. The DataLoaderManager uses the ReferenceDataDefinition to create an instance of the ReferenceCacheDataPayload, and queues it to be processed asynchronously using the Message Data Management subsystem.
  4. The processing of the message by the Message Data Management subsystem results in the reference cache data item being retrieved from the remote service. The Online Proxy adds the reference data into the reference cache.
  5. The results are returned back to the specific Application Service Agent.

Ff650491.f02offline06(en-us,PandP.10).gif

Figure 2.6. Queues reference data load request

Refreshing Reference Cache Data

Downloaded reference cache data can grow stale after a period of time. The block provides the ability to specify an expiration policy for each reference cache data item. Based on this expiration policy, the reference cache data item is refreshed by the block. The refresh is initiated by the block based on events raised by the CacheManager component of the Caching Application Block when a reference cache data item expires.

The components that collaborate to refresh the reference cache data include the CacheManager, the ReferenceDataCache, the ReferenceDataRefreshController, and the DataLoaderManager.

When a reference cache data item is downloaded and added to the cache, as explained in the previous section, "Downloading Reference Cache Data," an expiration policy can be specified in the reference data definition for the cache item. In addition to this, the Caching Application Block allows the specification of a callback method (delegate) when an item expires. When the reference cache data item is stored in the cache the ReferenceDataRefreshController class, using the CacheItemExpiredCallback method, is specified as the callback method when the item expires. This process is shown in Figure 2.7, which illustrates the following steps:

  1. The CacheManager monitors the cache items for expiration. When an item expires the cache manager calls the specified CacheItemExpiredCallBack method of ReferenceDataRefreshController.
  2. If the cache item has expired, the underlying Caching Application Block removes the item from the cache. The ReferenceDataRefreshController adds the item back into the cache before initiating download—this prevents the loss of information that would otherwise occur.
  3. Then the ReferenceDataRefreshController initiates the download of a reference data item through the DataLoaderManager.
  4. The DataLoaderManager uses the ReferenceDataDefinition to create an instance of the ReferenceCacheDataPayload. It then queues the object of the ReferenceCacheDataPayload as a message to be processed asynchronously using the message data management subsystem class QueueManager.
  5. The processing of the message by the Message Data Management subsystem results in the reference cache data item being retrieved from the remote service. The Online Proxy adds the reference data into the reference cache.
  6. The results are returned back to the specific Application Service Agent.

Ff650491.f02offline07(en-us,PandP.10).gif

Figure 2.7. Cache item expires and gets refreshed

As designed, the Offline Application Block does not deliver events to the application when reference data is refreshed in the Reference Data Cache. Adding that functionality is a straightforward task, as explained below.

Remember that events are delivered to the application when a service request is completed and when (and only when) the same Service Agent is available to be called back by the Offline Application Block. For the same thing to occur when data is refreshed in the cache, a long-lasting Service Agent must be created and used each time that data is refreshed, and that Service Agent must be made available to the application so that it can register to receive those completion events. This implementation is similar to the FailsafeServiceAgent implementation.

Managing Reference Cache Data

To manage reference cache data, you must mark it in some way so that you know if the data needs to be refreshed. The data can be marked dirty, expired, or both. The manner in which the reference cache handles the data may change, depending on how it is marked.

If a piece of data is dirty, it means that it has been changed locally but not on the server. Once the update does occur on the server, the data is marked clean. Remember that there is no way to know when a message is actually updated on the server. It may not occur until a scheduled batch job completes. Also, if the data has been changed several times, it is not marked as clean until all update requests have been sent.

A piece of data can also be marked as expired in the reference cache. This means that the Cache Manager has decided that an individual item in the cache has expired, causing the Cache Manager to remove it from the cache, and to execute a callback method to the reference cache about the expired item.

It is important to understand how, in the reference cache, data that is dirty interacts with data that is expired.

  • Data that is neither dirty nor expired is considered fresh. If that data expires, it is refreshed.
  • Data already marked expired that expires again will not have another refresh operation begun for it.
  • Data that expires and is marked dirty will not refresh until the data is marked clean in the reference cache. The reasoning is that the operation that made the data dirty locally gives the data a better, more recent value than the refreshed data. For this reason, the results of the refresh message are ignored.

Caching Reference Data

The Caching Application Block allows you to specify certain metadata for a cached item. This includes the expiration policy and a callback method when the item expires. The Offline Application Block must support additional metadata to be able to specify the context information of the components. These components retrieve the reference data when an item expires. The ReferenceDataDefinition class was created to store the extended metadata. The ReferenceDataDefinition class also contains a key to the actual reference cache item. In addition to the ability to pass this information around when required, it also provides an optimization when you need to check the metadata about the reference cache item because there is no need to deserialize the entire cache item.

The Caching Application Block removes an item from the cache permanently on expiration before notifying the application. This has several implications for the application running in an offline mode. If the item that is cached is removed, there is no way for the application to continue using that data in the cache. Since both the ReferenceDataDefinition and the cache item are cached, there is no way to retrieve them and add them back to the reference cache data if either of them is removed from the cache. To overcome this limitation, a level of indirection was added to the storage schema for the cache.

Using this schema, the expiration is specified on the first level of indirection. When that item expires, a notification with the value of the key occurs. The keys are then auto-generated for the ReferenceDataDefinition and the reference cache item. Thus, the application can continue to access them. The ReferenceDataRefreshController receives the notification and initiates the refresh of the data using the DataLoaderManager. The DataLoaderManager components add the first level of the schema before initiating the download.

The Caching Application Block already provides for in-memory cache, memory mapped file cache, and SQL database cache. The Offline Application Block builds on this infrastructure to provide caching support by including the isolated storage cache provider. This provider allows the application to store data specific to different users in different locations on the client computer where the smart client application is running.

The Caching Application Block provides support for encryption and decryption of cached data. The Offline Application Block takes advantage of this capability to secure cached data based on configuration information.

Request and Response Round Trip

This section describes how a request and response round trip flows in an application using the Offline Application Block. The following steps describe the round trip illustrated in Figure 2.8.

  1. The application's User Interface Controller (UIC) typically creates an instance of the specific Application Service Agent that can perform the task. The Application Service Agent creates the Payload for performing the task. The Payload contains all the necessary data to perform the task. In addition, the Payload contains all the context information about the components in OnlineProxyContext and ServiceAgentContext.
  2. The Application Service Agent queues the Payload by using the QueueManager class with the Enqueue method. The Enqueue method wraps the Payload in a QueueMessage class before storing it onto the queue using the configured queue storage provider.
  3. If the application is online, the Executor (running on a separate thread) gets the QueueMessage from the queue by using the Dequeue method of the QueueManager class. It then retrieves the Payload from the QueueMessage.
  4. The Executor retrieves the OnlineProxyContext from the Payload that corresponds to the Online Proxy. Using the OnlineProxyContext information through reflection, it executes the specified method of the Online Proxy. The Online Proxy interacts with the remote services to complete the operation. The Online Proxy also caches the results, performs any post-request processing, and handles common transactions.
  5. After the execution of the request the results are populated into the Payload by the Online Proxy. The Executor then returns the Payload to the ServiceAgentManager.
  6. The ServiceAgentManager class retrieves the GUID corresponding to the instance of the Application Service Agent from the Payload. Using the GUID, it locates the Application Service Agent in the Service Agent registry. If there is an Application Service Agent corresponding to the GUID (the original Application Service Agent), The ServiceAgentManager retrieves the original Application Service Agent to return the results; otherwise, the FailsafeServiceAgent class acts as the destination of last resort for errors that occur during Online Proxy execution.
  7. The Service Agent Manager retrieves the Application Service Agent and then calls the callback method using the ServiceAgentContext information in the payload. This call is made on a different thread. The Application Service Agent processes the results appropriately.

Ff650491.f02offline08(en-us,PandP.10).gif

Figure 2.8 . Request and response round trip

Application Design Considerations

So far, this chapter has concentrated on the design of the Offline Application Block. This section describes considerations for the design of the smart client application.

Life Cycle of a Service Agent

Application Service Agents are long-lived objects, meaning that they exist at least as long as necessary for a message to make a round-trip to its remote business capability or service. (If the application is shut down, the Application Service Agents that are in memory are flushed.) Each Application Service Agent is automatically registered with a Service Agent Registry, and is identified by a unique GUID. This GUID is used to tie a response from a remote business capability (service) back to the original Application Service Agent. If the original Application Service Agent, as identified by its unique GUID, is no longer available, the Service Agent Manager uses the FailsafeServiceAgent for reporting the errors.

Defining Multiple Providers in the Configuration File

In the configuration file, you can define multiple providers for a single configuration section. Providers or elements under the providers section that are not enabled can optionally have enabled="false" in the configuration, or they can omit the enabled attribute entirely. The algorithm to ensure that only one provider is enabled is encapsulated in the MultiProviderConfigHandler class. To allow for the dynamic changes to the configuration file at runtime, you can use the Configuration Management Application Block.

For more information, refer to the section on configuration files in Chapter 4.

Threading

The block creates several internal threads in its subsystems. They include:

  • A thread for processing queued messages asynchronously from the application code.
  • A thread that polls for connection state changes and informs the registered components of the state change.

It is important that application code that is called from one of these threads does not do any long-lasting processing in that call, to avoid delaying the block code. For example, if the connection state of the application changes, the Connection Manager will call into any class that has registered interest in that class's ConnectionStateChangedEvent. This call is made in the Connection Manager's connection detection thread, so any long-lived processing begun by a client will interfere with the Connection Manager monitoring for subsequent state changes. Event handling code should be quick to run and should not call back into block code if at all possible.

Reconciliation of Data

The block does not handle data reconciliation. This is the responsibility of either the client-side code or the remote business logic. Except for dirty or expired handling, as described in the "Reference Data Management" section, results from the remote business logic will overwrite any information available locally. For example, if a client's phone number is updated by the application, the change is queued, the request made, and the results received, with a notification that the operation was successful. However, if it takes hours or days for the information to finally be updated on the server side, there is the risk that any request to the server for the client's phone number during this time could return the old value. This is outside the scope of the block. A developer must supply the solution in application code or the in the remote business logic.

Running Components in Separate Processes

The Offline Application Block was designed to run entirely in a single process. It is possible, however, to run certain components of the block in more than one process.

The Executor is the best candidate for this. You can run it as a Windows service, allowing it to service the queue of pending messages while the application itself is closed. The following procedure describes how to do this:

  1. Update the creation logic in the builder classes for the Executor component. The main builder class must also be updated.
  2. The results returned after the message has been processed must be handled (returning it to the application) in both online and offline scenarios. There are several different approaches that you can take to achieve this. You can have a shared location, such as the cache, where the results for the application are stored. You can also pick them up or use some inter-process communication mechanism such as remoting to get the results back to the application.
  3. The Reference Data Management and Message Data Management subsystems have to be available to the Executor for the messages to be processed and the cache to be updated. This has implications on the queuing and the caching storage providers that can be used with the application and also on the design of the subsystems themselves.

By running some of the components of the block in a separate process, efficiencies can be gained, including having a shared cache and queue for multiple, possibly related, applications, as well as having common services that handle synchronization of message data and download/refresh of reference cache data. However, serious thought should be given to issues such as cache schema, communication between components, and any contention-related issues.

Alternative Designs

The design of the Offline Application Block uses asynchronous behavior to execute messages. Asynchronous behavior prevents the execution of long-running operations on the UI thread and thus provides a better user experience.

In some cases, where the network is reliable and has high bandwidth, you can consider an alternative design where the Application Service Agent queues the Payload to only occur when the application is offline. Once the application is online again, it directly calls the Online Proxy bypassing the Offline Application Block Subsystems. The Online Proxy returns the results to the Application Service Agent after processing the request. This is a synchronous call on the UI thread and is not advisable if it's a long-running operation, because it can freeze the UI. You can optimize by calling a thread other than the UI thread, and use the asynchronous support provided in .NET.

Tip   To avoid using if-else statements in the Service Agent you can use the state pattern to make a determination about the processing based on the state.

Summary

The Offline Application Block has been designed to provide the core infrastructure needed for your application to work in an offline mode. It is composed of the following subsystems:

  • Connection State Management
  • Service Agent Management
  • Message Data Management
  • Reference Data Management
  • Utility Services

The primary role of the Connection State Management subsystem is to detect the connection state and notify listeners of any state changes.

The Service Agent Management subsystem does the following

  • Maintains a registry of Application Service Agents
  • Contains the components responsible for passing the results back to the Application Service Agent after a request has been processed
  • Provides a Service Agent base class that the application developer must use to implement application service agents

The Message Data Management subsystem manages transactional data and supports processing messages asynchronously with respect to the application.

The Reference Data Management subsystem provides infrastructure for the application to cache the data locally and refresh the data when it becomes stale. It also provides the ability to store the data securely by allowing encryption and decryption of cached data.

The Utility Services subsystem manages connectivity and non-pool threads.

Start | Previous | Next

patterns & practices Developer Center

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

Did you find this helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2014 Microsoft. All rights reserved.