CHAPTER 1 ■ CREATE CLOUD TABLE STORAGE 18 Listing 1-8. The Context Query Class Derived from TableStorageDataServiceContext Implemented in a Data Table Storage public class UserDataContext : TableStorageDataServiceContext { public DataServiceQuery<Address> AddressTable { get { CreateQuery<Address>(ConfigurationManager.AppSettings["AddressTable"]); } } } ■ Note The compiler does not provide detailed information about why the data storage generation failed. Hopefully, the information will be available from Microsoft in future releases. Using PartitionKey and RowKey to Organize the Data to be Distributed In order to support load balancing, tables and therefore entities in the cloud are partitioned across storage nodes, which may be physically located in different servers. Each partition holds a consecutive range of entities that have the same partition key value, which is how partitions are organized. As noted above, we specify the partition key as the PartitionKey property in a table, and it must be unique to allow for consecutive ordering into partitions. This sounds familiar, because the partition key forms part of an entity’s primary key in combination with RowKey. The data can be organized based on the usage of PartitionKey and RowKey for each data table entity. By design, the values for both PartitionKey and RowKey could be empty strings, whereas null values are not allowed. Table 1-2 below shows possible combinations of PartitionKey and RowKey. Table 1-2. Using PartitionKey and RowKey to Organize the Table Structures PartitionKey RowKey Usage Conditions Empty string Empty string One partition or one row Has value Empty string Multiple partitions or one row Empty string Has value One partition or multiple rows per a partition Has value Has value Multiple partitions of multiple rows for each partition CHAPTER 1 ■ CREATE CLOUD TABLE STORAGE 19 Create Cloud Data Storage with Relational Data Structure Migrating existing relational data storage from an on-premises system to a cloud environment is a really interesting topic and a challenge for an organization to face. There are a variety of good answers and solutions. In this exercise, we are going to create data storage with a relational structure among data entities to provide you with a starting point. SQL Azure provides a ready-made solution for relational data storage infrastructure and is covered in Chapter 8. The rest of this chapter will provide an example exercise to handle a simple relational data structure that runs in a cloud using table storage, as you may not want the full-blown SQL Azure service. The data structure we will build includes three data entities: Address, Person, and User. The Address data entity is the same as the one we created in the first exercise. A Person data entity has an encapsulated object Address. A User data entity has an encapsulated object Person. In terms of the XML schema, the relationship among these entities can be understood as the reference of one schema to another, such as a User reference to Person and a Person reference to Address. This is a typical example using an object-oriented approach to data modeling and schema definitions. Figure 1-14 and Figure 1-15 provide the relationship in terms of XML schemas. Figure 1-14. User data structure schemas Figure 1-15. Schema for Address, Person, and User CHAPTER 1 ■ CREATE CLOUD TABLE STORAGE 20 As explained in the previous exercise, we cannot use the property access attribute to expose an embedded object from an entity class. We either have to alter the embedded object as we did previously or use methods to provide access to the internal objects. Otherwise, the data table generation fails and the compiler does not provide the specific reason. Listing 1-9 shows how to expose a custom-defined inner data entity object from a data object class using class member methods instead of member attributes. Listing 1-9. Using a Member Method Instead of Member Attributes to Expose Inner Data Entity Objects of a Non-portable Custom-defined Type public class Person : TableStorageEntity { private Address _address = null; public string FirstName { get; set; } public string LastName { get; set; } public string MiddleInitial { get; set; } public string Suffix { get; set; } public Address GetAddress() { return _address; } } Following the same procedures as we had in the first exercise, we successfully generated three data tables from local cloud data storage as the screenshot shows in Figure 1-16. Figure 1-16. Generated relational data storage From this example, we learned that data entity classes with relational structures in a cloud platform have some limitations. If an entity class has a dependency entity class, property attributes cannot be used for access. Instead, member methods should be used. It should be borne in mind that you need to refactor existing data storage for the cloud. We will discuss more refactoring issues in later chapters. CHAPTER 1 ■ CREATE CLOUD TABLE STORAGE 21 A Constraint for Data Entity Classes Contains Embedded Entity Classes Today, a lot of existing software uses data entity classes generated from XML schemas using the .NET utility Xsd.exe shipped with the .NET Framework. By default, the generated data entity classes using Xsd.exe use a property attribute function to access the embedded data entity objects. When we refactor existing code to a cloud platform, we can simply derive these data entity classes from TableStorageEntity. However, all property functions used to expose embedded data object from a container or parent class must be refactored to Get/Set member method pairs instead. Listing 1-10 shows the data entity class for Person generated by using Xsd.exe. All attribute properties such as the AddressRoot, as highlighted in Listing 1-9, must be reimplemented as Get/Set member method pairs. Listing 1-10. The Person Class Generated from Xsd.exe Using the XML Schemas [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://AzureForDotNetDeveloper.Schema.User.Person")] [System.Xml.Serialization.XmlRootAttribute (Namespace="http://AzureForDotNetDeveloper.Schema.User.Person", IsNullable=false)] public partial class PersonRoot { private string firstNameField; private string latNameField; private string middleInitialField; private string suffixField; private AddressRoot addressRootField; /// <remarks/> [System.Xml.Serialization.XmlElementAttribute (Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] public string FirstName { get { return this.firstNameField; } set { this.firstNameField = value; } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute( Namespace="http://AzureForDotNetDeveloper.Schema.User.Address")] public AddressRoot AddressRoot { get { return this.addressRootField; } CHAPTER 1 ■ CREATE CLOUD TABLE STORAGE 22 set { this.addressRootField = value; } } } Refactoring Data Entity Classes A solution to the constraint for data entity classes containing embedded entity classes is to refactor. To refactor the PersonRoot class for Windows Azure platform, follow these steps: 1. Add using Microsoft.Samples.ServiceHosting.StorageClient; into the project. 2. Make the PersonRoot class derive from the TableStorageEntity class. 3. Modify the Get/Set property pair into the GetAddress()/SetAddress() method pair. Listing 1-11 shows the results after the Person data entity class has been refactored. Listing 1-11. Refactoring the Person Class to the Azure Cloud Platform using Microsoft.Samples.ServiceHosting.StorageClient; /// <remarks/> [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true,« Namespace="http://AzureForDotNetDeveloper.Schema.User.Person")] [System.Xml.Serialization.XmlRootAttribute(Namespace=« "http://AzureForDotNetDeveloper.Schema.User.Person", IsNullable=false)] public partial class PersonRoot : TableStorageEntity { public AddressRoot GetAddressRoot() { return this.addressRootField; } public void SetAddressRoot(AddressRoot address) { this.addressRootField = address; } } . classes containing embedded entity classes is to refactor. To refactor the PersonRoot class for Windows Azure platform, follow these steps: 1. Add using Microsoft.Samples.ServiceHosting.StorageClient;