如何使用Azure Table Storage的单个查询检索多种类型的实体?

时间:2012-05-22 07:38:23

标签: c# azure nosql azure-table-storage

我正在尝试了解Azure表存储如何工作以创建Facebook样式的提要,而我仍然坚持如何检索条目。

(我的问题几乎与https://stackoverflow.com/questions/6843689/retrieve-multiple-type-of-entities-from-azure-table-storage相同,但答案中的链接已被破坏。)

这是我的预期方法:

  1. 为我的应用程序中的所有用户创建个人订阅源,其中包含不同类型的条目(通知,状态更新等)。我的想法是将它们存储在按每个用户的分区键分组的Azure表中。

  2. 检索同一分区键中的所有条目,并根据条目类型将其传递给不同的视图。

  3.   

    如何查询所有类型相同基类型的表存储   保持其独特的属性?

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

    我对Feed的查询:

    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 个答案:

答案 0 :(得分:6)

最后还有官方的方式! :)

查看NoSQL示例,该示例在Azure存储团队博客的此链接中完成此操作:

Windows Azure Storage Client Library 2.0 Tables Deep Dive

答案 1 :(得分:2)

有几种方法可以解决这个问题,如何做到这一点取决于您的个人偏好以及潜在的绩效目标。

  • 创建一个表示所有查询类型的合并类。如果我有StatusUpdateEntry和NotificationEntry,那么我只需将每个属性合并到一个类中。序列化器会 自动填写正确的属性,并将其他属性保留为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,您可以根据需要选择序列化。 Jai Haridas和Pablo Castro提供了一些示例代码,用于在您不知道类型时读取实体(包括在下面),并且您可以调整它以阅读您了解的特定类型。

这两种方法的缺点是,在某些情况下,最终会提取的数据超出您的需求。你需要权衡一下你真正想要查询一种类型与另一种类型的比例。请记住,您现在可以在表存储中使用投影,这样也可以减少有线格式的大小,并且当您有更大的实体或许多要返回时,可以真正加快速度。如果你只需要查询一个类型,我可能会使用RowKey或PartitionKey的一部分来指定类型,这样我就可以一次只查询一个类型(你可以使用一个属性,但是这对于查询目的而言不如PK或RK高效。

编辑:正如Lucifure所指出的,另一个很好的选择是围绕它进行设计。使用多个表,并行查询等。当然,您需要在超时和错误处理的基础上进行交易,但根据您的需要,这是一个可行且通常很好的选择。

阅读通用实体:

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

答案 2 :(得分:1)

另一个选择当然是每个表只有一个实体类型,并行查询表并合并按时间戳排序的结果。 从长远来看,在可扩展性和可维护性方面,这可能是更谨慎的选择。

或者你需要使用'dunnry'概述的一些通用实体,其中非公共数据没有明确地输入,而是通过字典保存。

我已经编写了一个备用Azure表存储客户端Lucifure Stash,它支持对天蓝色表存储的其他抽象,包括坚持到字典,并且如果这是方向可能适用于你的情况你想追求。

Lucifure Stash支持大数据列&gt; 64K,阵列&amp;列表,枚举,组合键,开箱即用的序列化,用户定义的变形,公共和私有属性和字段等。它可以在http://www.lucifure.com或通过NuGet.com免费供个人使用。

修改:现在开源于CodePlex

答案 3 :(得分:1)

在查询中使用DynamicTableEntity作为实体类型。它有一个你可以查找的属性字典。它可以返回任何实体类型。