最近は Azure ばっかり弄ってますが、テーブルストレージに何をどのように保存するかで悩んでいます。しかし、テーブルストレージって保存可能なデータに制約が多いんですよね。
- byte[]
- bool
- DateTime
- double
- Guid
- int
- long
- string
プリミティブな型しか保存できないのはまあいいんですが、個人的には独自な POCO クラスとかを保存したいわけですよ。なので TableServiceContext を拡張して実装してみました。
参考にしたのは以下のブログです。JSON は使いたくなかったので、プロパティを親に展開する形で実装しました。
tkg84's devlog: Azure Table Storageに非サポート型のプロパティを保存する
とりあえずコードを載せておきます。
public class ComplexTypeServiceContext : TableServiceContext { public ComplexTypeServiceContext(string baseAddress, StorageCredentials credentials) : base(baseAddress, credentials) { IgnoreMissingProperties = true; ReadingEntity += MessageServiceContext_ReadingEntity; WritingEntity += MessageServiceContext_WritingEntity; } private static readonly XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"; private static readonly XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices"; private static void MessageServiceContext_ReadingEntity(object sender, ReadingWritingEntityEventArgs e) { var properties = e.Data.Descendants(m + "properties").First(); foreach (var property in e.Entity.GetType().GetProperties()) { if (TypeDescriptor.GetConverter(property.PropertyType).CanConvertFrom(typeof(string))) { continue; } var value = Activator.CreateInstance(property.PropertyType); foreach (var childProperty in property.PropertyType.GetProperties()) { var element = properties.Element(d + property.Name + "_" + childProperty.Name); childProperty.SetValue(value, element == null ? null : element.Value, null); } property.SetValue(e.Entity, value, null); } } private static void MessageServiceContext_WritingEntity(object sender, ReadingWritingEntityEventArgs e) { var properties = e.Data.Descendants(m + "properties").First(); foreach (var property in e.Entity.GetType().GetProperties()) { if (TypeDescriptor.GetConverter(property.PropertyType).CanConvertFrom(typeof(string))) { continue; } var node = properties.Element(d + property.Name); if (node != null) { node.Remove(); } var value = property.GetValue(e.Entity, null); foreach (var childProperty in property.PropertyType.GetProperties()) { var element = new XElement(d + property.Name + "_" + childProperty.Name); element.SetValue(childProperty.GetValue(value, null)); properties.Add(element); } } } }
そして以下のようなクラスを用意します。
public sealed class Message : TableServiceEntity { public string Text { get; set; } public User User { get; set; } } public class User { public string Name { get; set; } public string Description { get; set; } }
これをさっきのサービスコンテキストを使ってテーブルストレージに保存するとこんな感じです。
ちゃんとデシリアライズも出来るので、個人的には重宝しそうです。