Chapter 8

Consuming Data Services

What's In This Chapter?

  • Initiating web transactions
  • Working disconnected
  • Exploring device resource considerations

The previous chapter showed you how to create REST and SOAP services to provide web-based data functionality. From retrieving data (GETs) to saving data (PUTs and POSTs) to deleting it (DELETEs), you perform the functions by consuming these data services.

This chapter provides you with a detailed introduction to RESTful consumption of data services and explains how to ensure that the data your mobile application requires to run correctly is available, even when your network connection isn't available. The chapter also discusses device resource considerations to keep in mind when devising a caching scheme for storing data locally.

Initiating RESTful Transactions

The RESTful services described in the previous chapter present the opportunity for an application to interact with centralized data and functionality that resides on the server. You can call these services from the mobile applications (that is, the consumers of the services) via RESTful web transactions.

These RESTful transactions support each data entity (or business object) to be operated upon in multiple ways via a single Uniform Resource Identifier (URI), aka a RESTful interface. You can accomplish this through the use of HTTP Request Methods or RESTful verbs. There are several RESTful verbs that specifically focus on basic data interactions. Table 8.1 summarizes the verbs covered in this chapter.

Table 8.1 RESTful Verbs

Utility Description
GET Requests information from the RESTful service, such as an object or list
DELETE Requests the RESTful service to remove a particular object
POST Submits a new object to the RESTful service; also refers generically to submitting information to the service
PUT Submits an updated object to the RESTful service

In simple terms, GETs are about obtaining information from the server and POSTs, PUTs, and DELETEs are about sending information back to the server. All these commands can operate on the same URI, and the actual operation performed is dependent on the type of verb used when calling the RESTful service.

This chapter uses many samples, and to keep them as simple as possible, much of the potentially repetitious code has been extracted into reusable methods to enable you to focus on the web transactions. Table 8.2 summarizes the WebHelpers methods used in this chapter's samples.

Table 8.2 WebHelpers Methods

Utility Description
ByteArrayToStr Converts a byte[] to a string
DeserializeJson<T> Deserializes a JSON string or byte[] into an object of type T
DeserializeXml<T> Deserializes an XML string or byte[] into an object of type T
DirectoryName Returns the expected directory name of a given file path string
ReadFromFile<T> Reads the contents from a file, deserializes them, and returns an object of type T
StreamToByteArray Converts a stream into a byte[]
StreamToString Converts a stream into a string
StrToByteArray Converts a string into a byte[]
WriteToFile<T> Serializes an object of type T and then writes contents to a file
XmlSerializeObjectToBytes Serializes an object to XML and then converts results to a byte[]

Each of these methods simplifies the code samples so you can focus on the concepts presented by each sample. These methods are not the only ways to abstract and generalize repetitious code, but they serve as a starting point for your development efforts. Listing 8.1 contains the code implementation for the WebHelpers class whose methods appear in this chapter's samples.

1.1
Listing 8.1: WebHelpers class implementation
// helpful utilities for processing web calls.
public class WebHelpers
{
  // Serializes an object into a byte array
  public static byte[] XmlSerializeObjectToBytes(object obj)
  {
    byte[] byteData = null;

    MemoryStream stream = new MemoryStream();

    XmlSerializer ser = new XmlSerializer(obj.GetType());
    XmlWriter writer = null;
    try
    {
      writer = XmlWriter.Create(stream, new XmlWriterSettings()
      {
        Encoding = Encoding.UTF8
      });

      ser.Serialize(stream, obj);
      byteData = stream.ToArray();
    }
    finally
    {
      if (writer != null)
        writer.Close();
    }

    return byteData;
  }

  // Converts a string to a byte array
  public static byte[] StrToByteArray(string str)
  {
    System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
    return encoding.GetBytes(str);
  }

  // Converts a byte array to a string
  public static string ByteArrayToStr(byte[] byteData)
  {
    try
    {
      Encoding enc = Encoding.GetEncoding("utf-8");
      return enc.GetString(byteData, 0, byteData.Length);
    }
    catch
    {
      // swallow exception if cannot convert to UTF8 string.
    }

    return null;
  }

  // Converts a stream into a byte array
  public static byte[] StreamToByteArray(Stream stream)
  {
    byte[] buffer = new byte[32768];
    using (MemoryStream ms = new MemoryStream())
    {
      while (true)
      {
        int read = stream.Read(buffer, 0, buffer.Length);
        if (read <= 0)
          return ms.ToArray();
        ms.Write(buffer, 0, read);
      }
    }
  }

  // Converts a stream into a string
  public static string StreamToString(Stream stream)
  {
    byte[] responseBytes = WebHelpers.StreamToByteArray(stream);
    return WebHelpers.ByteArrayToStr(responseBytes);
  }

  // XML deserialize to object
  public static T DeserializeXml<T>(Stream stream)
  {
    return (T)new XmlSerializer(typeof(T)).Deserialize(stream);
  }
  // XML deserialize to object
  public static T DeserializeXml<T>(string value)
  {
    return (T)new XmlSerializer(typeof(T)).Deserialize(new StringReader(value));
  }

  // JSON deserialize to object
  public static T DeserializeJson<T>(Stream stream)
  {
    // Uses NewtonSoft.Json http://james.newtonking.com/projects/json-net.aspx
    return JsonConvert.DeserializeObject<T>(WebHelpers.StreamToString(stream));
  }

  // JSON deserialize to object
  public static T DeserializeJson<T>(string value)
  {
    // Uses NewtonSoft.Json (http://james.newtonking.com/projects/json-net.aspx)
    return JsonConvert.DeserializeObject<T>(value);
  }

  public static void WriteToFile<T>(T obj, string filename)
  {
    byte[] bytes = WebHelpers.XmlSerializeObjectToBytes(obj);
    string path = DirectoryName(filename);
    if (!Directory.Exists(path)) Directory.CreateDirectory(path);

    File.WriteAllBytes(filename, bytes);
  }
  public static T ReadFromFile<T>(string filename)
  {
    if (!File.Exists(filename)) return default(T);
    return DeserializeXml<T>(File.ReadAllText(filename));
  }

  public static string DirectoryName(string filename)
  {
    if (string.IsNullOrEmpty(filename))
      return string.Empty;

    int idx = filename.LastIndexOf(System.IO.Path.DirectorySeparatorChar);

    if (idx > 0)
      return filename.Remove(idx);

    return string.Empty;
  }
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/WebHelpers.cs file of the download

Performing RESTful GETs

You can use RESTful GET transactions to obtain information from the RESTful server so your mobile application can use them.

The following samples in this section illustrate how to consume RESTful Services starting with some simple GETs and expanding into more complex scenarios. Although these examples work for MonoCross applications, they are more general in nature, and you can use them to consume RESTful services in any C# application.

Listing 8.2 contains an implementation of a simple RESTful GET call.

1.1
Listing 8.2: Simple GET implementation
/// Simple Synchronous GET call to make request and return the results in a string
public static string SimpleGet()
{
  string uri = "http://localhost/MxDemo/products.xml";

  HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
  HttpWebResponse response = (HttpWebResponse)request.GetResponse();

  if (response.StatusCode != HttpStatusCode.OK) return null;

  return WebHelpers.StreamToString(response.GetResponseStream());
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/RESTfulGet.cs file of the download

As you can see, you can initiate a simple GET call to obtain a list of products from the RESTful server with just a few short lines of code. It's simple; you specify the URI to call, make the request to the URI, obtain the response, and return it back to the calling function.

Listing 8.3 shows the Product list output in XML.

1.1
Listing 8.3: GET call results in XML format
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfProduct xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Product>
    <ID>1000</ID>
    <Description>This is a basic Widget.</Description>
    <Price>100.00</Price>
  </Product>
</ArrayOfProduct>

Sample GET response represented in XML format

Now, if your data format is in JSON rather than XML, then the same GET method works with a simple change to the URI, as shown in Listing 8.4.

1.1
Listing 8.4: Simple JSON GET implementation
// Simple Synchronous GET call to make request 
// and return the results in a JSON string
public static string SimpleGetJson()
{
  string uri = "http://localhost/MxDemo/products.json";

  HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
  HttpWebResponse response = (HttpWebResponse)request.GetResponse();

  if (response.StatusCode != HttpStatusCode.OK) return null;

  return WebHelpers.StreamToString(response.GetResponseStream());
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/RESTfulGet.cs file of the download

Listing 8.5 shows that the results returned are now in JSON format.

1.1
Listing 8.5: GET Call Results in JSON Format
[
  {  "Description":"This is a basic Widget."
   , "ID":"1000"
   , "Manufacturer":null
   , "Price":100.00
  }
]

Sample GET response represented in JSON format

You can change the URI to accommodate more than just whether the format is XML or JSON. You can use the URI to accommodate more complex RESTful endpoints that contain parameters in the URI Path or the URI Query. Following are just a few of the possible more complex URIs:

http://localhost/MxDemo/customers/7.xml

http://localhost/MxDemo/orders/1/10002.json

http://localhost/MxDemo/customers.xml?filter=Altera&page=0&items=0

Listing 8.6 shows an implementation of a GET that uses parameters in the URI.

1.1
Listing 8.6: Simple GET with Parameter Implementation
// Simple Synchronous GET call to make request with a parameter
// and return the results in an XML string
public static string SimpleGetWithParameters(string customerId)
{
  string uri 
    = string.Format("http://localhost/MxDemo/customers/{0}.xml", customerId);

  HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
  HttpWebResponse response = (HttpWebResponse)request.GetResponse();

  if (response.StatusCode != HttpStatusCode.OK) return null;  

  return WebHelpers.StreamToString(response.GetResponseStream());
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/RESTfulGet.cs file of the download

It's easy to see how with just a few changes you can create methods to support any number of URIs, both with and without parameters.

Although creating a method to make a call to a specific URI works, you can certainly do better by creating a generalized GET method whose purpose is to return string results for any URI provided to it. And, you can add a little more exception handling to make the method more robust. If you want to use the URI object from .NET in place of a string, then the implementation would look a little different.

Listing 8.7 contains the implementation of these generalized GET methods.

1.1
Listing 8.7: Generalized GET methods
// Generalized GET
public static string Get(string uri)
{
  if (string.IsNullOrEmpty(uri)) return null;
  if (!Uri.IsWellFormedUriString(uri, UriKind.RelativeOrAbsolute))
    return null;  // or log and/or throw an exception...

  HttpWebResponse response = null;
  try
  {
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    response = (HttpWebResponse)request.GetResponse();

    if (response.StatusCode != HttpStatusCode.OK) return null;

    return WebHelpers.StreamToString(response.GetResponseStream());
  }
  catch (Exception exc)
  {
    Console.WriteLine("Error in getting data for URI " + uri);
    Console.WriteLine(exc.Message);
    return null;
  }
  finally
  {
    if (response != null)
      response.Close();
  }
}

// Generalized GET with URI Object
public static string Get(Uri uri)
{
  HttpWebResponse response = null;
  try
  {
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    response = (HttpWebResponse)request.GetResponse();

    if (response.StatusCode != HttpStatusCode.OK) return null; 

    return WebHelpers.StreamToString(response.GetResponseStream());
  }
  catch (Exception exc)
  {
    Console.WriteLine("Error in getting data for URI " + uri.OriginalString);
    Console.WriteLine(exc.Message);
    return null;
  }
  finally
  {
    if (response != null)
      response.Close();
  }
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/RESTfulGet.cs file of the download

With such generalized GET methods, you can simplify your mobile application's RESTful service calls to a single line of code per call, as shown in the sample GET calls in Listing 8.8.

1.1
Listing 8.8: Generalized GET sample usage
// Generalized GET method Usage Samples
public void GetMethodCalls()
{
  // calling Get method with String URI
  string results1 = RESTfulGet.Get("http://localhost/MxDemo/customers/7.xml");
  string results2 = RESTfulGet.Get("http://localhost/MxDemo/customers/7.json");
  string results3 
    = RESTfulGet.Get("http://localhost/MxDemo/customers/7/contacts.xml");
  string results4 = RESTfulGet.Get("http://localhost/MxDemo/orders/1/10002.xml");

  // calling Get method with URI object
  Uri uri1 = new Uri("http://localhost/MxDemo/customers/7.xml");
  string uriResults1 = RESTfulGet.Get(uri1);

  Uri uri2 = new Uri("http://localhost/MxDemo/customers/7.json");
  string uriResults2 = RESTfulGet.Get(uri2);

  Uri uri3 = new Uri("http://localhost/MxDemo/customers/7/contacts.xml");
  string uriResults3 = RESTfulGet.Get(uri3);

  Uri uri4 = new Uri("http://localhost/MxDemo/orders/1/10002.xml");
  string uriResults4 = RESTfulGet.Get(uri4);
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/RESTfulGet.cs file of the download

You considered a few basic approaches with the RESTful web service calls to obtain information from the server, but so far all the methods returned the results of the service call in the form of a string. The string, presumably, is to be operated on in some fashion, whether it is to write the output to a file, process an XML result via XSLT, or convert the string into a usable object, a process known as deserialization.

To deserialize the string into an object, you need to know the format of the string in question. That format is generally either XML or JSON; although, it could be in other formats as well. It's a simple matter to create a generic GET method that can both retrieve the data for the given URI string and return a fully populated object. Listing 8.9 contains a generic method that performs both of these tasks.

1.1
Listing 8.9: Generic GET implementation
public static T Get<T>(string uriString)
{
  if (string.IsNullOrEmpty(uriString)) return default(T);
  if (!Uri.IsWellFormedUriString(uriString, UriKind.RelativeOrAbsolute))
    return default(T);  // or log and/or throw an exception...

  Uri uri = new Uri(uriString);

  HttpWebResponse response = null;
  try
  {
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    response = (HttpWebResponse)request.GetResponse();

    if (response.StatusCode != HttpStatusCode.OK) 
      return default(T);  // or error message...

    string path = uri.GetLeftPart(UriPartial.Path);
    Stream stream = response.GetResponseStream();

    // XML deserialization
    if (path.EndsWith("xml", StringComparison.InvariantCultureIgnoreCase))
      return WebHelpers.DeserializeXml<T>(stream);
    // JSON deserialization
    else if (path.EndsWith("json", StringComparison.InvariantCultureIgnoreCase))
      return WebHelpers.DeserializeJson<T>(stream);
    else
      return default(T);
  }
  catch (Exception exc)
  {
    Console.WriteLine("Error in getting data for URI " + uriString);
    Console.WriteLine(exc.Message);
    return default(T);
  }
  finally
  {
    if (response != null)
      response.Close();
  }
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/RESTfulGet.cs file of the download

The JsonConvert class referred to in the Listing 8.9 is from the set of JSON.NET tools available at http://james.newtonking.com/projects/json-net.aspx, but you can certainly replace it with other JSON tools, if you prefer a different tool.

You can see from the listing, we have added basic validation and some simple exception handling to the RESTful GET call. Usage of this particular method is as simple as specifying the URI being called and the generic type being returned. Listing 8.10 shows some sample usage of this method.

1.1
Listing 8.10: Generic GET method usage samples
// Generic GET Method Calls
public void GetMethodCallsGeneric()
{
  string uri1 = "http://localhost/MxDemo/products.xml";
  List<Product> listProd = RESTfulGet.Get<List<Product>>(uri1);

  string uri2 = "http://localhost/MxDemo/customers/7.xml";
  Company company = RESTfulGet.Get<Company>(uri2);

  string uri3 = "http://localhost/MxDemo/orders/1.xml";
  List<Order> listOrder = RESTfulGet.Get<List<Order>>(uri3);

  string uri4 = "http://localhost/MxDemo/customers/7/contacts.xml";
  List<Contact> listContact = RESTfulGet.Get<List<Contact>>(uri4);

  string uri5 = "http://localhost/MxDemo/products.json";
  List<Product> listProd2 = RESTfulGet.Get<List<Product>>(uri5);

  string uri6 = "http://localhost/MxDemo/customers/7.json";
  Company company2 = RESTfulGet.Get<Company>(uri6);

  string uri7 = "http://localhost/MxDemo/orders/1.json";
  List<Order> listOrder2 = RESTfulGet.Get<List<Order>>(uri7);

  string uri8 = "http://localhost/MxDemo/customers/7/contacts.json";
  List<Contact> listContact2 = RESTfulGet.Get<List<Contact>>(uri8);
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/RESTfulGet.cs file of the download

Synchronous methods to access RESTful data from a web service, such as those presented in Listing 8.10 are fairly straightforward, but they do have drawbacks. Synchronous methods block the primary processing while the RESTful calls are made rather than yielding the process to the primary thread. Asynchronous method calls, on the other hand, are slightly more complex to write, but they have the advantage of yielding the processing to a primary thread while the RESTful GET is performed.

You can think of it this way: suppose for a moment that you're talking to someone on the telephone and she needs to take care of something quickly. She can either put you on hold while she does whatever it is (that is, synchronous) and make you wait on the line until she completes the task, or she can call you back when the task has been completed (that is, asynchronous).

Listing 8.11 shows a sample class that illustrates how an asynchronous RESTful GET can be written.

1.1
Listing 8.11: Asynchronous GET implementation
public class RESTfulGetAsynch
{
  private ManualResetEvent allDone;

  const int DefaultTimeout = 60 * 1000;  // default a 60 second timeout.

  public string Get(string uriString)
  {
    if (string.IsNullOrEmpty(uriString)) return null;
    if (!Uri.IsWellFormedUriString(uriString, UriKind.RelativeOrAbsolute))
      return null;  // or log and/or throw an exception...

    return GetAsynch(new Uri(uriString));
  }

  public string Get(Uri uri)
  {
    return GetAsynch(uri);
  }

  private string GetAsynch(Uri uri)
  {
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    allDone = new ManualResetEvent(false);

    RequestState state = new RequestState()
    {
      Request = request,
      Uri = uri
    };

    // Start the asynchronous request.
    IAsyncResult result 
      = request.BeginGetResponse(new AsyncCallback(ResponseCallback), state);
    if (!allDone.WaitOne(DefaultTimeout))
    {
      Console.WriteLine("Call to {0} timed out", state.Uri);
      return null;
    }

    if (!string.IsNullOrEmpty(state.ResponseString))
      return state.ResponseString;
    else
      return null;
  }

  private void ResponseCallback(IAsyncResult result)
  {
    // Get and fill the RequestState
    RequestState state = (RequestState)result.AsyncState;
    try
    {
      HttpWebRequest request = state.Request;

      // End the Asynchronous response and get the actual response object
      state.Response = (HttpWebResponse)request.EndGetResponse(result);

      state.Expiration = state.Response.Headers["Expires"].TryParseDateTimeUtc();
      state.StatusCode = state.Response.StatusCode;

      if (state.StatusCode != HttpStatusCode.OK)
      {
        Console.WriteLine("GET {0} had status {1}", state.Uri, state.StatusCode);
        return;
      }

      state.ResponseString 
        = WebHelpers.StreamToString(state.Response.GetResponseStream());
    }
    catch (Exception ex)
    {
      // capture relevant details about the request
      ex.Data.Add("Uri", state.Request.RequestUri);
      ex.Data.Add("Verb", state.Request.Method);

      if (ex is WebException)
      {
        HttpWebResponse response = (HttpWebResponse)((WebException)ex).Response;
        state.StatusCode = response.StatusCode;
        ex.Data.Add("StatusDescription", response.StatusDescription);
        ex.Data.Add("StatusCode", state.StatusCode);
      }
      else
        state.StatusCode = (HttpStatusCode)(-1);

      state.ErrorMessage = string.Format("GET {0} ", state.Request.RequestUri );
      state.ErrorMessage += string.Format("had Exception {1}", ex.Message);
      state.Exception = ex;
      state.Expiration = DateTime.UtcNow;

      // Log exception
    }
    finally
    {
      if (state.Response != null)
        state.Response.Close();
      state.Request = null;

      allDone.Set();
    }
  }

  public class RequestState
  {
    public Uri Uri { get; set; }
    public HttpWebRequest Request { get; set; }
    public HttpWebResponse Response { get; set; }
    public string ResponseString { get; set; }
    public DateTime Expiration { get; set; }
    public HttpStatusCode StatusCode { get; set; }
    public Exception Exception { get; set; }
    public string ErrorMessage { get; set; }
  }
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/RESTfulGetAsynch.cs file of the download

Performing PUTs, POSTs, and DELETEs

Obtaining data from a server with a RESTful GET is only part of what clients need to consume a web service. Although getting the data from the server is important, you also need to consider how to send updates back to the RESTful server, whether those updates are to add a new invoice, to update a customer, or even to delete an address that is no longer needed.

You can send changes to the RESTful server with a WebRequest, just like the GET calls presented earlier, but you have the added steps of sending the new or updated information to the server (in the case of PUTs and POSTs) or simply requesting the removal of information (in the case of DELETEs).

Just like GETs, you can perform PUTs and POSTs either synchronously or asynchronously. Listing 8.12 shows an example of a synchronous post that you can use to submit web requests for both POSTs and PUTs.

1.1
Listing 8.12: RESTful PUT/POST implementation
// Post information to server, POSTs and PUTs can use this method by specifying verb
public static PostReturn 
  Post(Uri uri, byte[] postBytes, string contentType, string verb)
{
  // Create the request object
  HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);

  // Set values for the request back
  request.Method = verb;  
  request.ContentType = contentType;
  request.ContentLength = postBytes.Length;

  Stream postStream = null;

  HttpWebResponse response = null;
  Stream streamResponse = null;
  StreamReader streamRead = null;
  try
  {
    // obtain request stream from server to stream data to server
    postStream = request.GetRequestStream();
    postStream.Write(postBytes, 0, postBytes.Length);
    postStream.Close();

    response = (HttpWebResponse)request.GetResponse();

    PostReturn postReturn = new PostReturn()
      {
        StatusCode = response.StatusCode,
        Method = verb,
        Uri = uri
      };

    if (response.StatusCode != HttpStatusCode.OK)
    {
      Console.WriteLine("{0}", verb, uri, response.StatusCode);
      postReturn.ResponseString = null;
      return postReturn;
    }

    // return response from server if any
    postReturn.ResponseString = WebHelpers.StreamToString(response.GetResponseStream());
    return postReturn;
  }
  catch (Exception ex)
  {
    ex.Data.Add("Uri", request.RequestUri);
    ex.Data.Add("Verb", request.Method);
    ex.Data.Add("ContentType", request.ContentType);
    ex.Data.Add("ContentLength", request.ContentLength);

    if (ex is WebException)
    {
      HttpWebResponse webResponse = (HttpWebResponse)((WebException)ex).Response;
      ex.Data.Add("StatusDescription", webResponse.StatusDescription);
      ex.Data.Add("StatusCode", webResponse.StatusCode);
    }

    Console.WriteLine("Error in getting data for URI " + uri);
    Console.WriteLine(ex.Message);
    // log exception data if desired...
    return null;
  }
  finally
  {
    if (postStream != null) postStream.Close();
    if (streamResponse != null) streamResponse.Close();
    if (streamRead != null) streamRead.Close();
    if (response != null) response.Close();
  }
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/RESTfulPost.cs file of the download

You can use this example as-is to submit POSTs and PUTs to the RESTful web service. It simply takes in a URI and a byte array of a serialized object to POST.

Possible enhancements to this method include creating overloads that take in the object and serialize them to a byte array prior to posting, or even make the method call generic to allow for the return from the POST to be deserialized into an object.

This example doesn't just POST or PUT the data to the server; it also accepts a response back from the server. By convention, this response is the current state of the object after the server completes the POST or PUT. Listing 8.12 includes an object to contain the response from the POST calls to your server so that your mobile application can process them.

The PostReturn object is a simple Data Transfer Object that contains some basic information about the call and its results. The sample code simply uses this information to report on the status or to provide a string for deserializing returned objects for caching. If you need to track more properties for your application, simply add them to the PostReturn class. Listing 8.13 shows the implementation of the PostReturn class.

1.1
Listing 8.13: PostReturn implementation
public class PostReturn
{
  public HttpStatusCode StatusCode { get; set; }
  public string ResponseString { get; set; }
  public Uri Uri { get; set; }
  public string Method { get; set; }
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/PostReturn.cs file of the download

Using the RESTfulPost class is as simple as generating or obtaining an object, changing some property, and then sending the changes to the server via a single method call. Listing 8.14 shows a sample usage of the RESTfulPost class and GenerateCompany method, which you can use in this and future examples.

1.1
Listing 8.14: RESTful PUT/POST usage example
public static string PostCompany()
{
  // get a new company record.
  Company company = GenerateCompany();

  // XML Seralize the company to a byte array for posting to server
  byte[] postBytes = WebHelpers.XmlSerializeObjectToBytes(company);

  // Create new URI
  Uri uri = new Uri("http://localhost/MxDemo/customers/company.xml");

  // post to server and return results if any.
  PostReturn results = Post(uri, postBytes, "application/xml", "POST");
  return results.ResponseString;
}

public static string PutCompany()
{
  // Obtain an existing company records
  Company company
    = RESTfulGet.Get<Company>("http://localhost/MxDemo/customers/7.xml");

  // Make a change to the company record
  company.Name += " - Test Name Change";
  company.PrimaryPhone = "952-555-1212";

  // XML Seralize the company to a byte array for posting to server
  byte[] putBytes = WebHelpers.XmlSerializeObjectToBytes(company);

  // Create new URI
  Uri uri = new Uri("http://localhost/MxDemo/customers/company.xml");

  // post to server and return results if any.
  PostReturn results = Post(uri, putBytes, "application/xml", "PUT");
  return results.ResponseString;
}

public static Company GenerateCompany()
{
  Company company = new Company()
  {
    ID = "99",
    Name = "Tri Marine International, Inc.2",
    Website = "http://www.trimarinegroup.com",
    PrimaryPhone = "425-688-1288",
    PrimaryAddress = new Address()
    {
      ID = "99-a1",
      Description = "World Headquarters",
      Street1 = "10500 N.E. 8th St.",
      Street2 = "Ste. 1888",
      City = "Bellevue",
      State = "WA",
      Zip = "98004"
    },
    Addresses = new List<Address>() 
    {
      new Address() 
      {
        ID="99-a1",
        Description="World Headquarters",
        Street1="10500 N.E. 8th St.",
        Street2="Ste. 1888",
        City="Bellevue",
        State="WA",
        Zip="98004"
      }
    },
    Contacts = new List<Contact>()
    {
      new Contact() 
      {
        ID="99-c1",
        FirstName="Renato",
        LastName="Curto",
        Title="Chairman and CEO",
        Email="[email protected]",
        OfficePhone="425-688-1288",
      },
      new Contact() 
      {
        ID="99-c2",
        FirstName="Steve",
        LastName="Farno",
        Title="CFO",
        Email="[email protected]",
        OfficePhone="425-688-1288"
      }
    }
  };
  return company;
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/RESTfulPost.cs file of the download

Just like the GET calls, you can perform the POSTs and PUTs both synchronously, as in the previous example, or asynchronously, as shown in Listing 8.15, which contains an asynchronous method for POSTing or PUTing data to a server.

1.1
Listing 8.15: Asynchronous RESTful PUT/POST implementation
public class RESTfulPostAsynch
{
  private ManualResetEvent allDone = new ManualResetEvent(false);
  const int DefaultTimeout = 60 * 1000;

  private PostReturn Post(Uri uri, byte[] postBytes, string contentType, string verb)
  {
    RequestState state = new RequestState()
    {
      PostBytes = postBytes
    };

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);

    request.Method = verb;
    request.ContentType = contentType;
    request.ContentLength = postBytes.Length;
    request.KeepAlive = false;

    state.Request = request;

    // Start the asynchronous request.
    IAsyncResult result
      = request.BeginGetRequestStream(new AsyncCallback(RequestCallback), state);

    if (!allDone.WaitOne(DefaultTimeout))
    {
      Console.WriteLine("Call to {0} timed out", state.Uri);
      return null;
    }

    // return response from server if any
    PostReturn postReturn = new PostReturn()
    {
      StatusCode = state.StatusCode,
      ResponseString = state.ResponseString,
      Method = verb,
      Uri = uri
    };

    return postReturn;
  }

  private void RequestCallback(IAsyncResult asynchronousResult)
  {
    // Get and fill the RequestState
    RequestState state = (RequestState)asynchronousResult.AsyncState;
    HttpWebRequest request = state.Request;

    // End the operation
    Stream postStream = request.EndGetRequestStream(asynchronousResult);

    // Write to the request stream.
    postStream.Write(state.PostBytes, 0, state.PostBytes.Length);
    postStream.Close();

    // Start the asynchronous operation to get the response
    IAsyncResult result
      = request.BeginGetResponse(new AsyncCallback(ResponseCallback), state);
  }

  private void ResponseCallback(IAsyncResult result)
  {
    // Get and fill the RequestState
    RequestState state = (RequestState)result.AsyncState;
    try
    {
      HttpWebRequest request = state.Request;

      // End the Asynchronous response and get the actual response object
      state.Response = (HttpWebResponse)request.EndGetResponse(result);

      state.StatusCode = state.Response.StatusCode;

      if (state.StatusCode != HttpStatusCode.OK)
      {
        Console.WriteLine("{0} {1} had status {2}"
           , state.Request.Method, state.Uri, state.StatusCode);
        return;
      }

      state.ResponseString
        = WebHelpers.StreamToString(state.Response.GetResponseStream());
    }
    catch (Exception ex)
    {
      // capture relevant details about the request
      ex.Data.Add("Uri", state.Request.RequestUri);
      ex.Data.Add("Verb", state.Request.Method);

      if (ex is WebException)
      {
        HttpWebResponse response = (HttpWebResponse)((WebException)ex).Response;
        state.StatusCode = response.StatusCode;
        ex.Data.Add("StatusDescription", response.StatusDescription);
        ex.Data.Add("StatusCode", state.StatusCode);
      }
      else
        state.StatusCode = (HttpStatusCode)(-1);

      state.ErrorMessage = string.Format("{0} {1} "
                              , state.Request.Method, state.Request.RequestUri);
      state.ErrorMessage += string.Format("had Exception {0}", ex.Message);
      state.Exception = ex;
      state.Expiration = DateTime.UtcNow;

      // Log exception
    }
    finally
    {
      if (state.Response != null)
        state.Response.Close();
      state.Request = null;

      allDone.Set();
    }
  }

  public class RequestState
  {
    public Uri Uri { get; set; }
    public HttpWebRequest Request { get; set; }
    public HttpWebResponse Response { get; set; }
    public string ResponseString { get; set; }
    public DateTime Expiration { get; set; }
    public HttpStatusCode StatusCode { get; set; }
    public Exception Exception { get; set; }
    public string ErrorMessage { get; set; }
    public byte[] PostBytes { get; set; }
  }

  public static string PostCompanyAsynch()
  {
    // get a new company record.
    Company company = RESTfulPost.GenerateCompany();

    // XML Seralize the company to a byte array for posting to server
    byte[] postBytes = WebHelpers.XmlSerializeObjectToBytes(company);

    // Create new URI
    Uri uri = new Uri("http://localhost/MxDemo/customers/company.xml");

    // post to server and return results if any.
    RESTfulPostAsynch restfulPostAsynch = new RESTfulPostAsynch();

    PostReturn results 
      = restfulPostAsynch.Post(uri, postBytes, "application/xml", "POST");
    return results.ResponseString;
  }

  public static string PutCompanyAsynch()
  {
    // Obtain an existing company records
    Company company 
      = RESTfulGet.Get<Company>("http://localhost/MxDemo/customers/7.xml");

    // Make a change to the company record
    company.Name += " - Test Name Change";
    company.PrimaryPhone = "952-555-1212";

    // XML Seralize the company to a byte array for posting to server
    byte[] putBytes = WebHelpers.XmlSerializeObjectToBytes(company);

    // Create new URI
    Uri uri = new Uri("http://localhost/MxDemo/customers/company.xml");

    // post to server and return results if any.
    RESTfulPostAsynch restfulPostAsynch = new RESTfulPostAsynch();

    PostReturn results
      = restfulPostAsynch.Post(uri, putBytes, "application/xml", "PUT");
    return results.ResponseString;
  }

}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/RESTfulPostAsynch.cs file of the download

The last request method in this chapter is the RESTful DELETE. DELETEs are self-explanatory in that their purpose is to remove information that is no longer required. They are simpler to implement than GETs and POSTs because they neither send nor receive object information from the RESTful server.

All that you need to send a delete is the URI along with the HTTP verb of DELETE, as shown in the sample DELETE in Listing 8.16.

1.1
Listing 8.16: Synchronous RESTful DELETE implementation
public static void Delete(Uri uri)
{
  // Create the request object
  HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);

  // Set values for the request back
  request.Method = "DELETE";

  HttpWebResponse response = null;
  try
  {
    response = (HttpWebResponse)request.GetResponse();

    if (response.StatusCode != HttpStatusCode.OK)
      Console.WriteLine("{0} {1} returned status {2}"
                           , request.Method, uri, response.StatusCode);
  }
  catch (Exception ex)
  {
    ex.Data.Add("Uri", request.RequestUri);
    ex.Data.Add("Verb", request.Method);

    if (ex is WebException)
    {
      HttpWebResponse webResponse = (HttpWebResponse)((WebException)ex).Response;
      ex.Data.Add("StatusDescription", webResponse.StatusDescription);
      ex.Data.Add("StatusCode", webResponse.StatusCode);
    }

    Console.WriteLine("Error in deleting uri " + uri);
    Console.WriteLine(ex.Message);
    // log exception data if desired...
    return;
  }
  finally
  {
    if (response != null) response.Close();
  }
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/RESTfulDelete.cs file of the download

Use of the synchronous RESTful DELETE is as simple as creating the proper URI and calling the delete method, as shown in Listing 8.17.

1.1
Listing 8.17: Synchronous RESTful DELETE usage sample
public static void DeleteCompany()
{
  Uri uri = new Uri("http://localhost/MxDemo/customers/9");
  RESTfulDelete.Delete(uri);
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/RESTfulDelete.cs file of the download

You can also asynchronously perform RESTful DELETEs, as shown in Listing 8.18.

1.1
Listing 8.18: Asynchronous RESTful DELETE implementation
public class RESTfulDeleteAsynch
{
  private ManualResetEvent allDone;

  const int DefaultTimeout = 60 * 1000;  // default a 60 second timeout.

  private void Delete(Uri uri)
  {
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    allDone = new ManualResetEvent(false);

    request.Method = "DELETE";

    RequestState state = new RequestState()
    {
      Request = request,
      Uri = uri
    };

    // Start the asynchronous request.
    IAsyncResult result
      = request.BeginGetResponse(new AsyncCallback(ResponseCallback), state);

    if (!allDone.WaitOne(DefaultTimeout))
    {
      Console.WriteLine("Delete {0} timed out", state.Uri);
      return;
    }

    return;
  }

  private void ResponseCallback(IAsyncResult result)
  {
    // Get and fill the RequestState
    RequestState state = (RequestState)result.AsyncState;
    try
    {
      HttpWebRequest request = state.Request;

      // End the Asynchronous response and get the actual response object
      state.Response = (HttpWebResponse)request.EndGetResponse(result);

      state.StatusCode = state.Response.StatusCode;

      if (state.StatusCode != HttpStatusCode.OK)
        Console.WriteLine("DELETE {0} had status {1}", state.Uri, state.StatusCode);

      return;
    }
    catch (Exception ex)
    {
      // capture relevant details about the request
      ex.Data.Add("Uri", state.Request.RequestUri);
      ex.Data.Add("Verb", state.Request.Method);

      if (ex is WebException)
      {
        HttpWebResponse response = (HttpWebResponse)((WebException)ex).Response;
        state.StatusCode = response.StatusCode;
        ex.Data.Add("StatusDescription", response.StatusDescription);
        ex.Data.Add("StatusCode", state.StatusCode);
      }
      else
        state.StatusCode = (HttpStatusCode)(-1);

      state.ErrorMessage = string.Format("DELETE {0} ", state.Request.RequestUri);
      state.ErrorMessage += string.Format("had Exception {0}", ex.Message);
      state.Exception = ex;

      // Log exception
    }
    finally
    {
      if (state.Response != null)
        state.Response.Close();
      state.Request = null;

      allDone.Set();
    }
  }

  public class RequestState
  {
    public Uri Uri { get; set; }
    public HttpWebRequest Request { get; set; }
    public HttpWebResponse Response { get; set; }
    public HttpStatusCode StatusCode { get; set; }
    public Exception Exception { get; set; }
    public string ErrorMessage { get; set; }
  }

}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/RESTfulDeleteAsynch.cs file of the download

You can use the RESTfulPost class methods to perform the DELETE. This works but is more expensive because you serialize an object to be posted to the server. Depending on your RESTful service requirements, this may be necessary, for instance, in the case in which the service needs to inspect the object to confirm whether a delete is allowed.

In the preceding examples and discussion, you learned a lot about RESTful calls to the server so that your mobile application can have the data it needs to operate. All these examples carry the assumption that your mobile application is connected to the server and can carry out these calls. But connectivity isn't guaranteed, and your application needs to be prepared for those times when connectivity isn't available. The next section describes how to manage your data and corresponding interactions in a disconnected setting.

Working Disconnected

Although the previous examples describe in detail how to make RESTful web requests to retrieve, update, and delete information, they all carry the same assumption: The user is connected to the network and has all Intranet/Internet access and connectivity needed for the application to run as designed. Whether the application is on a desktop connected to a LAN or a laptop connected via wireless at a local coffee shop, there's an assumption that the connection will be available when and how it's needed.

However, that assumption of connectedness is just that, an assumption; one that you shouldn't rely upon to a great extent. You could rewrite an old adage to reflect this notion. “All mobile users will be connected some of the time and some mobile users will be connected all of the time, but not all mobile users will be connected all the time.”

Because connection cannot be guaranteed all the time, a mobile application developer has a couple options. One is for the application to simply not function completely when connections are not available. The other option is to write the application so that it can adequately function even when disconnected. The next section begins the discussion on when and how to cache data on the mobile applications.

Caching Data

Assume that the major need for connectedness is that the mobile applications use data obtained from a server, and specifically in these examples, a RESTful server. For a mobile application to function adequately when disconnected, it needs to have its data available for the user when and where the user needs it. After all, that's what mobile applications are for, right?

To meet this need, the mobile applications need to cache the data on the device so it's there when the user needs it, even if the connection isn't. Ultimately, consider two forms of caching for mobile applications: Volatile Cache and Persistent Cache, which are both detailed in Table 8.3.

Table 8.3 Mobile Device Cache Variants

Cache Type Description
Volatile Cache Cache is maintained in the device's memory. It provides quicker access to the cached data at the expense of not persisting between application and/or device restarts.
Persistent Cache Cache is maintained in the device's physical storage. It maintains the cached data between application and/or device sessions but doesn't perform as well.
No Cache Cache is not maintained. This option is included to call attention to the idea that not all data should be cached.

Volatile Cache is stored in the memory of the device and persists cached data only while the mobile application runs and doesn't persist between application or device restarts. Persistent Cache, on the other hand, maintains cached data on the physical storage of the device so that it remains available between application sessions and device restarts. Although No Cache isn't actually a caching type per se, it is a necessary consideration because there are cases in which certain data does not need to be cached and indeed must be deliberately excluded from caching, depending on the mobile application's business constraints.

Standardizing Cache Interface

Because you can cache data on the device in many ways, standardize all the caching mechanisms with a common interface. This allows for changing out the caching mechanism by platform or by application while minimizing any impact to the business logic, especially if you program to the cache interface rather than any particular implementation.

For example, you most likely need to implement a file-system persistent cache differently on an iOS Touch platform than you would on an Android or Windows Phone 7 platform. This is because they each have unique requirements for storing files used by the application.

Caching Mobile Data In-Memory

Recall that one type of cache is Volatile Cache. It hangs around in memory for as long as the application is running. When the application stops, this type of memory goes away and needs to be re-created when the application restarts.

The primary advantage of Volatile Cache is that it is faster to retrieve data than other methods because the objects are currently instantiated and the cache simply returns a reference to the object. Of course, this means that the objects in the volatile cache are actually consuming memory. This is not a problem for applications running on platforms with a lot of memory available. However, that doesn't apply to most mobile devices, where utilization and consumption of memory needs to be closely watched to prevent adverse impacts on the application, such as the operating system shutting down the application for using too much memory.

Listing 8.19 displays one possible implementation of a Volatile Cache.

1.1
Listing 8.19: Volatile Cache implementation
public class VolatileCache
{
  Dictionary<string, Customer> _cache = new Dictionary<string, Customer>();

  public void Store(string key, Customer customer)
  {
    if (string.IsNullOrEmpty(key))
      throw new ArgumentNullException("key");

    _cache[key] = customer;
  }

  public Customer Fetch(string key)
  {
    if (string.IsNullOrEmpty(key))
      throw new ArgumentNullException("key");

    if (!_cache.ContainsKey(key))
      return null;

    return _cache[key];
  }

  public void Clear(string key)
  {
    if (string.IsNullOrEmpty(key))
      throw new ArgumentNullException("key");

    if (_cache.ContainsKey(key))
      _cache.Remove(key);
  }

  public void ClearCache()
  {
    _cache = new Dictionary<string, Customer>();
  }
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/cache.cs file of the download

You can implement a volatile cache in many ways besides the one shown. For example, you could use a List<Customer> instead of a Dictionary<string, Customer> as the data type of the cache storage object in memory. Or you could use a custom cache key object in place of a string. Or… you get the idea.

Usage of the VolatileCache class is straightforward, as shown in Listing 8.20.

1.1
Listing 8.20: Volatile Cache usage
public static void VolatileCacheSample()
{
  VolatileCache cache = new VolatileCache();
  Customer customer = RESTfulPost.GenerateCompany();

  // key could be any unique string like customer ID, or even the URL, 
  // just so long as it is unique
  string key = customer.ID;

  // put item into cache directly
  cache.Store(key, customer);

  // get item from cache
  Customer customer2 = cache.Fetch(key);

  // remove item from cache
  cache.Clear(key);

  // clear entire cache
  cache.ClearCache();
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/cache.cs file of the download

Caching Mobile Data Persistently

Storing data in volatile cache is all well and good until you actually have to restart the application, and then all the data in the volatile cache goes away in a puff of binary smoke. This can leave the mobile application without the data it needs to function properly, especially if the fickle network connection happens to be unavailable at the same time.

This leaves your applications with a need for a more durable cache storage mechanism, a persistent cache. You can implement the persistent cache in several different ways. You can write it as a simple file storage writing and reading class, which contains its own variations per platform. You can add it to a volatile cache implementation to save the cached data for later restoration after an application restart. You can even write it as a front end to a mobile device data storage database application, such as SQLite, or even a mobile database that you developed.

Listing 8.21 displays one possible implementation of a persistent cache using a simple file storage approach.

1.1
Listing 8.21: Persistent Cache implementation
public class PersistentCache
{
  private static string
    RootPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
  private static string CachePath = Path.Combine(RootPath, "MobileCache");

  private string FileName(string key)
  {
    // file path is {CachePath}{type}{key}
    return Path.Combine(CachePath, "Customer", key);
  }

  public void Store(string key, Customer item)
  {
    if (string.IsNullOrEmpty(key))
      throw new ArgumentNullException("key");

    WebHelpers.WriteToFile<Customer>(item, FileName(key));
  }

  public Customer Fetch(string key)
  {
    if (string.IsNullOrEmpty(key))
      throw new ArgumentNullException("key");

    return WebHelpers.ReadFromFile<Customer>(FileName(key));
  }

  public void Clear(string key)
  {
    if (string.IsNullOrEmpty(key))
      throw new ArgumentNullException("key");

    File.Delete(FileName(key));
  }

  public void ClearCache()
  {
    string path = WebHelpers.DirectoryName(FileName(string.Empty));

    if (Directory.Exists(path))
      Directory.Delete(path, true);
  }
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/cache.cs file of the download

Usage of the persistent cache sample is the same as the volatile cache usage with the simple replacement of the cache variable to a new PersistentCache object rather than a VolatileCache object, as shown in Listing 8.22.

1.1
Listing 8.22: Persistent Cache usage
public static void PersistentCacheSample()
{
  PersistentCache cache = new PersistentCache();
  Customer customer = RESTfulPost.GenerateCompany();

  // key could be any unique string like customer ID, or even the URL, 
  // just so long as it is unique
  string key = customer.ID;

  // put item into cache directly
  cache.Store(key, customer);

  // get item from cache
  Customer customer2 = cache.Fetch(key);

  // remove item from cache
  cache.Clear(key);

  // clear entire cache
  cache.ClearCache();
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/cache.cs file of the download

Securing Mobile Data (Encryption)

When you cache data onto a file system so it can persist between application sessions, you need to consider the value of the data and what you should do to protect it.

If you need to store sensitive enterprise data, you need to manage two different basic threats to that data. The first threat you need to manage is to prevent unauthorized access to the data (that is, prevent theft of the data). The second is to prevent unauthorized changes to the data (that is, prevent tampering with the data).

You can handle both of these objectives via encryption. If your mobile device hardware supports it, you can encrypt the data stored at a file-system level. Or, when you write your persistent cache objects, you can encrypt the files when they are written to the file system and decrypt them when they are needed. This adds to the overhead of the caching, but the trade-off is better protection of the data.

The stronger encryption/decryption algorithms (such as AES-256) utilize a combination of a key and salt to make cracking the encryption considerably more difficult. This however leads to an inescapable question of where to store the key and salt so they can be used for encryption.

You could store the data on the RESTful server and give it to the application only when an HTTPS call can be safely made. But wait, that defeats the purpose of caching the data in the first place because there is little value in caching data that you can access only while connected.

Or, you could store the key and salt on the device so it's available for decrypting the cached data. Then, however, you run into the problem of how to protect the key and salt. You can't store it in plain text because that's like giving the keys to the kingdom to anyone who can find the file. So what do you do? One solution is to use a second level of encryption to protect the key and salt and base the secondary key and salt on device-level information (such as a device hardware ID) combined with a known user ID or password/PIN that the user must enter.

Another consideration to protect the data is that you may have multiple users of the same mobile device. Normally, you would expect just one user, but suppose a few sales reps meet at a convention for coffee (or drinks; they are sales reps after all). One rep needs to confirm that a particular sale was fulfilled. Having a single primary key for the application is no longer sufficient if you need to isolate one user's set of data from another's. It's also worthwhile to provide each user with a separate and distinct encryption key and salt so that if one of the users' information is compromised, the other users' information is still protected.

Not Caching Mobile Data

This chapter covers a lot about caching data so it's available whenever and wherever you need it. So, it would seem a logical thought that you would need to cache everything on the device all the time. Who wouldn't want to have all their data at their fingertips regardless of whether their device is connected? Sounds like the best of all possible worlds, doesn't it?

Not so fast. You see, there are many times in which it doesn't make sense to cache data on the device, or rather, where it makes sense to NOT cache the data.

First, there are obvious device limitations in terms of memory and storage space that you need to manage. We cover those limitations later in this chapter.

Secondly, the data might be of such a sensitive and classified nature that the risk is too high to cache the data even if it's protected by encryption. This might include the information that poses a competitive risk, such as customer account information or sales data, perhaps. It might also include financial information that might have legal or regulatory compliance requirements.

In the medical industry, for example, patient information is sacrosanct. With the Health Insurance Portability and Accountability Act (HIPAA) requirements, only those people who need to know a person's health information are allowed to know it. Failing to meet this requirement can come at a serious cost in the form of fines and lawsuits to any company storing this information inappropriately. Some hospitals forbid the storage of patient information on any mobile device allowed on their premises, regardless of how well the data may be encrypted.

You need to carefully consider the type of information you want to cache and take into account any business and legal ramifications before caching data on mobile devices.

Queuing Data to Server

This chapter provided much discussion around how and when to cache data so mobile applications can have data when and where it is needed. However, having the data available is only part of what is required for mobile applications to run when disconnected.

The other part is how to save the data and post changes back the server, whether it's adding a new company object, changing the price of a product, or removing an order that is no longer required. There needs to be a way to send the information back to the RESTful server.

Yes, this is what RESTful POSTs and PUTs are for. However, if your network connection is not active, then you still need a means to store any changes on a mobile device until a network connection is available for them to be POSTed to the server.

You can accomplish this in C# by using a Queue<T> to hold the data and then process each item on the Queue<T> while the connections are active. When connections aren't active, the queue processing should halt until the connection resumes.

You can retrigger the queue processing in several ways. You can use a System.Timer and regularly trigger the queue processing. Although the System.Timer approach works well, it has the drawback of its associated overhead. If your application is queuing only one or two data types, then you'd have one or two system timers running, but if you are queuing a couple dozen objects, you would then have a couple dozen Timer threads running, which would be a drain on the application's performance (and on the device's battery) due to the extra processing. Of course, you could also create a scenario in which you could manage all the queues from a single thread, which would also work.

It's worth considering a simpler option of starting the queue processing every time a new item is added to the queue and also when the mobile application restarts. These approaches are simple to implement and work quite well, even in an enterprise production setting. The trade-off with this approach is that while there is less overhead, there's also a potential for the posting to wait a longer time for the next data change or application restart. But remember, when talking about disconnected data, you cannot predict when the connection will return, so that might not be as big a drawback as it would seem otherwise. Take into account how reliable your connectivity is and consider adding an event handler that executes when the network reconnects.

The following samples of the CustomerQueue trigger the queue processing when a new item is added and provide a method to call when the application restarts. Listing 8.23 displays the implementation of the CustomerQueue class.

1.1
Listing 8.23: CustomerQueue implementation
public class CustomerQueue
{
  Queue<Customer> _queue = new Queue<Customer>();
  private object _lock = new object();

  // set up events for response.
  public delegate void PostComplete(PostReturn postReturn);
  public event PostComplete OnPostComplete;

  public bool Enabled { get; set; }

  public void PostItem(Customer customer)
  {
    lock (_lock)
    {
      _queue.Enqueue(customer);
    }
    ProcessQueue();
  }

  // process queue
  public void ProcessQueue()
  {
    // ProcessQueue queue items and only serialize queue when there 
    // is an error and the processing is halted.
    HttpStatusCode statusCode;
    bool exitLoop = false;

    // if not enabled, then serialize the queue and return
    if (!Enabled)
    {
      SerializeQueue();
      return;
    }

    lock (_lock)
    {
      int qCount = _queue.Count;
      if (qCount <= 0) return;

      while (_queue.Count > 0 && !exitLoop)
      {
        Customer nextCustomer = _queue.Peek();

        if (nextCustomer == null)
        {
          _queue.Dequeue();
          continue;
        }

        try
        {
          statusCode = PostRequest(nextCustomer);

          if (statusCode == HttpStatusCode.OK)
          {
            // post was successful, remove customer from queue
            _queue.Dequeue();
          }
          else
          {
            // post not successful.
            Console.WriteLine("POST Customer returned status {0}", statusCode);
            _queue.Dequeue();
          }

        }
        catch (Exception exc)
        {
          // Log exception 
          Console.WriteLine("Customer Post encountered exception {0}", exc.Message);
        }
      }
      SerializeQueue();
    }
  }

  private HttpStatusCode PostRequest(Customer customer)
  {
    byte[] postBytes = WebHelpers.XmlSerializeObjectToBytes(customer);

    Uri uri = new Uri("http://localhost/MxDemo/customers/customer.xml");
    PostReturn postReturn
      = RESTfulPost.Post(uri, postBytes, "application/xml", "POST");

    // handle post return as needed. XML used for example
    Customer customerReturn = null;
    if (!string.IsNullOrEmpty(postReturn.ResponseString))
      customerReturn = WebHelpers.DeserializeXml<Customer>(postReturn.ResponseString);

    // post return to the post complete event
    if (OnPostComplete != null)
      OnPostComplete(postReturn);

    return postReturn.StatusCode;
  }

  private readonly string QueueFileName
    = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal),
                                              "Queue", "Customer_queue.xml");

  // serialize queue, convert to List for Serialization
  public void SerializeQueue()
  {
    lock (_lock)
    {
      if (_queue.Count > 0)
        WebHelpers.WriteToFile<List<Customer>>(_queue.ToList(), QueueFileName);
      else
        File.Delete(QueueFileName);
    }
  }

  // deserialize queue, 
  public void DeserializeQueue()
  {
    lock (_lock)
    {
      List<Customer> queuelist
        = WebHelpers.ReadFromFile<List<Customer>>(QueueFileName);

      if (queuelist == null)
        return;

      queuelist.ForEach(_queue.Enqueue);
    }
  }

  // remove queue file, 
  public void DeleteQueue()
  {
    lock (_lock)
    {
      File.Delete(QueueFileName);
    }
  }
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/queue.cs file of the download

The CustomerQueue class is responsible for storing objects until they get POSTed. To preserve the data between application sessions and reboots, the items on the queue are serialized to the file system, so they're available for deserialization when needed. Also, there are controls to allow the consuming methods to determine when the queue processing is allowed.

Depending on the response returned from the server, sometimes the item is removed from the queue. For successful postings, the item is removed to make room for any subsequent items to be POSTed next. If there is an error connecting to the server, it doesn't remove the item from the queue but does halt the queue processing, so the system doesn't continually retry to post data when the servers are unavailable, for example. The remaining errors assume that the posting was rejected by the RESTful server and thus get removed. All POST attempts call the OnPostComplete event so that any calling application can respond as needed.

Listing 8.24 shows a sample method for how to use the CustomerQueue class and process the response from the server.

1.1
Listing 8.24: Sample usage of the CustomerQueue class
public class QueueSample
{
  public static void PostQueueSample()
  {
    // create new customer Queue
    CustomerQueue queue = new CustomerQueue();

    // subscribe to Post Complete event.
    queue.OnPostComplete += new CustomerQueue.PostComplete(queue_OnPostComplete);

    // obtain customer object
    Customer customer
      = RESTfulGet.Get<Customer>("http://localhost/MxDemo/customers/1.xml");

    // make a simple change
    customer.Name += " - Test";

    // post change to customer object
    queue.PostItem(customer);

    // start queue processing
    queue.ProcessQueue();
  }

  static void queue_OnPostComplete(PostReturn postReturn)  {
    Console.WriteLine("{0} to {1}", postReturn.Method, postReturn.Uri.AbsoluteUri);
    Console.WriteLine("returned status {0}", postReturn.StatusCode);
    Console.WriteLine("With the following results:");
    Console.WriteLine(postReturn.ResponseString);
  }
}

Found in the CustomerManagement.DataServices/CustomerManagement.Samples/queue.cs file of the download

Device Resource Considerations

This chapter covered a lot about making RESTful calls and keeping the data cached so that the applications can work properly even while disconnected from the network and servers. So, all you need to do is simply download and cache every object that you might possibly need so that all the data for every conceivable need is at your fingertips at all possible times, right?

Definitely not!

Today's mobile devices have problems similar to the original personal computers in that their resources are limited. Resources such as memory and network bandwidth are certainly more constrained on mobile devices than the modern PCs.

Mobile devices, whether iPads or Androids, monitor memory usage closely. Even when your application is well behaved in terms of memory consumption, the OS closes your application if it's been idle and the device thinks the memory your application is using is needed elsewhere. Of course, if your application consumes too much memory, the devices won't think twice before closing your application.

Depending on where your mobile device is, the wireless connection may be sketchy at best, and the edge bandwidth on 3G isn't always the fastest. So you need to minimize the size and quantity of objects you download and ensure the objects that you do download contain only those fields and properties essential for the applications to function. Downloading data that you don't need only consumes excess memory and slows down the performance of the network calls.

Managing Memory/File System Consumption

Each mobile device has its own set of memory specifications, and instead of delving into the specifics of each device, in this section we review some principles for managing memory. These are principles you've heard before, of course, but they are still worth mentioning.

  • Keep your objects as small as possible. Include only the details you need, not those that you might need at a later date.
  • Add details to your objects only when they are needed.
  • Create the objects only when you need them, and then allow them to go out of scope as soon as you finish with them.
  • Restrict the scope of variables to the smallest scope possible. Use local variables rather than class variables, especially if the object is going to be around for awhile.
  • Be extra vigilant about memory leaks.

Managing Network Bandwidth

Although there is little your application can do to improve the performance of the network connections, consider a few ideas about how you can use that scarce network bandwidth.

Two primary strategies for managing the network bandwidth are to simply reduce the size and the frequency of the download. The smaller the payload of the service call, the less time it takes to complete. So, limit your lists of objects to only those items needed to present the list in the application, and reserve more detailed information for the individual objects in their separate calls.

In addition, you should limit the size of the lists to only what you need for the application to function. This could include filtering search result sets on the server or just sending one page of results at a time.

If an application requires a large set of data, then focus on shrinking the data to as small as possible. Although XML is useful, perhaps you can consider using pipe-delimited strings instead of XML records. (Yes, that's rather old school, but it is smaller.)

Another option is to use the compression utilities built into the WebRequest calls. WebRequest calls can support GZIP and Deflate compression using the AutomaticDecompression property. These compression types are expressed in the enum DecompressionMethods.

Often the most effective means to reduce network consumption is simply to exclude details that are not absolutely essential to the function of the application. If you're in doubt about including a particular data item in your service call, just remember the YAGNI principle: You Aren't Going To Need It. Okay, so you might, but be certain to establish the clear need for an item before burdening your application with it.

Summary

In this chapter, you learned how to make RESTful calls from your mobile applications and how to obtain data, modify it, and send the changes back to the RESTful servers. In addition, you learned how to cache the data so that it's available even if the network connection to the server is unavailable so that your mobile applications can still function correctly even while disconnected.

In the next chapter, you learn more about how to code for device-specific functions, such as audio, camera, and geo-location.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.123.73