[Azure] C# + Microsoft.Azure.Cosmos.Table v1+ Azure Storage Table - 如何處理複雜型別寫入及還原

2021-01-14

寫了幾篇 Azure Storage Table  , 接下來就是這系列最後一篇了,主要目的我們要探討一下 關於物件中有特殊型別怎麼辦,身為真男人,當然你可以把複雜型別開成 string 把資料拿回來後自己在還原成物件,但是這樣做蠻麻煩的,這時候我在想如果乾脆複寫掉 TableEntity 自己來做一個,並且在序列化跟反序列化動手腳的話,這不是很完美。




前置作業 首先你要 安裝 nuget https://www.nuget.org/packages/Microsoft.Azure.Cosmos.Table ,這邊我撰寫案例是用 .net core 3.1;在 Azure Portal  開一個 儲存體帳戶,並且在左側的存取金鑰中拿到 Connection String.


說明一下今天要做的,我之前有設計一個測試的物件如下

public class User : DTableEntity { public string Name { get; set; } public Int32? Age { get; set; } public DateTime Create { get; set; } public List<User> Friends { get; set; } public string Meta { get; set; } public DateTime? LastLoginDate { get; set; } public User() { Friends = new List<User>(); } public User(string classId, string userId) { PartitionKey = classId; RowKey = userId; Friends = new List<User>(); } }

這裡面有一個 Property 是 List<User> Friends ,這是一個複雜型別,這時候我們必須把資料寫入至 Azure Table ,但是需要用 JSON 儲存
不多說直接給我搞得一個 DTableEntity ,於是只要把 User 繼承他,就可以輕輕鬆鬆達到這效果 

using Microsoft.Azure.Cosmos.Table; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json.Linq; internal class ObjectUtil { public static Type GetType(string typeName) { var type = Type.GetType(typeName); if (type != null) return type; foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) { type = a.GetType(typeName); if (type != null) return type; } return null; } /// <summary> /// 轉成可以擴充的物件 /// </summary> /// <param name="obj"></param> /// <returns></returns> public static dynamic ConvertToDynamic(object obj) { return JObject.Parse(JsonConvert.SerializeObject(obj)); } } public class DTableEntity : TableEntity { public override void ReadEntity(IDictionary<string, EntityProperty> properties, OperationContext operationContext) { base.ReadEntity(properties, operationContext); foreach (var thisProperty in GetType().GetProperties().Where(thisProperty => thisProperty.GetType() != typeof(string) && properties.ContainsKey(thisProperty.Name) && (properties[thisProperty.Name].PropertyType == EdmType.String || properties[thisProperty.Name].PropertyType == EdmType.DateTime))) { var t = thisProperty.PropertyType; if (t.IsPrimitive || t == typeof(String)) { Convert.ChangeType(properties[thisProperty.Name].PropertyAsObject, thisProperty.PropertyType); } else if (t == typeof(DateTime?) || t == typeof(DateTime)) { if (properties[thisProperty.Name] != null) { thisProperty.SetValue(this, TimeZoneInfo.ConvertTimeFromUtc(properties[thisProperty.Name].DateTime.Value, TimeZoneInfo.FindSystemTimeZoneById(TimeZoneInfo.Local.Id))); } } else { if (thisProperty.PropertyType.IsGenericType && (thisProperty.PropertyType.GetGenericTypeDefinition() == typeof(List<>))) { var newStr = thisProperty.PropertyType.ToString().Replace("System.Collections.Generic.List`1[", "").Replace("]", ""); var type = ObjectUtil.GetType(newStr); Type listType = typeof(List<>).MakeGenericType(new Type[] { type }); thisProperty.SetValue(this, JsonConvert.DeserializeObject(properties[thisProperty.Name].StringValue, listType)); } else { thisProperty.SetValue(this, JsonConvert.DeserializeObject(properties[thisProperty.Name].StringValue, ObjectUtil.GetType(thisProperty.PropertyType.ToString()))); } } } } public override IDictionary<string, EntityProperty> WriteEntity(OperationContext operationContext) { var properties = base.WriteEntity(operationContext); foreach (var thisProperty in GetType().GetProperties().Where(thisProperty => !properties.ContainsKey(thisProperty.Name) && typeof(TableEntity).GetProperties().All(p => p.Name != thisProperty.Name))) { var value = thisProperty.GetValue(this); if (value != null) { var t = thisProperty.GetType(); properties.Add(thisProperty.Name, new EntityProperty(JsonConvert.SerializeObject(value))); } } return properties; } }

中間的序列化跟反序列化,我都幫你處理了,但是會用掉一點性能去換,當然這世界沒有完美的,這裡面參考許多開源,整理出來一個輕量參考那些文件已經不可考了所以也沒有附上了,在這系列的最後提供這讓大家可以輕鬆使用。

如果你是專案直接要用可以直接到這邊下宰我編譯好的 https://github.com/donma/Azure.Cosmos.Table

Happy Coding : )


當麻許的碎念筆記 2014 | Donma Hsu Design.