When you have Windows Azure Table Storage with a schema, you declare a class which inherits from "TableStorageEntity"  and this class sets the fields which you will use in your table. Your table schema could be dynamic and during runtime you will be handling key and values collection , which will be defined in tablestorage fields. If you want to get table storage entities you can use storage client sample classes to achieve your objective however a question comes how to update the table schema during runtime when objects are constructed dynamically. Or you may ask how to enumerate table properties to construct the proper type of object dynamically during run time.


After a little research on net, I found that ReadingEntity and WritingEntity methods can be used to solve it. Here are the details:


You can define a class that has PartitionKey, RowKey and a dictionary (maps string to object) and then use ReadingEntity event on the DataServiceContext to achieve this.


    [DataServiceKey("PartitionKey", "RowKey")]  

    public class GenericEntity  

    {  

        public string PartitionKey { get; set; }  

        public string RowKey { get; set; }  


        Dictionary<string, object> properties = new Dictionary<string, object>();  


        internal object this[string key]  

        {  

            get  

            {  

                return this.properties[key];  

            }  


            set  

            {  

                this.properties[key] = value;  

            }  

        }  


        public override string ToString()  

        {  

            // TODO: append each property  

            return "";  

        }  

    }  



Once you have the entity defined, you can do your deserialization in the ReadingEntity method.


        void TestGenericTable()  

        {  

            var ctx = CustomerDataContext.GetDataServiceContext();  

            ctx.IgnoreMissingProperties = true;  

            ctx.ReadingEntity += new EventHandler<ReadingWritingEntityEventArgs>(OnReadingEntity);  

            var customers = from o in ctx.CreateQuery<GenericTable>(CustomerDataContext.CustomersTableName) select o;  


            Console.WriteLine("Rows from '{0}'", CustomerDataContext.CustomersTableName);  

            foreach (GenericEntity entity in customers)  

            {  

                Console.WriteLine(entity.ToString());  

            }  

        } 


The ReadingEntity implementation is as follows:


        // Credit goes to Pablo from ADO.NET Data Service team

        public void OnReadingEntity(object sender, ReadingWritingEntityEventArgs args)  

        {  

            // TODO: Make these statics  

            XNamespace AtomNamespace = "http://www.w3.org/2005/Atom";  

            XNamespace AstoriaDataNamespace = "http://schemas.microsoft.com/ado/2007/08/dataservices";  

            XNamespace AstoriaMetadataNamespace = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";  


            GenericEntity entity = args.Entity as GenericEntity;  

            if (entity == null)  

            {  

                return;  

            }  


            // read each property, type and value in the payload  

            var properties = args.Entity.GetType().GetProperties();  

            var q = from p in args.Data.Element(AtomNamespace + "content")  

                                    .Element(AstoriaMetadataNamespace + "properties")  

                                    .Elements()  

                    where properties.All(pp => pp.Name != p.Name.LocalName)  

                    select new  

                    {  

                        Name = p.Name.LocalName,  

                        IsNull = string.Equals("true", p.Attribute(AstoriaMetadataNamespace + "null") == null ? null : p.Attribute(AstoriaMetadataNamespace + "null").Value, StringComparison.OrdinalIgnoreCase),  

                        TypeName = p.Attribute(AstoriaMetadataNamespace + "type") == null ? null : p.Attribute(AstoriaMetadataNamespace + "type").Value,  

                        p.Value  

                    };  


            foreach (var dp in q)  

            {  

                entity[dp.Name] = GetTypedEdmValue(dp.TypeName, dp.Value, dp.IsNull);  

            }  

        }  


      private static object GetTypedEdmValue(string type, string value, bool isnull)  

        {  

            if (isnull) return null;  


            if (string.IsNullOrEmpty(type)) return value;  


            switch (type)  

            {  

                case "Edm.String": return value;  

                case "Edm.Byte": return Convert.ChangeType(value, typeof(byte));  

                case "Edm.SByte": return Convert.ChangeType(value, typeof(sbyte));  

                case "Edm.Int16": return Convert.ChangeType(value, typeof(short));  

                case "Edm.Int32": return Convert.ChangeType(value, typeof(int));  

                case "Edm.Int64": return Convert.ChangeType(value, typeof(long));  

                case "Edm.Double": return Convert.ChangeType(value, typeof(double));  

                case "Edm.Single": return Convert.ChangeType(value, typeof(float));  

                case "Edm.Boolean": return Convert.ChangeType(value, typeof(bool));  

                case "Edm.Decimal": return Convert.ChangeType(value, typeof(decimal));  

                case "Edm.DateTime": return XmlConvert.ToDateTime(value, XmlDateTimeSerializationMode.RoundtripKind);  

                case "Edm.Binary": return Convert.FromBase64String(value);  

                case "Edm.Guid": return new Guid(value);  


                default: throw new NotSupportedException("Not supported type " + type);  

            }  

        }  



Furthermore if you decide to save the dynamic data, you can use WritingEntity event to store the data. You can add the xml element for each property to the content element in which the content element can be retrieved from ReadingWritingEntityEventArgs.


References:

http://social.msdn.microsoft.com/Forums/en-US/windowsazure/thread/481afa1b-03a9-42d9-ae79-9d5dc33b9297/