Azure Table storage 之改进DynamicTableEntity类为其添加动态语言扩展
在之前的一篇文章中提到,storage类库中包含一个可以用来动态获取Azure table storage 表结构的类-DynamicTableEntity。
我们可以通过这个类,我们无需为每一个表提前声明一个class,也能够对其进行操作。
但在我们要添加Entity到表中的时候会感觉到一点不舒服
以下我所希望使用的该类的方式:
dynamic entity = new DynamicTableEntity(); entity.Name=”sam”; entity.Gender=true; …
这样的形式
而实际代码却是
DynamicTableEntity entity1 = new DynamicTableEntity(); Dictionary<string, EntityProperty> data1 = new Dictionary<string, EntityProperty>(); data1.Add("Name", new EntityProperty("Sam")); data1.Add("Gender", new EntityProperty(true)); data1.Add("Age", new EntityProperty(18)); entity1.Properties = data1; entity1.PartitionKey = "Partition1"; entity1.RowKey = "1"; UserDataList.Add(entity1);
身为一个以dynamic开头的类居然不能够用dynamic关键字来初始化它,在实例化一个DynamicTableEntity类的时候居然要如此麻烦,实在让人不爽。
所以, 经过一番研究,发现了一个可以支持动态扩展的类,为了与DynamicTableEntity区分开来,所以取名DynamicObjectTableEntity。它实现了DynamicTableEntity相同的功能,而且继承了DynamicObject,从而具有动态语言的特性,更加方便 使用。
而且动态语言扩展一直以来除了在linq中使用比较多以外,其它地方基本看不到。这里正好通过对dynamicTableEntity的改造可以加深对动态语言扩展的理解。
实现代码如下:
public class DynamicObjectTableEntity : DynamicObject,ITableEntity { #region DynamicTableEntity's code // Methods public DynamicObjectTableEntity() { this.Properties = new Dictionary<string, EntityProperty>(); } public DynamicObjectTableEntity(string partitionKey, string rowKey) : this(partitionKey, rowKey, DateTimeOffset.MinValue, null, new Dictionary<string, EntityProperty>()) { } public DynamicObjectTableEntity(string partitionKey, string rowKey, string etag, IDictionary<string, EntityProperty> properties) : this(partitionKey, rowKey, DateTimeOffset.MinValue, etag, properties) { } internal DynamicObjectTableEntity(string partitionKey, string rowKey, DateTimeOffset timestamp, string etag, IDictionary<string, EntityProperty> properties) { //CommonUtility.AssertNotNull("partitionKey", partitionKey); //CommonUtility.AssertNotNull("rowKey", rowKey); //CommonUtility.AssertNotNull("properties", properties); this.PartitionKey = partitionKey; this.RowKey = rowKey; this.Timestamp = timestamp; this.ETag = etag; this.Properties = properties; } public void ReadEntity(IDictionary<string, EntityProperty> properties, OperationContext operationContext) { this.Properties = properties; } public IDictionary<string, EntityProperty> WriteEntity(OperationContext operationContext) { return this.Properties; } // Properties public string ETag { get; set; } public EntityProperty this[string key] { get { return this.Properties[key]; } set { this.Properties[key] = value; } } public string PartitionKey { get; set; } public IDictionary<string, EntityProperty> Properties { get; set; } public string RowKey { get; set; } public DateTimeOffset Timestamp { get; set; } #endregion #region override DynamicObject's mehtods public override bool TryGetMember(GetMemberBinder binder, out object result) { if (!Properties.ContainsKey(binder.Name)) Properties.Add(binder.Name, ConvertToEntityProperty(binder.Name, null)); result = Properties[binder.Name]; return true; } public override bool TrySetMember(SetMemberBinder binder, object value) { EntityProperty property = ConvertToEntityProperty(binder.Name, value); if (Properties.ContainsKey(binder.Name)) Properties[binder.Name] = property; else Properties.Add(binder.Name, property); return true; } /// <summary> /// Convert object value to EntityProperty. /// </summary> private EntityProperty ConvertToEntityProperty(string key, object value) { if (value == null) return new EntityProperty((string)null); if (value.GetType() == typeof(byte[])) return new EntityProperty((byte[])value); if (value.GetType() == typeof(bool)) return new EntityProperty((bool)value); if (value.GetType() == typeof(DateTimeOffset)) return new EntityProperty((DateTimeOffset)value); if (value.GetType() == typeof(DateTime)) return new EntityProperty((DateTime)value); if (value.GetType() == typeof(double)) return new EntityProperty((double)value); if (value.GetType() == typeof(Guid)) return new EntityProperty((Guid)value); if (value.GetType() == typeof(int)) return new EntityProperty((int)value); if (value.GetType() == typeof(long)) return new EntityProperty((long)value); if (value.GetType() == typeof(string)) return new EntityProperty((string)value); throw new Exception("This value type" + value.GetType() + " for " + key); throw new Exception(string.Format("This value type {0} is not supported for {1}", key)); } /// <summary> /// Get the edm type, if the type is not a edm type throw a exception. /// </summary> private Type GetType(EdmType edmType) { switch (edmType) { case EdmType.Binary: return typeof(byte[]); case EdmType.Boolean: return typeof(bool); case EdmType.DateTime: return typeof(DateTime); case EdmType.Double: return typeof(double); case EdmType.Guid: return typeof(Guid); case EdmType.Int32: return typeof(int); case EdmType.Int64: return typeof(long); case EdmType.String: return typeof(string); default: throw new TypeLoadException(string.Format("not supported edmType:{0}", edmType)); } } #endregion }
在使用了如下类之后,我们就可以用我预期的代码来操作table 了
代码如下:
static void Main(string[] args) { var connectionString = "DefaultEndpointsProtocol=https;AccountName=[Account];AccountKey=[Key]"; CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString); try { var client = storageAccount.CreateCloudTableClient(); var table = client.GetTableReference("ShortMessages"); table.CreateIfNotExists(); dynamic entity = new DynamicObjectTableEntity("default", DateTime.Now.ToShortTimeString()); entity.Name = "pete"; entity.Message = "Hello"; table.Execute(TableOperation.Insert(entity)); Console.WriteLine("insert successfully!"); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.Read(); }
示例参考MSDN:http://code.msdn.microsoft.com/Dynamic-TableServiceEntity-151d661f