devxlogo

Managing Offline Clients with the Smart Client Offline Application Block

Managing Offline Clients with the Smart Client Offline Application Block

ich client. Fat client. Thin client. Web client. Add another to the list: the so-called smart client. But what is a smart client? A smart client:

  • Uses local CPU power
  • Communicates with Web services
  • Supports online/offline scenarios
  • Uses the device hardware
  • Provides automatic software updates

But here’s a better question: Why should you use a smart client?

Running your application in the browser is like having your office in the elevator. ? Alan Cooper

Just because it’s a popular place and gets a lot of traffic doesn’t mean it offers the best environment for the work you need to do.

It reminds me of the tremendous uptake of ASP.NET shortly after the .NET Framework was announced. Because ASP.NET was first of the .NET citizens to market, many companies began using it immediately and never evaluated other technologies such as Windows Forms. But ASP.NET is not a good choice for all applications.

The biggest advantage ASP.NET has is its simple deployment. Still, ASP.NET can’t provide the powerful graphical features of a Windows Forms application. And everything in ASP.NET is based on request and response messages, each of which requires a round-trip to the server. In many situations a smart client would result in more powerful and user friendly applications.

What You Need:
Visual Studio .NET 2005 Community Technology Preview (May 2004)
Smart Client Offline Application Block

Emissary and Fiefdom: The SOA Architecture

Figure 1. SOA Structure: This image shows the Service Consumer and the Service Provider platform on which the emissary and the fiefdoms are built.

To understand how to manipulate a smart client, it’s a good idea to first map out its role as part of a service-oriented architecture (SOA) structure (see Figure 1).

As Figure 1 shows, there are two platforms: the service consumer platform (emissary), which includes the smart client, and the service provider platform (fiefdom), which includes Web services.

The fiefdom follows the principle “Never trust an outsider.” Typically, fiefdoms are an organization’s enterprise applications. Why? Because fiefdoms allow data access and data manipulation only through a set of well defined interfaces. For example, a SQL Server 2005 instance?that exposes stored procedures via Web services for data access and data manipulation is a fiefdom. In that scenario the only way for outside persons and software components to manipulate data is via the set of well-defined interfaces exposed by those stored procedures. In short, a fiefdom offers services rather than direct data access to its consumers.

An emissary is the client. It uses services to communicate through well-defined interfaces with the fiefdom. The fiefdom and the emissary have no direct references to each other?a very big advantage in the evolution of software components.

Figure 2. Two Approaches: This image compares the service oriented and the data oriented approach to building distributed applications.

When you implement the emissary as a smart client, you gain the ability to add offline support. Offline support means that the emissary can work with the fiefdom without any physical connection being present during execution. How can this be achieved?

The answer lies in the way that the client receives and accesses data. Data that the client needs to make a service request to the fiefdom is called reference data. Often this is static data such as product catalogs and customer lists. The emissary can download static data from the fiefdom, storing and caching it locally. But there is another kind of data, user data, which is generated by the emissary and includes items such as orders and bank statements. The emissary transmits these through service requests to the fiefdom where the data can be processed further.

The Components of the Application Block
So now that you know that smart clients can deliver the application without an underlying connection to the fiefdom, how do you make it work? There are two approaches: data-oriented and service-oriented. The data-oriented approach uses a local database (such as MSDE) where the client stores changes while offline. This local database can then be synchronized with the backend database when the smart client is back online. In the service-oriented approach the smart client uses service requests, which are cached locally and then executed when the connection is restored. Figure 2 shows both approaches.

Figure 3. Workflow: This figure shows the workflow of the components used in the Smart Client Offline Application Block.

The Smart Client Offline Application Block from Microsoft is a toolkit that helps you implement the service-oriented approach. It provides an infrastructure to cache service requests locally until the network connection is back. This infrastructure is built on top of these four components:

  • Service Agent: The Service Agent is a central component where all the other services of the building block can be accessed.
  • Service Request: All details for a service request are stored in a service request object, which is stored in a queue awaiting execution.
  • Service Request Queue: Stores the service requests until the underlying network connection is restored.
  • Executor: Takes the cached requests from the queue and executes them.

Figure 3 shows the workflow among these components.

The Smart Client Offline Application Blocks provide these services:

  • Determine if a physical network connection is available, and switch between offline and online modes.
  • Cache the reference data.
  • Execute locally stored service requests when a connection is made.
  • Provide extension points for the customization of the Smart Client Offline Application Block.

Figure 4 shows a logic flow diagram of subsystems within the Smart Client Offline Application Block. All of these subsystems are loosely coupled so it’s very easy to change or extend them. The Smart Client Offline Application Blocks offers interfaces for implementing your own components.

Table 1 describes a few of these subsystems.

Subsystem

Description

Connection State Management

Makes it possible to check if a connection to the network is available. This process is performed automatically or started manually.

Service Agent Management

Coordinates the work with all other subsystems of the Building Block and creates an event when a service request completes successfully.

Reference Data Management

Makes it possible to store reference data locally so that it can be used in offline mode.

Message Data Management

Manages the caching, queuing, and execution of service requests.


Figure 4. Logic Flow Diagram: This graphic shows the several subsystems into which the Smart Client Offline Application Block is divided.
?
Figure 5. Internal Design: This figure shows the internal components used by the Smart Client Offline Application Block.

Figure 5 shows the internal design of the Smart Client Offline Application Block and the components behind the subsystems that carry out specific tasks.

Table 2 gives detail on these components.

Component

Description

Connection Detection Strategy

Determines if a connection to the underlying network is present.

ConnectionManager

Manages the connection detection providers. Through the implementation of the interface IConnectionDetectionStrategy you can plug in your own written connection detection providers.

Executor

When the smart client is in online mode, the executor takes the cached service requests from the local queue and executes them against the proper online proxy.

Queue Storage

Provides a cache where service requests can be stored when the smart client is in offline mode.

QueueManager

Provides an interface to the physical queue storage.

DataLoaderManager

Provides features for storing reference data locally.

ReferenceDataCache

Provides an interface to the available cache providers. The features of the Caching Application Block are used here.

Cache Block

The complete collection of caching storage providers.

Application Service Agent

Stores service requests locally during offline mode and accesses the online proxy.

Online Proxy

Communicates with the Web service and stores reference data in the local cache when necessary.

ServiceAgent

A base class from which to derive your own service agents. It is responsible for registering service agents in the Service Agent Registry.

ServiceAgentManager

The Service Agent Manager returns the result of a service request back to the correct service agent.

Figure 6. ReferenceDataCache: This figure shows how reference data can be retrieved from a backend Web service through the Smart Client Offline Application Block.

Offline Orders: A Sample Application
Now that you’re familiar with the components of the Smart Client Offline Application Block, let’s give it a workout. Here’s a typical use case: You have an application that allows sales reps to enter new order data for their customers. Before the smart client switches to the offline mode, the DataLoaderManager stores all reference data (custmers, products) locally. This way, during the offline phase the user can create orders normally. The orders are stored in a local queue, awaiting the network connection. To store reference data locally an application service agent uses the DataLoadManager class to start the download process. The DataLoaderManager calls the QueueManager, which stores a service request in the local queue.

When the network connection is restored, the Executor takes the service requests from the queue and calls the appropriate online proxy using Reflection. The online proxy calls the back end Web service, which returns the requested reference data. This data is stored with the component ReferenceDataCache in the local cache. Up to this point the requested data is available to the smart client for further processing (see Figure 6).

Figure 7. The Executor: This figure shows how the service agent works with other components to execute a Web service request.

To transmit an order to the Web service within the fiefdom, the client calls a service agent to handle the service request. The service agent creates an instance of the class Payload, which contains the current context and the current service request. Then the QueueManager wraps the payload instance to an instance of the class QueueMessage and stores this instance in the local queue.

As soon as the smart client is back in online mode the Executor starts up on a separate thread. As described above, it reads all cached messages (instances of the class QueueMessage) from and calls the appropriate methods on the online proxy using Reflection. The proxy itself receives instances of the class Payload as a parameter. Once the proxy has executed the service request, the Executor returns the payload instance (including the result from the Web service call) back to the application service agent, whose only job is to raise an event to inform the UI that the Request was executed successfully (see Figure 7).

Writing Your First Offline Application
It’s time to write some code. The sample application consists of three Web service methods that use the request/response message pattern and connect to SQL Server 2000’s Northwind database. The three services are:

  • GetCustomers: Retrieves all customers from the Customers table. This reference data is needed for making new orders.
  • GetProducts: Retrieves all products from the Products table. This is also needed for making new orders.
  • PlaceOrder: Transfers a new order to the back end system for processing. It creates new records in the Orders and Order Details tables. I used the PlaceOrder and PlaceOrderDetails stored procedures.

For each Web service method I’ve created the necessary request and response messages. For the method GetCustomers there are the GetCustomersRequest and GetCustomersResponse messages. The ProcessMessage function handles the messages and returns the required response.

 [WebMethod]public GetCustomersResponse GetCustomers(   GetCustomersRequest request){   return request.ProcessMessage();}[WebMethod]public GetProductsResponse GetProducts(   GetProductsRequest request){   return request.ProcessMessage();}[WebMethod]public PlaceOrderResponse PlaceOrder(   PlaceOrder Request){   return request.ProcessMessage();} 

The ProcessMessage function contains all the code needed to process a request message. It provides the following operations:

  • Go online: switches to the online mode
  • Go offline: switches to the offline mode
  • Place Order: creates a new service request
  • Download Reference Data: forces the download of the reference data (customers, products)
  • Add: adds a new order detail to the current order

Here’s a more detailed look at the client code. At startup, the application creates a new Controller object inside the Main function. The Controller accepts the current view as a parameter.

 public static void Main(){   frmMain view = new frmMain();   try   {      mainController = new Controller(view);   }   catch (Exception ex)   {      MessageBox.Show(ex.Message);      mainController = null;   }   if (mainController != null)   {      mainController.Start();      Application.Run(view);      mainController.Dispose();   }} 

The Controller object encapsulates the logic from the view in the presentation layer. Therefore the current view is stored in the private variable named view. There are three other notable private variables:

  • offlineBlockBuilderInstance: stores the singleton instance of the offline building blocks from which the complete functionality of the building block can be accessed
  • currentState: stores the current state of the smart client (offline, online)
  • northwindServiceAgent: stores the service agent that communicates with the back end Web services

The Controller object’s constructor initializes all variables and defines callback functions for the following events:

  • ConnectionStateChangedEvent: This event is exposed from the building block and is raised when the underlying connection state is changed from online to offline or vice versa.
  • CustomerReferenceDataAvailableEvent: The Northwind service agent raises this event when the customer reference data is available to the smart client.
  • ProductsReferenceDataAvailableEvent: The Northwind service agent raises this event when the products reference data is available to the smart client.

To switch the Smart Client Offline Application Block into the offline or online mode you have only to call the appropriate methods of the class ConnectionManager, which raises the ConnectionStateChangedEvent.

 public void GoOline(){   offlineBlockBuilderInstance.      ConnectionManager.GoOnline();}public void GoOffline(){   offlineBlockBuilderInstance.      ConnectionManager.GoOffline();}private void ConnectionStateChangedEvent(   object sender, ConnectionStateChangedEventArgs e){   currentState = e.CurrentState;   if (currentState == ConnectionState.OffLine)   {      UpdateState("Offline");   }   else   {      UpdateState("Online");   }} 

UpdateState ensures the proper UI is delivered, depending on whether the current mode is online of offline. Next, the Controller object must call the function DownloadReferenceData to begin download of customer and product reference data. The Northwind service agent is responsible for this action.

 public void DownloadReferenceData(){   northwindServiceAgent.GetCustomersReferenceData(      new GetCustomersRequest());   northwindServiceAgent.GetProductsReferenceData(      new GetProductsRequest());} 

Both functions in the Northwind service agent receive request messages.

 public class NorthwindServiceAgent : ServiceAgent{   public void GetCustomersReferenceDat(      GetCustomersRequest request)   {      string onlineProxyAssemblyName = "SmartClient";      string onlineProxyClassName =         "SmartClient.NorthwindOnlineProxy";      string onlineProxyMethodName = "GetCustomers";      string specificServiceAgentMethodToBeInvoked =          "CustomerDataAvailableCallback";      int absoluteExpirationTime = 600;      string cacheKey = "SmartClient_Customer";      OnlineProxyContext onlineProxyMethodContext =          new OnlineProxyContext(         onlineProxyAssemblyName,          onlineProxyClassName,          onlineProxyMethodName);      ServiceAgentContext specificServiceAgentContext =          new ServiceAgentContext(         specificServiceAgentMethodToBeInvoked);      ReferenceDataDefinition refDataDefinition =          new ReferenceDataDefinition(cacheKey,          absoluteExpirationTime,          onlineProxyMethodContext, request);     offlineBlockBuilderInstance.DataLoaderManager.        LoadData(this.Guid, specificServiceAgentContext,         refDataDefinition);   }} 

Every service agent must derive from the base class ServiceAgent. This ensures that every service agent in the system is registered with the service agent registry under a GUID.

Figure 8. Service Request Message: This figure shows how a service request message is queued locally and executed through the executor.

To call a specific method on the online proxy you must create a new instance of the OnlineProxyContext class. This instance contains the assembly name, the class name, and the method name to be invoked, as these data are needed to call the method through Reflection. Furthermore you have to create a new instance of the class ServiceAgentContext. This instance contains all information needed to call a callback function on the service agent when the service request completed successfully.

At the end you have to create a new instance of the class ReferenceDataDefinition. This instance contains all data needed to store the requested reference data locally in the configured cache store. The last method called is LoadData of the class DataLoaderManager. When the smart client is online the service request is taken directly from the queue and executed against the specified online proxy. But when the smart client is in the offline mode, the service request is stored in the configured queue storage provider until a connection is made (see Figure 8).

Talking to the Back End
The last component to analyze in our sample application is the online proxy, which communicates directly with the back end Web service. The following code shows the GetCustomers function specified in the OnlineProxyContext.

 public Payload GetCustomers(Payload referencePayload){   ReferenceCacheDataPayload refCacheDataPayload =       referencePayload as ReferenceCacheDataPayload;   NorthwindService svc = new NorthwindService();   GetCustomersResponse response = svc.GetCustomers(      (GetCustomersRequest)refCacheDataPayload.      DataDefinition.AssociatedRequestDatas);   offlineBlockBuilderInstance.ReferenceDataCache.Store(      refCacheDataPayload.DataDefinition, response);   refCacheDataPayload.UpdateDataToReturn(response);   return refCacheDataPayload;} 

The preceding code first creates a new instance of the proxy class NorthwindService. The request message is accessed through the property refCacheDataPayload.DataDefinition.AssociatedRequestData. The request message is then passed as a parameter to GetCustomers, which returns the corresponding response message GetCustomersResponse. The Store function of the class ReferenceDataCache stores the returned reference data in the local cache storage provider. Furthermore the function UpdateDataToReturn is called to store the result directly in the variable refCacheDataPayload, which is then returned as a function result. Therefore the requested reference data can be used directly from the smart client.

After all the reference data is available to the smart client the application can go to offline mode if necessary.

Now take a look at the PlaceOrder function in the class NorthwindServiceAgent, which is responsible for executing a new order against the back end Web service:

 public void PlaceOrder(PlaceOrderRequest request){   string onlineProxyAssemblyName = "SmartClient";   string onlineProxyClassName =       "SmartClient.NorthwindOnlineProxy";   string onlineProxyMethodName = "PlaceOrder";   string specificServiceAgentMethodToInvoke =       "OrderPlacedCallback";   OnlineProxyContext onlineProxyMethodContext =       new OnlineProxyContext(      onlineProxyAssemblyName,       onlineProxyClassName,       onlineProxyMethodName);   ServiceAgentContext uploadServiceAgentContext =       new ServiceAgentContext(      specificServiceAgentMethodToInvoke);   Payload messageToEnqueue = new Payload(      onlineProxyMethodContext, this.Guid,       uploadServiceAgentContext, request);   OfflineBlockBuilder.Instance.PayloadConsumer.Enqueue(      messageToEnqueue);} 

As you can see, the code here is also an OnlineProxyContext instance that indicates which function of the online proxy should be called. The same applies to the ServiceAgentContext instance. The code creates a new Payload class instance, which is then enqueued in the Queue Storage Provider via the PayloadConsumer.Enqueue call. As soon as the smart client is back to the online mode the Service Request is taken from the Executor and executed against the specified online proxy.

The Occasionally Connected Future
It’s essential to know how to use the Smart Client Offline Application Blocks to take full advantage of the occasionally-connected capabilities of .NET smart clients. Not only will your end users value these features highly and increase your organization’s efficiency, but knowing how to deal with occasionally-connected clients is a valuable skill that you’ll need in the future, as new software appears that supports this paradigm. It’s also a great way&#151:via the fiefdoms and emissaries metaphor?to get functional exposure to today’s service-oriented architectures.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist