2012-05-22 31 views
5

Estoy tratando de entender cómo funciona el almacenamiento de tablas Azure para crear feeds estilo facebook y estoy atascado en cómo recuperar las entradas.¿Cómo recupero múltiples tipos de entidades usando una sola consulta para Azure Table Storage?

(Mis preguntas es casi el mismo que https://stackoverflow.com/questions/6843689/retrieve-multiple-type-of-entities-from-azure-table-storage pero el enlace en la respuesta se rompe.)

Este es mi aproximación que se trate:

  1. Crear una alimentación de personal para todos los usuarios dentro de mi aplicación que puede contener diferentes tipos de entradas (notificación, actualización de estado, etc.). Mi idea es almacenarlos en una tabla de Azure agrupada por una clave de partición para cada usuario.

  2. Recupere todas las entradas dentro de la misma clave de partición y páselo a diferentes vistas según el tipo de entrada.

¿Cómo consultar la tabla de almacenamiento para todos los tipos del mismo tipo de base , manteniendo sus propiedades únicas?

El CloudTableQuery<TElement> requiere una entidad con tipo, si puedo especificar EntryBase como argumento genérico que no entiendo las propiedades específicas de la entrada (NotificationSpecificProperty, StatusUpdateSpecificProperty) y viceversa.

Mis entidades:

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

Mi consulta para una alimentación:

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 

    } 

} 

Respuesta

2

Hay algunas maneras de hacer esto y cómo lo hace depende un poco de su preferencia personal así como también metas de rendimiento potencial.

  • Cree una clase amalgamada que represente todos los tipos consultados. Si tuviera StatusUpdateEntry y un NotificationEntry, simplemente fusionaría cada propiedad en una sola clase. El serializador será automáticamente completará las propiedades correctas y dejará las demás nulas (o predeterminadas). Si también coloca una propiedad 'tipo' en la entidad (calculada o configurada en almacenamiento), podría fácilmente activar ese tipo. Como siempre recomiendo mapear desde entidad de tabla a su propio tipo en la aplicación, esto también funciona bien (la clase solo se usa para DTO).

Ejemplo:

[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"; 
     } 
    } 
} 
  • omitir el proceso de serialización. Puede hacerlo usted mismo enganchando el evento ReadingEntity. Le da el XML sin procesar y puede elegir serializar como quiera. Jai Haridas y Pablo Castro dieron un código de ejemplo para leer una entidad cuando no se conoce el tipo (incluido a continuación), y se puede adaptar para leer tipos específicos que usted conoce.

La desventaja de ambos enfoques es que en algunos casos termina extrayendo más datos de los que necesita. Debe ponderar cuánto desea realmente consultar un tipo frente a otro. Tenga en cuenta que puede usar la proyección ahora en el almacenamiento de la Tabla, por lo que también reduce el tamaño del formato del cable y puede acelerar realmente las cosas cuando tiene entidades más grandes o muchas más para regresar. Si alguna vez tuvo la necesidad de consultar solo un tipo, probablemente usaría RowKey o PartitionKey para especificar el tipo, lo que me permitiría consultar solo un tipo a la vez (podría usar una propiedad, pero eso no es tan eficiente para fines de consulta como PK o RK).

Editar: Como señaló Lucifure, otra gran opción es diseñar a su alrededor. Utiliza múltiples tablas, consultas en paralelo, etc. Necesitas intercambiar eso con la complejidad de los tiempos de espera y el manejo de errores, por supuesto, pero también es una opción viable y, a menudo, buena dependiendo de tus necesidades.

la lectura de un Entidad genérico:

[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

Espero que la gente se de cuenta de que esta es una respuesta muy antigua ahora. Las entidades genéricas o dinámicas se admiten directamente en el SDK de almacenamiento. – dunnry

1

Otra opción, por supuesto, es tener un solo tipo de entidad por mesa, consultar las tablas en paralelo y combinar el resultado ordenados por fecha y hora. A largo plazo, esta puede ser la opción más prudente en cuanto a escalabilidad y facilidad de mantenimiento.

Como alternativa, necesitaría usar un poco de sabor de entidades genéricas como se indica en 'dunnry', donde los datos no comunes no se escriben explícitamente y en su lugar se conservan a través de un diccionario.

He escrito un cliente alternativo Azure de almacenamiento de tabla, Lucifure Stash, que apoya abstracciones adicionales sobre el almacenamiento de tablas azul incluyendo persistente a/de un diccionario, y puede trabajar en su situación si esa es la dirección que desea seguir .

Lucifure Stash admite columnas de datos grandes> 64K, matrices & listas, enumeraciones, claves compuestas, serialización fuera de la caja, morphing definido por el usuario, propiedades y campos públicos y privados, y más. Está disponible gratis para uso personal en el http://www.lucifure.com o en NuGet.com.

Editar: Ahora código abierto en CodePlex

1

Uso DynamicTableEntity como el tipo de entidad en sus consultas. Tiene un diccionario de propiedades que puede buscar. Puede devolver cualquier tipo de entidad.

Cuestiones relacionadas