简体   繁体   English

如何使用对 Azure 表存储的单个查询检索多种类型的实体?

[英]How do I retrieve multiple types of entities using a single query to Azure Table Storage?

I'm trying to grasp how Azure table storage works to create facebook-style feeds and I'm stuck on how to retrieve the entries.我试图了解 Azure 表存储如何工作以创建 facebook 风格的提要,但我一直在研究如何检索条目。

(My questions is almost the same as https://stackoverflow.com/questions/6843689/retrieve-multiple-type-of-entities-from-azure-table-storage but the link in the answer is broken.) (我的问题几乎与https://stackoverflow.com/questions/6843689/retrieve-multiple-type-of-entities-from-azure-table-storage相同,但答案中的链接已损坏。)

This is my intended approach:这是我的预期方法:

  1. Create a personal feed for all users within my application which can contain different types of entries (notification, status update etc).为我的应用程序中的所有用户创建一个个人提要,其中可以包含不同类型的条目(通知、状态更新等)。 My idea is to store them in an Azure Table grouped by a partition key for each user.我的想法是将它们存储在按每个用户的分区键分组的 Azure 表中。

  2. Retrieve all entries within the same partition key and pass it to different views depending on entry type.检索同一分区键内的所有条目,并根据条目类型将其传递给不同的视图。

How do I query the table storage for all types of the same base type while keeping their unique properties?如何在保持其唯一属性的同时查询相同基类型的所有类型的表存储?

The CloudTableQuery<TElement> requires a typed entity, if I specify EntryBase as generic argument I don't get the entry-specific properties ( NotificationSpecificProperty , StatusUpdateSpecificProperty ) and vice versa. CloudTableQuery<TElement>需要一个类型化实体,如果我将EntryBase指定为通用参数,我将不会获得特定于条目的属性( NotificationSpecificPropertyStatusUpdateSpecificProperty ),反之亦然。

My entities:我的实体:

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

My query for a 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

    }

}

Finally there's a official way!终于有官方方法了! :) :)

Look at the NoSQL sample which does exactly this in this link from the Azure Storage Team Blog:在 Azure 存储团队博客的此链接中查看 NoSQL 示例,该示例正是这样做的:

Windows Azure Storage Client Library 2.0 Tables Deep Dive Windows Azure 存储客户端库 2.0 表深入探讨

There are a few ways to go about this and how you do it depends a bit on your personal preference as well as potentially performance goals.有几种方法可以解决这个问题,你如何做到这一点取决于你的个人偏好以及潜在的性能目标。

  • Create an amalgamated class that represents all queried types.创建一个代表所有查询类型的合并类。 If I had StatusUpdateEntry and a NotificationEntry, then I would simply merge each property into a single class.如果我有 StatusUpdateEntry 和 NotificationEntry,那么我只需将每个属性合并到一个类中。 The serializer will automatically fill in the correct properties and leave the others null (or default).序列化程序将自动填写正确的属性,并将其他属性保留为 null(或默认值)。 If you also put a 'type' property on the entity (calculated or set in storage), you could easily switch on that type.如果您还在实体上放置了一个“类型”属性(在存储中计算或设置),您可以轻松地打开该类型。 Since I always recommend mapping from table entity to your own type in the app, this works fine as well (the class only becomes used for DTO).由于我总是建议在应用程序中从表实体映射到您自己的类型,因此这也可以正常工作(该类仅用于 DTO)。

Example:例子:

[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";
       }
    }
}
  • Override the serialization process.覆盖序列化过程。 You can do this yourself by hooking the ReadingEntity event.您可以通过挂钩 ReadingEntity 事件自己完成此操作。 It gives you the raw XML and you can choose to serialize however you want.它为您提供原始 XML,您可以根据需要选择序列化。 Jai Haridas and Pablo Castro gave some example code for reading an entity when you don't know the type (included below), and you can adapt that to read specific types that you do know about. Jai Haridas 和 Pablo Castro 给出了一些示例代码,用于在您不知道类型(包括在下面)时读取实体,您可以修改它以读取您知道的特定类型。

The downside to both approaches is that you end up pulling more data than you need in some cases.这两种方法的缺点是,在某些情况下,您最终会提取比您需要的更多的数据。 You need to weigh this on how much you really want to query one type versus another.您需要权衡您真正想要查询一种类型与另一种类型的程度。 Keep in mind you can use projection now in Table storage, so that also reduces the wire format size and can really speed things up when you have larger entities or many to return.请记住,您现在可以在表存储中使用投影,这样也可以减少线格式大小,并且当您有更大的实体或许多实体要返回时,可以真正加快速度。 If you ever had the need to query only a single type, I would probably use part of the RowKey or PartitionKey to specify the type, which would then allow me to query only a single type at a time (you could use a property, but that is not as efficient for query purposes as PK or RK).如果您只需要查询一种类型,我可能会使用 RowKey 或 PartitionKey 的一部分来指定类型,这样我一次只能查询一种类型(您可以使用属性,但是这对于查询目的不如 PK 或 RK 有效)。

Edit: As noted by Lucifure, another great option is to design around it.编辑:正如 Lucifure 所指出的,另一个不错的选择是围绕它进行设计。 Use multiple tables, query in parallel, etc. You need to trade that off with complexity around timeouts and error handling of course, but it is a viable and often good option as well depending on your needs.使用多个表、并行查询等。当然,您需要权衡超时和错误处理的复杂性,但根据您的需要,它也是一个可行且通常是不错的选择。

Reading a Generic Entity:读取通用实体:

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

Another option, of course, is to have only a single entity type per table, query the tables in parallel and merge the result sorted by timestamp.当然,另一种选择是每个表只有一个实体类型,并行查询表并合并按时间戳排序的结果。 In the long run this may prove to be the more prudent choice with reference to scalability and maintainability.从长远来看,考虑到可扩展性和可维护性,这可能被证明是更谨慎的选择。

Alternatively you would need to use some flavor of generic entities as outlined by 'dunnry', where the non-common data is not explicitly typed and instead persisted via a dictionary.或者,您需要使用“dunnry”概述的一些通用实体,其中非公共数据没有显式输入,而是通过字典持久化。

I have written an alternate Azure table storage client, Lucifure Stash, which supports additional abstractions over azure table storage including persisting to/from a dictionary , and may work in your situation if that is the direction you want to pursue.我编写了一个替代 Azure 表存储客户端 Lucifure Stash,它支持对 azure 表存储的额外抽象,包括持久化到/从字典,如果这是你想要追求的方向,它可能适用于你的情况。

Lucifure Stash supports large data columns > 64K, arrays & lists, enumerations, composite keys, out of the box serialization, user defined morphing, public and private properties and fields and more. Lucifure Stash 支持大于 64K 的大数据列、数组和列表、枚举、复合键、开箱即用的序列化、用户定义的变形、公共和私有属性和字段等。 It is available free for personal use at http://www.lucifure.com or via NuGet.com.它可免费供个人使用,网址http://www.lucifure.com或通过 NuGet.com。

Edit: Now open sourced at CodePlex编辑:现在在CodePlex开源

Use DynamicTableEntity as the entity type in your queries.使用DynamicTableEntity作为查询中的实体类型。 It has a dictionary of properties you can look up.它有一个您可以查找的属性字典。 It can return any entity type.它可以返回任何实体类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 您如何在azure表存储中查询多个分区键? - How do you query for multiple partition keys in azure table storage? 如何使用Linq查询Azure存储表? - How do I query an Azure storage table with Linq? 如何从 TableOperation 中的 Azure 表存储中检索实体列表而不指定 rowKey? - How to retrieve a list of entities without specifying rowKey from Azure Table Storage in the TableOperation? 如何在PartitionKey中使用单引号查询azure表存储 - How to query azure table storage with single quote in PartitionKey 如何在 Azure 表存储中使用 RowKey 或时间戳检索最新记录 - How to retrieve latest record using RowKey or Timestamp in Azure Table storage 如何在 .net core 中使用 Linq 方法语法查询 Azure 存储表? - How do I query an Azure storage table with Linq method syntax in .net core? 如何从 azure 表存储查询中获取 1000 多个实体? - How to get more than 1000 entities from an azure table storage query? Azure表存储(Partition key row key),如何在同一个partition不同rowKey对应插入多个实体? - Azure table storage (Partition key row key), how to insert multiple entities in corresponding to same parttion and different rowKey? 删除Azure表存储中的实体 - Deleting entities in Azure Table Storage 在单个查询中从 CosmosDb 中获取多种类型的实体 - Fetch multiple types of entities From CosmosDb in a single query
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM