Chapter 10

Using MonoCross Utilities

What's In This Chapter?

  • Surveying the MonoCross Utilities
  • Encrypting sensitive data
  • Storing objects to the file system
  • Serializing objects
  • Tracking application events
  • Utilizing network functions
  • Multithreading MonoCross applications

Earlier chapters discussed the MonoCross framework and how to use it to design and build cross-platform applications. You learned how to create data services as well as how to consume them. Now it's time to explore the MonoCross Utilities.

The MonoCross Utilities are a collection of service functions that perform basic programming tasks using a common set of interfaces. They are cross-platform in that the same usage in your application correctly functions without the need to write custom code for each platform. This chapter introduces you to the MonoCross Utilities, delves into their interfaces, and clearly and simply shows how to use them in your applications.

The MonoCross Utilities are available for evaluation at www.monocross.net. See Chapter 3, “Setting Up Your Development Environment” for more information on how to download and install the MonoCross Utilities.

Understanding MonoCross Utilities

The MonoCross Utilities are a collection of interfaces that you use to perform common application functions, such as saving files, serializing objects, making calls to RESTful web service endpoints, and so forth. When you use the MonoCross Utilities in your code, you program to the common interfaces of the utilities rather than particular platform-specific implementations.

The MonoCross Utilities are a series of interfaces that have been implemented for each mobile platform. These utilities allow you to code your application once for all platforms, rather than once for each platform. Table 10.1 summarizes the cross-platform interfaces contained within the MonoCross Utilities.

Table 10.1 MonoCross Utilities

Utility Description
Encryption Encrypts and decrypts strings, byte arrays, and streams
Logging Manages the logging of application events
Network Performs web service requests and provides simple and convenient access to a RESTful endpoint for GET and POST commands
Serializer Supports the transformation of objects in memory to and from strings, byte arrays, and files
Storage Stores and retrieves files from persistent storage and performs other file system-related tasks
Threading Supports the background execution of methods in a direct call as well as a worker pool and an idle pool

You can access all the MonoCross Utilities from a single static class called MonoCross.Utilities.MXDevice. A static accessor method exists for each. For example, you can call the MonoCross File Utility functions via methods on the MXDevice.File interface described in the “Using File Storage” section later in this chapter.

When an application needs to interface with file storage, it makes certain assumptions about where to store and access data. On Windows, it might be in a temporary folder such as C:Temp, whereas on iOS it is within an isolated application sandbox. This leads to the obvious question: How can you perform cross-platform file interactions if the root folder is different for each platform?

This is where the MXDevice.DataPath comes in. MonoCross uses the DataPath abstract property to establish the root folder for file storage interactions. When you specify a file in your application code to read or write, use the MXDevice.DataPath as part of the filename, instead of such things as C: emp or other platform-specific notation.

If necessary, the application code can set the MXDevice.DataPath property; it defaults to a cross-platform value if it is not otherwise set.

You learn how to extend the MXDevice.DataPath property for use in the code examples in the “Using File Storage” and “Serializing Objects” sections later in this chapter. In the examples, a new property, named RootPath, extends the MXDevice.DataPath as the root folder for storing and retrieving files so that you can use the examples on any platform. Listing 10.1 displays the code that defines the RootPath property.

1.1
Listing 10.1: RootPath Property
public static string RootPath
{
  get
  {
    // AppendPath simplifies appending subfolders to strings
    return MXDevice.DataPath.AppendPath( "MonoCross.Utilities.Samples" );
  }
}

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

Encrypting Application Information

Applications deal in data, and sometimes these data are sensitive and/or confidential and need to be protected from prying eyes, while at the same time be readily available for those who have access permission. Whether the data is customer details in a business application or patient information in a medical application, you need to protect the data.

The MonoCross Encryption Utility provides a simple, yet effective, means to encrypt your application's data. Because many texts cover encryption as an advanced topic, you may ask why this chapter covers encryption first among all the MonoCross Utilities. The answer is that both the MonoCross File Utility and the MonoCross Serialization Utility heavily use the MonoCross Encryption Utility, so it makes sense to cover it first.

Understanding the Encryption Utility

The MonoCross Encryption Utility encrypts and decrypts string, byte[], and Stream objects. A complete coverage of encryption is beyond the scope of this text, but this chapter covers how the MonoCross Encryption Utility abstracts and simplifies the encryption functionality provided by .NET for use across platforms. The utility contains several methods designed to be fully functional with just a single line of code.

The MonoCross Encryption Utility leverages .NET support for Advanced Encryption Standard (AES256). When used with sufficiently strong key and salt values, this encryption is secure enough to meet current enterprise security standards.

The MonoCross Encryption Utility supports three separate modes of encryption. The Default Encryption specifies application-level settings that indicate whether encryption is required as well as the default encryption key and salt. Specific Encryption and No Encryption are exceptions to the Default Encryption settings. Table 10.2 further defines these modes.

Table 10.2 MonoCross Encryption Utility Modes

Encryption Mode Description
Default Encryption Manages application-level encryption settings, specifying whether encryption is required — and if so, the key and salt to use.
Specific Encryption Provides an exception to the Default Encryption settings by enabling you to use an alternative key and salt to encrypt and decrypt information.
No Encryption Provides another exception to Default Encryption settings by ensuring that no encryption or decryption be used in that mode. You primarily use this mode within the MonoCross File and Serialization Utilities.

In code, you express these encryption modes as enum EncryptionMode with Default, Encryption, and NoEncryption values.

You can find the MonoCross Encryption Utility within the MonoCross.Utilities.Encryption namespace. Table 10.3 describes the available methods.

Table 10.3 MonoCross Encryption Utility

Method Description
DecryptBytes Decrypts a byte[] into a byte[]using the default key and salt. Method overloads support the use of specified key and salts.
DecryptStream Decrypts a stream into a stream using the default key and salt. Method overloads support the use of specified key and salts.
DecryptString Decrypts a string into a string using the default key and salt. Method overloads support the use of specified key and salts.
EncryptBytes Encrypts a byte[]into a byte[]using the default key and salt. Method overloads support the use of specified key and salts.
EncryptStream Encrypts a stream into a stream using the default key and salt. Method overloads support the use of specified key and salts.
EncryptString Encrypts a string into a string using the default key and salt. Method overloads support the use of specified key and salts.
Key String property indicating the default encryption key to use for encrypting and decrypting information.
Required Boolean to indicate whether encryption is required for the current application instance.
Salt Byte[] property indicating the default salt to use for encrypting and decrypting information.

Putting the Encryption Utility to Work

The MonoCross Encryption Utility abstracts and simplifies data encryption into easy-to-use functions. The utility becomes slightly more complicated when you use Specific Encryption and No Encryption modes.

Start with Default Encryption. To activate encryption for the entire application, you need to set Encryption to Required (via the MXDevice.Encryption.Required property) and populate an application level key and salt for performing encryption, MXDevice.Encryption.Key and MXDevice.Encryption.Salt, respectively.

Listing 10.2 shows sample methods that demonstrate how to enable and disable Default Encryption. You also use these methods in later samples in this section.

1.1
Listing 10.2: Enable/Disable Default Encryption
public static void EnableEncryption()
{
  // Activate Default Encryption
  MXDevice.Encryption.Required = true;
  // This key and salt is for example purposes only. Change your own key and salt.
  MXDevice.Encryption.Key = "llMkihr6tN4JO16LAdGJsEEdy6m+/vNqy2rYQuydBk2=";
  MXDevice.Encryption.Salt = Convert.FromBase64String( "mDE9r+ACKTqVwFc6nTdPbl==" );
}

public static void DisableEncryption()
{
  // Deactivate Default Encryption
  MXDevice.Encryption.Required = false;
  MXDevice.Encryption.Key = String.Empty;
  MXDevice.Encryption.Salt = new byte[] { };
}

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

Now that the Default Encryption is set up, it's a simple call to one of the EncryptXXX() or DecryptXXX() methods located within MXDevice.Encryption. Listing 10.3 demonstrates how to encrypt and decrypt a string.

1.1
Listing 10.3: Encrypting and Decrypting Text
public static void TextWithDefaultEncryption()
{
  string text = "A quick little critter went to pick up litter.";

  // enable Default Encryption, 
    // note this can be performed when application starts 
    // and only needs to be performed once.
   EnableEncryption();

  string encrypted = MXDevice.Encryption.EncryptString( text );
  string decrypted = MXDevice.Encryption.DecryptString( encrypted );
}

public static void TextWithSpecificEncryption()
{
  string key = "kjWkslr4qw4UT28LAfKJsYIdy9m+/vHry3fYDuydGk2=";
  byte[] salt = Convert.FromBase64String( "lRG7r+BNLTqVcKq6pTsMfi==" );

  string text = "A quick little critter went to pick up litter.";

  // Example uses alternate key and salt properties.
  string encrypted = MXDevice.Encryption.EncryptString( text, key, salt );
  string decrypted = MXDevice.Encryption.DecryptString( encrypted, key, salt );
}

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

Using File Storage

The MonoCross File Utility simplifies the basic tasks required for file system interactions. This includes saving to and reading from files as well as moving/copying files, creating directories, and many other file system needs.

Understanding the File Utility

The MonoCross File Utility stores and reads text, byte arrays, and streams to files. The MonoCross File Utility also integrates with the MonoCross Encryption Utility and follows the Default Encryption settings for applying encryption to file storage. For instance, your application may have the requirement to encrypt any file stored that contains data of a proprietary or sensitive nature, such as customer information, product and sales data, and so on.

You can store files as encrypted or plain text. If you use Default Encryption, the file is automatically encrypted. Otherwise, you can include a key and salt in the file read and save methods for Specific Encryption and also apply the enum EncryptionMode.NoEncryption where you want plain text interaction.

The MonoCross File Utility manages the application interactions with the file system via a simple, cross-platform interface. You can find the MonoCross File Utility within the MonoCross.Utilities.Storage namespace. Table 10.4 describes the available methods.

Table 10.4 MonoCross File Utility

Method Description
Copy Copies a file, either to a new file in the same directory or to a new directory.
CopyDirectory Copies a directory including its subdirectory and file contents. Method overloads support overwriting at the destination directory.
CreateDirectory Creates a directory at a specified location.
Delete Removes a file from the file system.
DeleteDirectory Removes a directory from the file system.
DirectoryName Returns the name of the containing directory of a given file or directory.
EnsureDirectoryExists Ensures that a directory exists for a given file or directory path. If the containing directory doesn't exist, this method creates it.
Exists Returns true or false depending on whether the given file exists.
GetDirectoryNames Returns a string array of directory names contained within a given directory path.
GetFileNames Returns a string[] of filenames contained within a given directory path.
Length Returns the length of a given filename.
Move Changes a file's location or renames the file if moving to a different filename in the same directory.
MoveDirectory Changes a directory's location along with its subdirectory and file contents.
Read Reads a file into a byte[]. Method overloads support file decryption.
ReadString Reads a file into a string. Method overloads support file decryption.
Save Saves contents into a named file. Method overloads support multiple content types (string, byte[] and stream). Additional method overloads support encryption.

Putting the File Utility to Work

Using the MonoCross File Utility is simple and focuses on what you intend the code to accomplish rather than on platform-specific implementation details. It automatically handles stream management and other issues. Listing 10.4 demonstrates how to save text and byte arrays to files and read the contents into variables.

1.1
Listing 10.4: Save and Read a File
public static void TextToFile()
{
  string text = "A quick little critter went to pick up litter.";
  string fileName = RootPath.AppendPath( "String.txt" );

  // Save text to file
  MXDevice.File.Save( fileName, text );

  // Read text from file
  string text1 = MXDevice.File.ReadString( fileName );
}

public static void BytesToFile()
{
  byte[] bytes = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 };
  string fileName = RootPath.AppendPath( "Bytes.txt" );

  // Save byte array to file
  MXDevice.File.Save( fileName, bytes );

  // Read byte array from file
  byte[] bytes1 = MXDevice.File.Read( fileName );
}

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

The previous example performs the Save() and Read() methods using the Default Encryption settings, so the files encrypt automatically, if required, with no extra coding on your part. In addition, these settings enable different environments (for example, development versus production) to have different encryption requirements without changing your file interaction code.

Now, suppose you have a situation in which you need to always store certain files as encrypted but with a different encryption key and salt from the Default Encryption settings. Simply add an alternative key and salt to the file ReadXXX() and SaveXXX() methods to support Specific Encryption. Listing 10.5 demonstrates this usage.

1.1
Listing 10.5: Save and Read a File with Specific Encryption
public static void TextToFileWithSpecificEncryption()
{
  string key = "kjWkslr4qw4UT28LAfKJsYIdy9m+/vHry3fYDuydGk2=";
  byte[] salt = Convert.FromBase64String( "lRG7r+BNLTqVcKq6pTsMfi==" );

  string text = "A quick little critter went to pick up litter.";
  string fileEnc = RootPath.AppendPath( "StringWithSpecificEncryption.txt" );

  // Save text to an encrypted file, using specific Key and Salt. 
  MXDevice.File.Save( fileEnc, text, key, salt );

  // Read text from encrypted file with same Key and Salt file was encrypted with.
  string text1 = MXDevice.File.ReadString( fileEnc, key, salt );
}

public static void BytesToFileWithSpecificEncryption()
{
  string key = "kjWkslr4qw4UT28LAfKJsYIdy9m+/vHry3fYDuydGk2=";
  byte[] salt = Convert.FromBase64String( "lRG7r+BNLTqVcKq6pTsMfi==" );

  byte[] bytes = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 };
  string fileEnc = RootPath.AppendPath( "BytesWithSpecificEncryption.txt" );

  // Save byte array to an encrypted file. 
  MXDevice.File.Save( fileEnc, bytes, key, salt );

  // Read byte array from encrypted file
  byte[] bytes1 = MXDevice.File.Read( fileEnc, key, salt );
}

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

Through these examples, you can learn how the MonoCross File Utility enables encryption via Default Encryption and Specific Encryption. Consider one final encryption scenario in which you need to use Default Encryption for the application but you need to save or read a file in a plain text or unencrypted state. This could occur when your application needs to read a file from a different application that perhaps doesn't support encryption or doesn't have your application's key and salt.

You handle this situation with the No Encryption mode. Simply apply the EncryptionMode.NoEncryption enumeration value to the Save() and Read() method calls to ensure that the saves or reads do not use encryption. Listing 10.6 demonstrates how this is done.

1.1
Listing 10.6: Save and Read a File with No Encryption
public static void TextToFileWithNoEncryption()
{
  string text = "A quick little critter went to pick up litter.";
  string fileEnc = RootPath.AppendPath( "StringWithNoEncryption.txt" );

  // Save text to a plain text file, 
  // regardless whether the default encryption settings are active or not. 
  MXDevice.File.Save( fileEnc, text, EncryptionMode.NoEncryption );

  // Read plain text from file, do not decrypt.
  string text1 = MXDevice.File.ReadString( fileEnc, EncryptionMode.NoEncryption );
}

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

The previous examples show how to save and read files both with and without encryption, but the MonoCross File Utility contains additional methods for manipulating files. Listing 10.7 demonstrates how to copy, move, and delete files.

1.1
Listing 10.7: Manipulating Files
public static void FileManipulation()
{
  string text = "A quick little critter went to pick up litter.";
  string fileName = RootPath.AppendPath( "StringManip.txt" );

  // Disable Default Encryption for this this sample
  EncryptionSample.DisableEncryption();

  // Save text to file
  MXDevice.File.Save( fileName, text );

  // Move file
  string fileMove = RootPath.AppendPath( "StringManipMove.txt" );
  MXDevice.File.Move( fileName, fileMove );

  // Copy file
  string fileCopy = RootPath.AppendPath( "StringManipCopy.txt" );
  MXDevice.File.Copy( fileMove, fileCopy );

  // Delete File.
  MXDevice.File.Delete( fileMove );
}

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

Serializing Objects

An application that uses objects needs to be able to share those objects with another application, a web server, or even between sessions of the same application. Serialization is the process of transferring objects from their in-memory representation into some other form and back again.

Understanding the Serializer Utility

The MonoCross Serializer Utility simplifies the serialization of objects and lists of objects through a common cross-platform interface. You can serialize (and deserialize) the objects into strings, byte arrays, and even to files. The SerializationFormat enumeration supports both XML and JSON formats within namespace MonoCross.Utilities.Serialization.

The MonoCross Serializer Utility internally supports both the MonoCross File and Encryption Utilities and offers encryption overloads in all the SerializeXXX() and DeserializeXXX() methods. Further, the utility uses generics to simplify the interface and avoid the overhead of casting and boxing/unboxing of objects.

You can find the MonoCross Serializer Utility within the MonoCross.Utilities.Serialization namespace. Table 10.5 describes the available methods.

Table 10.5 MonoCross Serializer Utility

Method Description
ContentType Specifies the content type associated with the specific serialization (XML or JSON) formats used (namely application/xml and application/json).
DeserializeList Deserializes a string or byte[] into a List<object>. Method overloads support decryption as part of the deserialization.
DeserializeListFromFile Deserializes the contents of a file into a List<object>. Method overloads support decryption as part of the deserialization.
DeserializeObject Deserializes a string or byte[] into an object. Method overloads support decryption as part of the deserialization.
DeserializeObjectFromFile Deserializes the contents of a file into an object. Method overloads support decryption as part of the deserialization.
SerializeList Serializes a List<object> to a string. Method overloads support encryption as part of the serialization.
SerializeListToBytes Serializes a List<object> to a byte[]. Method overloads support encryption as part of the serialization.
SerializeListToFile Serializes a List<object> to a specified file. Method overloads support encryption as part of the serialization.
SerializeObject Serializes an object to a string. Method overloads support encryption as part of the serialization.
SerializeObjectToBytes Serializes an object to a byte[]. Method overloads support encryption as part of the serialization.
SerializeObjectToFile Serializes an object to a specified file. Method overloads support encryption as part of the serialization.

Putting the Serializer Utility to Work

The MonoCross Serializer Utility supports your application's serialization needs with as simple an interface as possible. In most instances you need only two lines of code to perform serialization. The first line uses a generic factory method to create a serializer specific to the business object being serialized. The second line actually performs the serialization. As you can see in the following examples, after the serializer for a specific type has been created, you can use it to make multiple SerializeXXX() and DeserializeXXX() calls.

All the MonoCross Serializer Utility examples rely on the CustomerManagement.Shared.Model.Product class for demonstrating the serialization and deserialization of objects and lists. Listing 10.8 displays a simple List<Product> generation method.

1.1
Listing 10.8: Get Product List Implementation
namespace CustomerManagement.Shared.Model
{

public static List<Product> GetProductList()
{
  List<Product> list = new List<Product>();

  list.Add( new Product() { ID = "1", Description = "First Sample Product" } );
  list.Add( new Product() { ID = "2", Description = "Second Sample Product" } );
  list.Add( new Product() { ID = "3", Description = "Third Sample Product" } );
  list.Add( new Product() { ID = "4", Description = "Fourth Sample Product" } );

  return list;
}

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

3.11MonoTouch and Mono for Android both support compiler linking to shrink the size of the application by removing unused functions and methods from the application classes. Using compiling with linking has the side effect of removing default public constructors from classes needed for deserialization. This error manifests itself as a “Missing default constructor” runtime error during object deserialization.

You can apply the preserve attributes to the business object to preserve these methods so that deserialization can proceed unhindered. Due to namespace differences, you need a separate attribute for MonoTouch and Mono for Android development.

The C# compiler tags in Listing 10.8 apply the Preserve attribute in a cross-platform manner.

#if (ANDROID)

[Android.Runtime.Preserve( AllMembers = true )]

#elif (TOUCH)

[MonoTouch.Foundation.Preserve (AllMembers = true)]

#endif

Android and Touch are arbitrary project compilation tags to indicate whether the project compiles under Mono for Android or MonoTouch, respectively.

Listing 10.9 demonstrates how to serialize a Product to an XML string and deserialize the string into a Product.

1.1
Listing 10.9: Serialize Product to Text
public static void XmlSerializeObjectToText()
{
  Product product = GetProductList().First();

  // create a product serializer. Serializer defaults to XML.
  ISerializer<Product> serializer = SerializerFactory.Create<Product>();

  // Serialize object to a string.
  string sXml = serializer.SerializeObject( product );

  // deserialize the object into a product
  Product product1 = serializer.DeserializeObject( sXml );
}

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

The serializer defaults to XML serialization, but it also supports JSON serialization simply by entering a SerializationFormat enumeration parameter to the SerializerFactory.Create() method.

Listing 10.10 demonstrates how to serialize a Product to a JSON string and deserialize the string back into a Product.

1.1
Listing 10.10: Serialize Product to JSON Text
public static void JsonSerializeObjectToText()
{
  Product product = GetProductList().First();

  // create a JSON product serializer.
  ISerializer<Product> serializer;
  serializer = SerializerFactory.Create<Product>( SerializationFormat.JSON );

  // Serialize object to a string.
  string sJson = serializer.SerializeObject( product );

  // deserialize the object into a product
  Product product1 = serializer.DeserializeObject( sJson );
}

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

You can also serialize objects and lists into byte[] and into files by calling the appropriate SerializeXXX() methods such as SerializeObjectToByteArray() and SerializeListToFile().

Listing 10.11 demonstrates how to serialize a Product and a List< Product> into XML files and deserialize them again.

1.1
Listing 10.11: Serialize Product to Files
public static void XmlSerializeToFile()
{
  List<Product> list = GetProductList();
  Product product = list.First();

  string fileList = RootPath.AppendPath( "SerializeList.xml" );
  string fileObject = RootPath.AppendPath( "SerializeObject.xml" );

  // create a product serializer.  Serializer defaults to XML.
  ISerializer<Product> serializer = SerializerFactory.Create<Product>();

  // Serialize to a file, using Default Encryption settings.
  serializer.SerializeListToFile( list, fileList );
  serializer.SerializeObjectToFile( product, fileObject );

  // deserialize the files into a list and product
  List<Product> list1 = serializer.DeserializeListFromFile( fileList );
  Product product1 = serializer.DeserializeObjectFromFile( fileObject );
}

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

The MonoCross Serialization Utility supports Default Encryption so that the previous listing can write the objects to a file encrypted if required. The MonoCross Serialization Utility supports Specific Encryption through similar method overloads used in the File Interface and also supports the No Encryption method.

Listing 10.12 demonstrates how to serialize a Product and a List<Product> into XML files using both Specific Encryption and No Encryption and deserialize them again.

1.1
Listing 10.12: Serialize Product to Files with Encryption
public static void XmlSerializeObjectToFileWithEncryption()
{
  string key = "kjWkslr4qw4UT28LAfKJsYIdy9m+/vHry3fYDuydGk2=";
  byte[] salt = Convert.FromBase64String( "lRG7r+BNLTqVcKq6pTsMfi==" );

  Product prod = GetProductList().First();
  string fileEnc = RootPath.AppendPath( "SerializeSpecificEncryption.xml" );
  string filePlain = RootPath.AppendPath( "SerializePlain.xml" );

  // create a product serializer. Serializer defaults to XML.
  ISerializer<Product> serializer = SerializerFactory.Create<Product>();

  // Serialize object to a file, using Specific Encryption settings.
  serializer.SerializeObjectToFile( prod, fileEnc, key, salt );

  // Serialize object to a file, using No Encryption settings.
  serializer.SerializeObjectToFile( prod, filePlain, EncryptionMode.NoEncryption );

  // deserialize the file into a product, using Specific Encryption settings.
  Product prod1 = serializer.DeserializeObjectFromFile(fileEnc, key, salt );

  // deserialize file into a product, using No Encryption settings.
  serializer.DeserializeObjectFromFile( filePlain, EncryptionMode.NoEncryption );
}

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

Logging Application Events

Applications occasionally need to record internal events to describe what happens during application run time. Whether it's to record critical information before a crash, track metrics used to improve performance, or log a series of breadcrumbs to track an application's progress, you need to be able to record information and have that recording persist between application sessions.

Understanding the Log Utility

The MonoCross Log Utility provides a cross-platform approach for logging application events. It follows standard logging levels of Debug, Info, Warning, Error, and Fatal, plus it adds an additional level for tracking application metrics, called Metrics.

In addition, the MonoCross Log Utility supports the logging of exceptions. The exceptions display in the log file with the message, stack trace, and the Exception.Data collection, so you can add additional information to the Exception before logging it.

Table 10.6 shows the details of the MonoCross Log Utility.

Table 10.6 MonoCross Log Utility

Method Description
Debug Logs a Debugging event. Method overloads support logging exceptions as well as messages.
Error Logs an Error event. Method overloads support logging exceptions as well as messages.
Fatal Logs a Fatal event. Method overloads support logging exceptions as well as messages.
Info Logs an Information event. Method overloads support logging exceptions as well as messages.
Metric Logs a Metric event. Method overloads support logging of messages in addition to time duration of the metric being tracked expressed in milliseconds.
Warning Logs a Warning event. Method overloads support logging exceptions as well as messages.

Putting the Log Utility to Work

The MonoCross Log Utility is simple to use. Just determine the log level of the message your application is to record and then call the appropriate MXDevice.Log method.

Listing 10.13 demonstrates the basic usage of the Log Interface.

1.1
Listing 10.13: Logging Sample
public static void SimpleLogging()
{
  MXDevice.Log.Debug( "Logging an Debug Message" );
  MXDevice.Log.Info( "Logging an Information Message" );
  MXDevice.Log.Warn( "Logging a Warning Message" );
  MXDevice.Log.Error( "Logging an Error Message" );
  MXDevice.Log.Fatal( "Logging a Fatal Message" );

  MXDevice.Log.Metric( "Logging a Metric: Time: 100 milliseconds" );
  MXDevice.Log.Metric( "Logging a Metric:", 100 );
}

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

The MonoCross Log Utility also supports overloads for logging an Exception in addition to text messages. Each of the logging methods has overloads to log the message text and the Exception. As stated before, the exception logging also supports the output of the Exception.Data collection, so it is worthwhile to collect supporting information for the exceptions.

Listing 10.14 demonstrates how to log an exception.

1.1
Listing 10.14: Logging Exceptions
public static void ExceptionLogging()
{
  Product prod = null;
  try
  {
    prod = new Product();
    throw new Exception( "Test Exception" );
  }
  catch ( Exception exc )
  {
    // Add optional Data entries to the exception.
    exc.Data.Add( "Uri", "www.monocross.net" );
    exc.Data.Add( "Object Type Name", prod.GetType().Name );

    // we can just log a message
    MXDevice.Log.Error( "An exception occurred." );

    // we can just log the exception
    MXDevice.Log.Error( exc );

    // or both
    MXDevice.Log.Error( "An exception occurred.", exc );

    // And yes, all Log levels support exception logging.
    MXDevice.Log.Info( exc );
  }
}

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

The log output is sent to MXDevice.DataPath in a file named in the format YYYY_MM_DD_BasicLogger.log (for example, 2011_07_04_BasicLogger.log). In addition, the log file contains the date and time of the logging event, the process ID of the event, useful for tracking issues in multithreaded applications and the log level. This file contains entries similar those in Listing 10.15.

1.1
Listing 10.15: Sample Log File Entries
07-04-2011 19:53:37:0938 :10: [Debug] Logging an Debug Message
07-04-2011 19:53:37:0948 :10: [Info] Logging an Information Message
07-04-2011 19:53:37:0958 :10: [Warn] Logging a Warning Message
07-04-2011 19:53:37:0968 :10: [Error] Logging an Error Message
07-04-2011 19:53:37:0978 :10: [Fatal] Logging a Fatal Message
07-04-2011 19:53:37:0988 :10: [Metric] Logging a Metric: Time: 100 milliseconds
07-04-2011 19:53:37:1008 :10: [Metric] Logging a Metric: : Milliseconds: 100
07-04-2011 19:53:37:1128 :10: [Error] An exception occurred.
07-04-2011 19:53:37:1198 :10: [Error] Date: 7/4/2011 7:53:37 PM, [Exception]
 Error
 Message: Test Exception
 Stack:    at CustomerManagement.Samples.LoggingSample.ExceptionLogging() in
 C:Developmentmonocross-netranchesAlphaCustomerManagement.Samples
LoggingSample.cs:line 30
 Data: key:Uri, value:www.monocross.net; key:Object Type Name, value:Product;
07-04-2011 19:53:37:1238 :10: [Error] An exception occurred.
07-04-2011 19:53:37:1248 :10: [Error] Date: 7/4/2011 7:53:37 PM, [Exception]
 Error
 Message: Test Exception
 Stack:    at CustomerManagement.Samples.LoggingSample.ExceptionLogging() in
 C:Developmentmonocross-netranchesAlphaCustomerManagement.Samples
LoggingSample.cs:line 30
 Data: key:Uri, value:www.monocross.net; key:Object Type Name, value:Product;
07-04-2011 19:53:37:1278 :10: [Info] Date: 7/4/2011 7:53:37 PM, [Exception]
 Info
 Message: Test Exception
 Stack:    at CustomerManagement.Samples.LoggingSample.ExceptionLogging() in
 C:Developmentmonocross-netranchesAlphaCustomerManagement.Samples
LoggingSample.cs:line 30
 Data: key:Uri, value:www.monocross.net; key:Object Type Name, value:Product; 

Sample logging output similar to expected output of logging samples

Accessing Network Functionality

No man is an island, nor is an application. Applications often need to access data on a web server to display on a mobile device or even save the user's changes back to a centralized database. The MonoCross Network Utility provides a simple and reliable means of accessing RESTful services via the Get() and Post() methods.

Understanding the Network Utility

The MonoCross Network Utility supports the interaction with websites and URIs. This utility is used to access RESTful interfaces and return data in XML or JSON format as required by your particular application. It does this in a synchronous or asynchronous method as needed by the relevant platform.

In addition, the Get() and Post() methods can inject header information into the web requests. This header information is most often used to pass authentication information, such as user ID and password, from your application to the RESTful server being accessed. The server then uses the information to authenticate the user or confirm that a previous authentication is still valid before allowing the transaction to proceed.

You can find the MonoCross Network Utility within the MonoCross.Utilities.Network namespace. Table 10.7 describes the available methods.

Table 10.7 MonoCross Network Utility

Method Description
Get Returns the payload of a specified URI call as a string. Method overloads support injection of request header values to transmit string values such as authentication tokens.
PostBytes Posts a byte[] to a specified URI. Method overloads support injection of request header values.
PostObject Posts an object to a specified URI. The object serializes into a byte[] prior to transmission. Method overloads support injection of request header values.
PostString Posts a string to a specified URI. The string converts into a byte[] prior to transmission. Method overloads support injection of request header values.

Putting the Network Utility to Work

Chapter 8, “Consuming Data Services,” covers using the MonoCross Network Utility. Following is a simple demonstration of how to use the methods contained in the Network Interface.

Listing 10.16 demonstrates how to use an MXDevice.Network.Get() call to return a string from a website URI.

1.1
Listing 10.16: Performing a Simple Get
public static void SimpleGet()
{
  // a fictional RESTful site that will return a product with id of 1
  string uri = "https://www.myproducttest.com/products/1.xml";

  // make the network call and results of the GET will be stored in string.
  string returnValue = MXDevice.Network.Get( uri );
}

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

If a URI call to a RESTful service returns a known object type (such as Product), it's a simple matter to deserialize the object from the returned string for use within the application. You can make any necessary changes and then post them to the server for updates on the server.

Listing 10.17 demonstrates how to use an MXDevice.Network.Get() call to return a string from a website URI.

1.1
Listing 10.17: Performing a Product Get and Post
public static void ProductGetAndPost()
{
  // a fictional RESTful site that will return a product with id of 1
  string uri = "https://www.myproducttest.com/products/1.xml";
  string returnValue = MXDevice.Network.Get( uri );

  // serialize response into a product
  ISerializer<Product> serializer = SerializerFactory.Create<Product>();
  Product prod = serializer.DeserializeObject( returnValue );

  prod.Description = "New Description.";

  // post object to server.
  // MXDevice.Network handles serializing object to bytes for the post
  MXDevice.Network.PostObject( uri, prod );
}

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

As you can see, the MonoCross Network Utility simplifies getting and posting objects from RESTful interfaces. Situations arise in which additional information needs to be part of the request for the server to fulfill the request. These can include perhaps the user ID and password injected with the headers, or perhaps an authentication token on requests performed after authentication occurs.

You can collect any values that you need to include in the headers into a Dictionary<string, string> and pass them into the Get() and Post() methods. Because these values are strings, you can encrypt them prior to sending using Default Encryption or Specific Encryption, provided both the client application making the request and the server fulfilling it know the key and salt.

Listing 10.18 expands the previous example with injection headers.

1.1
Listing 10.18: Getting and Posting with Header Injection
public static void ProductGetAndPostWithInjection()
{
  // a fictional RESTful site that will return a product with id of 1
  string uri = "https://www.myproducttest.com/products/1.xml";

  Dictionary<string, string> headers = new Dictionary<string, string>();
  // add some header information, such as a username and authentication token
  headers.Add( "UserName", "TestUser1" );
  headers.Add( "AuthToken", "ABC123DEF456asd13234edfadf12334" );

  // assuming the server being called knows the appropriate key and salt 
  // we can encrypt the header value, example uses Default Encryption.
  string encryptedInfo = MXDevice.Encryption.EncryptString( "Info to Encrypt" );
  headers.Add( "EncryptedInfo", encryptedInfo );

  // now that the headers have been set up, include them in the Get calls
  // the Network Interface will include them in the request.
  string returnValue = MXDevice.Network.Get( uri, headers );

  // serialize response into a product
  ISerializer<Product> serializer = SerializerFactory.Create<Product>();
  Product prod = serializer.DeserializeObject( returnValue );

  prod.Description = "New Description.";

  // post object to server.
  // MXDevice.Network handles serializing object to bytes for the post
  MXDevice.Network.PostObject( uri, prod, headers );
}

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

Threading Your Application

Gone are the days when an application, even a small mobile app, can ignore threading and all that entails. Processes may need to run in the background, but not so many at a time to impact performance. The MonoCross Thread Utility provides a simple and effective means to initiate process threads and execute them directly and in thread pools.

Understanding the Thread Utility

The MonoCross Thread Utility supports background threads and related thread pools. Two thread pools exist: the Worker Pool and the Idle Queue Pool. The Worker Pool abstracts the default thread pool of the platform in question, whereas the Idle Thread Queue executes threads only when the user interface is idle — that is, when the user is not navigating within the application. In addition, the DiscardIdleThread() method can remove delegate calls placed on the Idle Thread Queue, if they haven't been processed.

You can find the MonoCross Thread Utility within the MonoCross.Utilities.Threading namespace. Table 10.8 describes the available methods.

Table 10.8 MonoCross Thread Utility

Method Description
DiscardIdleThread Discards all unprocessed delegate methods on the Idle Thread Queue.
QueueIdle Adds a ParameterizedThreadStart to the Idle Thread Queue. Method overloads support an object parameter.
QueueWorker Adds a ParameterDelegate to the Worker Pool. Method overloads support an object parameter.
Start Starts background process of a ThreadDelegate or ParameterDelegate. Method overloads support an object parameter.

Putting the Thread Utility to Work

The MonoCross Thread Utility encapsulates a common set of threading methods supported across the platforms. Although documenting all the ins and outs of threading are beyond the scope of this section, all rules of multithreaded development are still valid, so you need to properly apply locking code and handle exceptions.

The following code samples demonstrate the use of static methods for simplicity, but you can just as easily implement them using instance methods.

The MonoCross Thread Utility supports two internal delegates for use in threading, one with an object parameter and the other with no parameters. Listing 10.19 displays the signature of the internal threading delegates.

1.1
Listing 10.19: Threading Delegates
public delegate void ThreadDelegate();
public delegate void ParameterDelegate( object parameter );

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

You can use both of these delegates as parameters for the MXDevice.Thread.Start() method overloads; any delegates passed into this method must match one of these two signatures. Use the Start() method to immediately execute the delegate on a separate thread. Listing 10.20 displays the use of the Start() methods, along with their relevant delegate methods.

1.1
Listing 10.20: Starting Threads
// matches ThreadDelegate() signature
public static void DoStartWork()
{
  MXDevice.Log.Info( "Starting work." );
}

// matches ParameterDelegate() signature
public static void DoStartWorkParam( object parameter )
{
  MXDevice.Log.Info( String.Format( "Starting work with {0}.", parameter ) );
}

public static void StartWork()
{
  Product prod = new Product() { ID = "1", Description = "Description" };

  MXDevice.Log.Info( "StartWork() Output" );

  MXDevice.Thread.Start( ThreadingSample.DoStartWork );
  MXDevice.Thread.Start( ThreadingSample.DoStartWorkParam, "StringParameter" );
  MXDevice.Thread.Start( ThreadingSample.DoStartWorkParam, typeof( String ) );
  MXDevice.Thread.Start( ThreadingSample.DoStartWorkParam, prod );
}

// StartWork() will result in a log file similar to the following
07-05-2011 23:26:30:4494 :9: [Info] StartWork() Output
07-05-2011 23:26:30:4564 :9: [Info] Starting work.
07-05-2011 23:26:30:4584 :9: [Info] Starting work with String Parameter.
07-05-2011 23:26:30:4594 :9: [Info] Starting work with System.String.
07-05-2011 23:26:30:4594 :9: [Info] Starting work with
CustomerManagement.Shared.Model.Product.

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

Starting a thread immediately is all well and good, but suppose you have multiple tasks that you need to run in the background. The Worker Pool utility supports the execution of background tasks such as refreshing data, cache file cleanup, and so on. Any method that matches the MonoCross.Utilities.Threading.ParameterDelegate signature, which contains a single object parameter, can be added to the pool. Listing 10.21 demonstrates how to add a delegate to the Worker Pool.

1.1
Listing 10.21: Pooling Threads
public static void DoPoolWorkParam( object parameter )
{
  MXDevice.Log.Info( String.Format( "Pooling work with {0}.", parameter ) );
}

public static void PoolingWork()
{
  Product prod = new Product() { ID = "1", Description = "Description" };

  MXDevice.Log.Info( "PoolingWork() Output" );

  // QueueWorker only supports ParameterDelegate Signature
  MXDevice.Thread.QueueWorker( ThreadingSample.DoPoolWorkParam );
  MXDevice.Thread.QueueWorker( ThreadingSample.DoPoolWorkParam, "StringParameter" );
  MXDevice.Thread.QueueWorker( ThreadingSample.DoPoolWorkParam, typeof( String ) );
  MXDevice.Thread.QueueWorker( ThreadingSample.DoPoolWorkParam, prod );
}

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

Lastly, there are situations in which your application needs to run background threads, but only when the application is idle so the thread processing doesn't negatively impact the user experience with the application. When this occurs, you can add the delegates to the Idle Queue Pool with the QueueIdle() method. Listing 10.22 demonstrates how to add delegates to the Idle Queue Pool.

1.1
Listing 10.22: Queueing Threads
public static void DoQueueWorkParam( object parameter )
{
  MXDevice.Log.Info( String.Format( "Queueing work with {0}.", parameter ) );
}

public static void QueueingWork()
{
  Product prod = new Product() { ID = "1", Description = "Description" };

  MXDevice.Log.Info( "QueueingWork() Output" );

  // Add delegates to Idle Queue Thread.
  MXDevice.Thread.QueueIdle( ThreadingSample.DoQueueWorkParam );
  MXDevice.Thread.QueueIdle( ThreadingSample.DoQueueWorkParam, "StringParameter" );
  MXDevice.Thread.QueueIdle( ThreadingSample.DoQueueWorkParam, typeof( String ) );
  MXDevice.Thread.QueueIdle( ThreadingSample.DoQueueWorkParam, prod );

  // The Idle Queue Thread runs when navigation is idle.
  // The DiscardIdleThread removes any unprocessed delegates from the queue.
  MXDevice.Thread.DiscardIdleThread();
}

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

Summary

In this chapter, you explored the MonoCross Utilities and how they simplify cross-platform coding for many common tasks. You learned how to encrypt data, interact with the file system, serialize objects, log events, make network GETs and POSTs, and multithread your applications. All this was possible through a collection of simple, abstracted interfaces that enable you to focus on your business logic rather than on cross-platform idiosyncrasies.

In the next chapter, you learn more about hybrid applications. Specifically, you learn what they are and, more important, how to create them.

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

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