CHAPTER 2 ■ ACCESS CLOUD TABLE STORAGE 53 namespace CloudTableStorageService WebRole.CloudTableStorageDataService { using Microsoft.Samples.ServiceHosting.StorageClient; using CloudTableStorageService WebRole.CloudTableStorageDataContext; using CloudTableStorageService WebRole.CloudTableStrorageDataEntity; abstract public class DataTableService : ICloudTableStorageService { protected StorageAccountInfo account = null; protected TableContext dataTableContext = null; protected CloudTableServiceFactory cloudTableFactory = new CloudTableServiceFactory(); public DataTableService() { // Get the settings from the Service Configuration file account = StorageAccountInfo.GetDefaultTableStorageAccountFromConfiguration(); cloudTableFactory = new CloudTableServiceFactory(); } public TableContext TableContext() { return dataTableContext; } virtual public bool Insert(ICloudEntity entity) { bool success = false; ICloudEntity dependency = null; try { dataTableContext.AddObject( dataTableContext.TableName, entity); dataTableContext.SaveChanges(); dependency = entity.GetDependencyEntity(); while (null != dependency) { cloudTableFactory = new CloudTableServiceFactory(); cloudTableFactory.FactoryCloudTableService(dependency) .Insert(dependency); dependency = dependency.GetDependencyEntity(); } success = true; } catch { } return success; } virtual public bool Update(ICloudEntity entity) { CHAPTER 2 ■ ACCESS CLOUD TABLE STORAGE 54 bool success = false; if (null != entity) { dataTableContext.MergeOption = MergeOption.PreserveChanges; dataTableContext.AttachTo( dataTableContext.TableName, entity, "*"); dataTableContext.UpdateObject(entity); dataTableContext.SaveChanges(); success = true; } return success; } virtual public bool Delete(ICloudEntity entity) { bool success = false; if (null != entity) { foreach (ICloudEntity entityType in entity.DependencyType()) { ICloudEntity dependency = QueryDependencyEntity(entityType, (entity as TableStorageEntity).RowKey); if (null != dependency) { cloudTableFactory.FactoryCloudTableService(dependency) .Delete(dependency); } } try { dataTableContext.AttachTo( dataTableContext.TableName, entity, "*"); dataTableContext.DeleteObject(entity); dataTableContext.SaveChanges(); success = true; } catch (Exception ex) { } } return success; } CHAPTER 2 ■ ACCESS CLOUD TABLE STORAGE 55 protected ICloudEntity QueryDependencyEntity(ICloudEntity entity, string key) { ICloudEntity dependencies = null; ICloudTableStorageService cloudTableservice = cloudTableFactory.FactoryCloudTableService(entity); dependencies = cloudTableservice.TableContext().QueryEntitiesByPartionKey(key); return dependencies; } } } We re-engineered the Address class based upon these new definitions as Listing 2-22 shows. There is no cloud table entity upon which the Address entity depends, so the body of the overridden method Initialization() is empty. Listing 2-22. Class Address Is Derived from the Base Class TableStorageEntity using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Configuration; namespace CloudTableStorageService WebRole.CloudTableStrorageDataEntity { using Microsoft.Samples.ServiceHosting.StorageClient; public enum State : int { AL, AK, AS, AZ, AR, CA, CO, CT, DE, DC, FM, FL, GA, GU, HI, ID, IL, IN, IA, KS, KY, LA, ME, MH, MD, MA, MI, MN, MS, MO, MT, NE, NV, NH, NJ, NM, NY, NC, ND, MP, OH, OK, OR, PW, PA, PR, RI, SC, SD, TN, TX, UT, VT, VI, VA, WA, WV, WI, WY } public class Address : CloudTableStorageEntity { private State state = CloudTableStorageService WebRole.CloudTableStrorageDataEntity.State.OR; public string Address1 { get; set; } public string Address2 { get; set; } public string City { get; set; } public int? State { get { return (int) state; } set { state = (State)value; } } public string Zip { get; set; } public string County { get; set; } public string Country { get; set; } public Address() : this(Guid.NewGuid().ToString(), Guid.NewGuid().ToString()) CHAPTER 2 ■ ACCESS CLOUD TABLE STORAGE 56 { } public Address(string partitionKey, string rowKey) : base(partitionKey, rowKey) { } public Address(string partitionKey) : this(partitionKey, Guid.NewGuid().ToString()) { } public Address(string address1, string address2, string city, State state, string zip, string county, string country, string parentRowKey) : this(parentRowKey, Guid.NewGuid().ToString()) { Address1 = address1; Address2 = address2; City = city; State = (int)state; Zip = zip; County = county; Country = country; } override public ICloudEntity GetDependencyEntity() { return null; } override public void SetDependencyEntity(ICloudEntity entity) { } override protected void Initialization() { } } } Listing 2-23 shows a code example for deleting and updating an Address. In terms of proof of concept we just simply use the default settings of the MergeOption for the DataContext object without assigning any value. CHAPTER 2 ■ ACCESS CLOUD TABLE STORAGE 57 Listing 2-23. Code Example of Implementation for Delete and Update an Entity from a Single Cloud Storage Table public bool Update(Address entity) { bool success = false; try { dataTableContext.AttachTo( dataTableContext.TableName, entity, "*"); dataTableContext.UpdateObject(entity); dataTableContext.SaveChanges(); success = true; } catch(Exception ex) { } 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; } As Listing 2-23 shows, the final update is via a DataServiceContext object. To dig into the technical details is out of the scope of this book, so check out the documentation. The code example to implement the Default.aspx file used to test the Address table update is shown in Listing 2-24. Listing 2-24. Request Update Address Table from Multiple Thread Concurrently protected void btnAddAddress Click(object sender, EventArgs e) { if (Page.IsValid) { UpdateTest();