CHAPTER 2 ■ ACCESS CLOUD TABLE STORAGE 28 Figure 2-2. The AddressTableContext class diagram 3. Add a new class called AddressTableContext to inherit from the base class TableContext, as shown in Listing 2-3. The purpose of creating this class is to encapsulate the table storage interface function to a specific data table. In this exercise we only access the Address table, so we only need to create one derived class from TableContext. If there are multiple tables we need to access then we need to create more classes derived from TableContext in the future. Each derived class is dedicated to a specific data table. So why can't we come up with a generic table-access class, which exposes the data table access functions and returns generic types. The answer is the table name is a static string and needs to match the name of the physical data storage table. Another reason is that this allows the client code to accept the return data table type as a concerte type without transforming the generic type. This will significantly reduce unnecessary complexity. To reach that end there are three tasks that need to be done. 1. Create a constructor to this class to accept a parameter of instance of StorageAccountInfo. 2. Read the table name from the configuration settings in the body of the constructor. 3. Add a query interface to query the Address table. CHAPTER 2 ■ ACCESS CLOUD TABLE STORAGE 29 Listing 2-3. Implementation of Class AddressTableContext, a Derived Class of TableContext using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Configuration; namespace CloudTableStorageService WebRole.CloudTableStorageDataContext { using Microsoft.Samples.ServiceHosting.StorageClient; using CloudTableStorageService WebRole.CloudTableStrorageDataEntity; internal class AddressTableContext : TableContext { internal AddressTableContext(StorageAccountInfo accountInfo) : base(accountInfo) { TableName = ConfigurationManager.AppSettings["AddressTable"]; } public IQueryable<Address> AddressTable { get { return CreateQuery<Address>(TableName); } } } } 4. In the CloudTableStorageService WebRole project create a C# class called DataTableService and mark it as an abstract class since we are going to use it as a base class. This class implements the facade design pattern to encapsulate the StorageAccountInfo and TableContext classes. The definition of this base class is shown in Listing 2-4. Listing 2-4. Definition of Base Class DataTableService using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace CloudTableStorageService WebRole.CloudTableStorageDataService { using Microsoft.Samples.ServiceHosting.StorageClient; using CloudTableStorageService WebRole.CloudTableStorageDataContext; abstract public class DataTableService { protected StorageAccountInfo account = null; protected TableContext dataTableContext = null; CHAPTER 2 ■ ACCESS CLOUD TABLE STORAGE 30 public DataTableService() { // Get the settings from the Service Configuration file account = StorageAccountInfo.GetDefaultTableStorageAccountFromConfiguration(); } public TableContext TableContext() { return dataTableContext; } } } 5. Add a new C# class AddressTableService in the same folder. (The class diagram is shown in Figure 2-3. The implementation for the class is shown in Listing 2-5.) This class is derived from the base class DataTableService and provides a set of public access functions to perform basic data I/O between the table storage context and the custom-defined data entity container classes. This class must have at least the next four public methods: • Select(): The Select() method can include functionality to retrieve an enumerable collection of data items and to retrieve a single item. In this example we are going to implement Select() to retrieve the enumerable item collection from the table AddressTable. • Insert(): To insert an entity into a cloud storage table; in this example, to the table AddressTable. • Update(): To refresh changes of a data entity to a cloud storage table; in this example, to the table AddressTable. • Delete(): To remove a data entity from a cloud storage table; in this example, from the table AddressTable. Figure 2-3. Class AddressTableService provides data IO services CHAPTER 2 ■ ACCESS CLOUD TABLE STORAGE 31 Listing 2-5. Implementation of the Class AddressTableService using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Data.Services.Client; using System.Configuration; namespace CloudTableStorageService WebRole.CloudTableStorageDataService { using Microsoft.Samples.ServiceHosting.StorageClient; using CloudTableStorageService WebRole.CloudTableStrorageDataEntity; using CloudTableStorageService WebRole.CloudTableStorageDataContext; public class AddressTableService : DataTableService { /// </summary> public AddressTableService() { dataTableContext = new AddressTableContext(base. account); dataTableContext.RetryPolicy = RetryPolicies.RetryN(Convert.ToInt32( ConfigurationManager.AppSettings["Retry"]), TimeSpan.FromSeconds(1)); } public IEnumerable<Address> Select() { if (null == dataTableContext || null == ( dataTableContext as AddressTableContext)) { return null; } var results = from a in ( dataTableContext as AddressTableContext).AddressTable select a; if (0 == (results as DataServiceQuery<Address>).ToArray<Address>().Count<Address>()) { return null; } TableStorageDataServiceQuery<Address> query = new TableStorageDataServiceQuery<Address>( results as DataServiceQuery<Address>); IEnumerable<Address> queryResults = query.ExecuteAllWithRetries(); return queryResults; } CHAPTER 2 ■ ACCESS CLOUD TABLE STORAGE 32 public bool Insert(Address entity) { bool success = false; try { dataTableContext.AddObject( dataTableContext.TableName, entity); dataTableContext.SaveChanges(); success = true; } catch { } return success; } public bool Update(Address entity) { bool success = false; try { if (Delete(entity)) { success = Insert(entity); } } catch { } return success; } public bool Delete(Address entity) { bool success = false; try { dataTableContext.AttachTo( dataTableContext.TableName, entity, "*"); dataTableContext.DeleteObject(entity); dataTableContext.SaveChanges(); success = true; } catch { } return success; } } } 6. Open AddressTable.aspx from the project in Visual Studio and insert two table objects into the body of the form as Figure 2-4 shows.