我正在尝试了解Azure表存储如何工作以创建Facebook样式的提要,而我仍然坚持如何检索条目。
(我的问题几乎与https://stackoverflow.com/questions/6843689/retrieve-multiple-type-of-entities-from-azure-table-storage相同,但答案中的链接已被破坏。)
这是我的预期方法:
为我的应用程序中的所有用户创建个人订阅源,其中包含不同类型的条目(通知,状态更新等)。我的想法是将它们存储在按每个用户的分区键分组的Azure表中。
检索同一分区键中的所有条目,并根据条目类型将其传递给不同的视图。
如何查询所有类型相同基类型的表存储 保持其独特的属性?
CloudTableQuery<TElement>
需要一个类型化实体,如果我将EntryBase
指定为通用参数我没有获得特定于条目的属性(NotificationSpecificProperty
,StatusUpdateSpecificProperty
)和副反之亦然。
我的实体:
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
}
}
答案 0 :(得分:6)
最后还有官方的方式! :)
查看NoSQL示例,该示例在Azure存储团队博客的此链接中完成此操作:
答案 1 :(得分:2)
有几种方法可以解决这个问题,如何做到这一点取决于您的个人偏好以及潜在的绩效目标。
示例:
[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";
}
}
}
这两种方法的缺点是,在某些情况下,最终会提取的数据超出您的需求。你需要权衡一下你真正想要查询一种类型与另一种类型的比例。请记住,您现在可以在表存储中使用投影,这样也可以减少有线格式的大小,并且当您有更大的实体或许多要返回时,可以真正加快速度。如果你只需要查询一个类型,我可能会使用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作为实体类型。它有一个你可以查找的属性字典。它可以返回任何实体类型。