6

Facebook スタイルのフィードを作成するために Azure テーブル ストレージがどのように機能するかを把握しようとしていますが、エントリを取得する方法に行き詰まっています。

(私の質問はhttps://stackoverflow.com/questions/6843689/retrieve-multiple-type-of-entities-from-azure-table-storageとほぼ同じですが、回答のリンクが壊れています。)

これは私の意図したアプローチです:

  1. さまざまな種類のエントリ (通知、ステータス更新など) を含めることができるアプリケーション内のすべてのユーザー用の個人フィードを作成します。私の考えは、ユーザーごとにパーティション キーでグループ化された Azure テーブルにそれらを格納することです。

  2. 同じパーティション キー内のすべてのエントリを取得し、エントリの種類に応じて異なるビューに渡します。

一意のプロパティを保持しながら、同じ基本型のすべての型のテーブル ストレージをクエリするにはどうすればよいですか?

にはCloudTableQuery<TElement>型付きエンティティが必要です。EntryBaseジェネリック引数として指定すると、エントリ固有のプロパティ ( NotificationSpecificPropertyStatusUpdateSpecificProperty) が取得されず、その逆も同様です。

私のエンティティ:

public class EntryBase : TableServiceEntity
{
    public EntryBase()
    {


    }
    public EntryBase(string partitionKey, string rowKey)
    {
        this.PartitionKey = partitionKey;
        this.RowKey = rowKey;
    }
}


public class NotificationEntry : EntryBase
{
    public string NotificationSpecificProperty { get; set; }
}

public class StatusUpdateEntry : EntryBase
{
    public string StatusUpdateSpecificProperty { get; set; }
}

フィードの私のクエリ:

List<AbstractFeedEntry> entries = // how do I fetch all entries?

foreach (var item in entries)
{

    if(item.GetType() == typeof(NotificationEntry)){

        // handle notification

    }else if(item.GetType() == typeof(StatusUpdateEntry)){

        // handle status update

    }

}
4

4 に答える 4

7

ついに正式な方法があります!:)

Azure ストレージ チーム ブログのこのリンクで、まさにこれを行う NoSQL サンプルを見てください。

Windows Azure ストレージ クライアント ライブラリ 2.0 テーブルの詳細

于 2012-11-13T12:48:57.190 に答える
2

これを行うにはいくつかの方法があり、それをどのように行うかは、個人的な好みと潜在的なパフォーマンスの目標に少し依存します。

  • 照会されたすべてのタイプを表す統合クラスを作成します。StatusUpdateEntryとNotificationEntryがある場合は、各プロパティを1つのクラスにマージするだけです。シリアライザーは自動的に正しいプロパティを入力し、他のプロパティはnull(またはデフォルト)のままにします。エンティティに「type」プロパティ(計算またはストレージに設定)も設定すると、そのタイプを簡単にオンに切り替えることができます。私は常にテーブルエンティティからアプリ内の独自のタイプにマッピングすることをお勧めしますので、これも問題なく機能します(クラスはDTOにのみ使用されます)。

例:

[DataServiceKey("PartitionKey", "RowKey")]
public class NoticeStatusUpdateEntry
{
    public string PartitionKey { get; set; }   
    public string RowKey { get; set; }
    public string NoticeProperty { get; set; }
    public string StatusUpdateProperty { get; set; }
    public string Type
    {
       get 
       {
           return String.IsNullOrEmpty(this.StatusUpdateProperty) ? "Notice" : "StatusUpate";
       }
    }
}
  • シリアル化プロセスをオーバーライドします。これは、ReadingEntityイベントをフックすることで自分で行うことができます。それはあなたに生のXMLを与え、あなたはあなたが望むようにシリアル化することを選ぶことができます。JaiHaridasとPabloCastroは、タイプ(以下に含まれる)がわからないときにエンティティを読み取るためのサンプルコードをいくつか示しました。これを適応させて、知っている特定のタイプを読み取ることができます。

両方のアプローチの欠点は、場合によっては必要以上のデータを取得してしまうことです。これを、あるタイプと別のタイプに対して実際にクエリする量と比較検討する必要があります。テーブルストレージでプロジェクションを使用できるようになったことを覚えておいてください。これにより、ワイヤフォーマットのサイズも小さくなり、エンティティが大きい場合や返されるエンティティが多い場合に、処理速度が大幅に向上します。単一の型のみをクエリする必要がある場合は、おそらくRowKeyまたはPartitionKeyの一部を使用して型を指定します。これにより、一度に1つの型のみをクエリできるようになります(プロパティを使用できますが、これは、クエリの目的ではPKやRKほど効率的ではありません)。

編集:Lucifureが指摘したように、もう1つの優れたオプションは、その周りを設計することです。複数のテーブルを使用したり、並列クエリを実行したりします。もちろん、タイムアウトやエラー処理に関する複雑さとトレードオフする必要がありますが、ニーズによっては、実行可能で、多くの場合、適切なオプションです。

一般的なエンティティの読み取り:

[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 "";   
    }   
}   


    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());   
        }   
    }  

    // 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);   
        }   
    }
于 2012-05-22T15:03:52.870 に答える
1

もちろん、別のオプションは、テーブルごとに 1 つのエンティティ タイプのみを持ち、テーブルを並行してクエリし、タイムスタンプで並べ替えられた結果をマージすることです。長期的には、スケーラビリティと保守性に関して、これがより賢明な選択であることが証明される可能性があります。

または、「ダンリー」で概説されているように、汎用エンティティのフレーバーを使用する必要があります。この場合、非共通データは明示的に型指定されず、代わりに辞書を介して永続化されます。

私は代替の Azure テーブル ストレージ クライアント、Lucifure Stash を作成しました。これは、辞書への永続化や辞書からの永続化など、Azure テーブル ストレージに対する追加の抽象化をサポートしており、それがあなたが追求したい方向である場合、あなたの状況で機能する可能性があります。

Lucifure Stash は、64K を超える大規模なデータ列、配列とリスト、列挙、複合キー、すぐに使えるシリアル化、ユーザー定義のモーフィング、パブリックおよびプライベート プロパティとフィールドなどをサポートします。http://www.lucifure.com または NuGet.com から、個人使用であれば無料で入手できます。

編集:CodePlexでオープンソースになりました

于 2012-05-22T16:20:47.423 に答える
0

クエリのエンティティ タイプとしてDynamicTableEntityを使用します。検索できるプロパティのディクショナリがあります。任意のエンティティ タイプを返すことができます。

于 2014-11-10T22:12:11.497 に答える