Mobile applications often want to make webservice requests to either send a message to a server or as part of the synchronisation process. However if a connection is not available this request needs to be queued. When a connection is available, if the request is important enough it should be sent. Whether or not a request is important enough is dependent on the information in the request (ie the number of "stamps" allocated to the request) and how expensive the connection is (ie the "price" of the connection type"). If you read my blog article on the Connection Manager blog you will have noted that each connection type is allocated a price. What we need now is a way to assign a number of stamps to a request and to manage a queue for requests (which should survive an application restart). To do this we use the Disconnected Service application block (class list shown below).
This block needs to be notified when the connectivity of the device changes. When a connection becomes available it can send pending requests. So that the block can register for connection change events we use the Connection Management block:
Dim configuration As String = "<Connections>" & _
" <Connection Type='CellConnection' Price='8'/> " & _
" <Connection Type='NicConnection' Price='2'/> " & _
" <Connection Type='DesktopConnection' Price='1'/> " & _
Dim factory As IConnectionManagerFactory = New Implementations.ConnMgrApiFactory(configuration)
Dim conManager As ConnectionManager = factory.Create()
In order to queue requests this block uses two databases (don't try to use the database as you will get exceptions) to manage a pending queue and a dead request queue (where failed requests get placed). So we create two DatabaseServices using the Data Access block.
Dim requestPath As String = "DataSource=""AWReplication.sdf"";"
Dim database As DatabaseService = New SqlDatabaseService(requestPath)
Dim deadRequestPath As String = "DataSource=""AWReplicationDead.sdf"";"
Dim deaddatabase As DatabaseService = New SqlDatabaseService(deadRequestPath)
The next thing we need to do is create a ProxyFactory that will be used to create the web service proxy class. As we will be queuing the web requests this block needs to be able to create the proxy class automatically. This is done using the ProxyFactory class. At the moment users of the block need to create their own factory class that inherits from the abstract OnlineProxyFactory class. Hopefully this dependence will be removed in future versions. A sample implementation is as follows:
Public Class ProxyFactory
Public Overrides Function GetOnlineProxy(ByVal request As Microsoft.Practices.Mobile.ApplicationBlocks.DisconnectedServiceAgent.Request) As Object
If (request.OnlineProxyType Is GetType(HelloWorldService.Service)) Then
Dim onlineProxy As New HelloWorldService.Service()
If (onlineProxy IsNot Nothing) Then
onlineProxy.Url = onlineProxy.Url.Replace("/localhost/", "/" & MyBase.GetEndpointPhysicalAddress(request) & "/")
We can see from this example that the url of the web request gets dynamically updated. In order for this to happen we have to load a list of endpoints that will be used for this substitution. This list loads from an xml configuration file as shown in the following example:
Dim pf As New OnlineProxyFactory 'ProxyFactory
'Load the list of address endpoints
Dim catalog As New AddressCatalog()
pf.Catalog = catalog
'Load the list of credentials
Dim credService As New CredentialsService()
pf.Credentials = credService
This example also loads a series of credentials from an alternative configuration file. This makes use of the CredentialService application block (class file list below):
Sample configuration files:
<?xml version="1.0" encoding="Windows-1252"?>
<Credential ServiceName="ServiceProxy://HelloWorldService" UserName="" Password="" />
<?xml version="1.0" encoding="utf-8" ?>
<Network Name="My ISP" Address="192.168.1.100" />
So now we have a ConnectionManager, Pending and Dead request databases, and a ProxyFactory. Now all we need to do is create the DispatchService:
Private ds As New DispatchService
A note of caution - make sure you place the DispatchService so that it won't get garbage collected (ie don't make it a local method variable). This DispatchService is all ready to go, so we can go ahead and create a new request.
req = New Request()
req.Id = Guid.NewGuid()
req.CallParameters = New CallParameters()
req.MethodName = "HelloWorld"
req.OnlineProxyType = GetType(HelloWorldService.Service)
req.Endpoint = "MyHost"
req.Behavior.Tag = "Hello"
req.Behavior.ReturnCallback = New CommandCallback(GetType(DispatchResponse), "ReturnCallbackMethod")
req.Behavior.ExceptionCallback = New CommandCallback(GetType(DispatchResponse), "ExceptionCallbackMethod")
req.Behavior.MaxRetries = 0
req.Behavior.Stamps = 5
This creates a new web request for the HelloWorld web service. You will notice that two call back methods are provided (specified by class and method name). These will be invoked when the web request is executed or an exception is thrown. We also allocate the number of "stamps" for this request. The request will only be sent when a connection type of a price less than or equal to 5 is active. Now all we have to do is use the DispatchService to enqueue the request.