2014-04-30 3 views
12

Je ne sais pas exactement où, mais j'ai une mauvaise idée quelque part avec ça.Comment interroger une table de stockage Azure avec Linq?

J'essaie, dans un premier temps, interroger une table de stockage azur en utilisant linq. Mais je ne peux pas comprendre comment c'est fait. De regarder une variété de sources, j'ai ce qui suit:

List<BlogViewModel> blogs = new List<BlogViewModel>(); 

CloudStorageAccount storageAccount = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("BlogConnectionString")); 
CloudTableClient tableClient = storageAccount.CreateCloudTableClient(); 
CloudTable blogTable = tableClient.GetTableReference("BlogEntries"); 

try 
{ 
    TableServiceContext tableServiceContext = tableClient.GetTableServiceContext(); 
    TableServiceQuery<BlogEntry> query = (from blog in blogTable.CreateQuery<BlogEntry>() 
    select blog).AsTableServiceQuery<BlogEntry>(tableServiceContext); 
    foreach (BlogEntry blog in query) 
    { 
     blogs.Add(new BlogViewModel { Body = blog.Body }); 
    } 
} 
catch { } 

Je l'avais probablement plus proche avant que je me suis amusé avec elle. Soit ça, soit je ne comprends pas ce qu'est le service de table. Le code suivant a fonctionné pour moi, mais j'essaye de le changer en utilisant Linq à la place.

List<BlogViewModel> blogs = new List<BlogViewModel>(); 

var storageAccount = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("BlogConnectionString")); 
var tableClient = storageAccount.CreateCloudTableClient(); 
CloudTable blogTable = tableClient.GetTableReference("BlogEntries"); 

TableRequestOptions reqOptions = new TableRequestOptions() 
{ 
    MaximumExecutionTime = TimeSpan.FromSeconds(1.5), 
    RetryPolicy = new LinearRetry(TimeSpan.FromSeconds(3), 3) 
}; 
List<BlogEntry> lists; 

try 
{ 
    var query = new TableQuery<BlogEntry>(); 
    lists = blogTable.ExecuteQuery(query, reqOptions).ToList(); 

    foreach (BlogEntry blog in lists) 
    { 
     blogs.Add(new BlogViewModel { Body = blog.Body }); 
    } 
} 
catch { } 

Je n'ai pas réussi à trouver un bon exemple solide de ce que je devrais faire. Mais d'après ce que j'ai lu, cela suggère que l'utilisation de Linq est possible. Toute aide ou pointeurs appréciés. Merci.


Légère mise à jour. Voici l'erreur de syntaxe que j'obtiens actuellement sur AsTableServiceQuery:

'System.Linq.IQueryable' ne contient pas de définition pour 'AsTableServiceQuery' et pas de méthode d'extension 'AsTableServiceQuery' acceptant un premier argument de type 'System.Linq. IQueryable 'pourrait être trouvé (vous manque une directive using ou une référence d'assembly?)

Cependant, je ne pense pas que ceci reflète le vrai problème, je pense que je l'ai juste mis ensemble mal, juste ne peux pas Trouver un exemple solide partout où cela fonctionne.

+0

ce qui se passe exactement? obtenez-vous des messages d'erreur? pas de données? – Igorek

+0

@Igorek Désolé, j'ai mis à jour avec l'erreur. Cependant, je ne pense pas que le code soit suffisamment proche de ce qu'il devrait être pour que l'erreur puisse vraiment signifier quoi que ce soit. – AndrewPolland

Répondre

23

Pour plus d'informations sur ce changement, s'il vous plaît voir notre article de blog Announcing Storage Client Library 2.1 RTM & CTP for Windows Phone.

S'il vous plaît assurez-vous que BlogEntry implémente ITableEntity puis le code suivant devrait fonctionner parfaitement:

List<BlogViewModel> blogs = new List<BlogViewModel>(); 

CloudTableClient tableClient = storageAccount.CreateCloudTableClient(); 
CloudTable blogTable = tableClient.GetTableReference("BlogEntries"); 

try 
{ 
    IEnumerable<BlogEntry> query = (from blog in blogTable.CreateQuery<BlogEntry>() 
            select blog); 
    foreach (BlogEntry blog in query) 
    { 
     blogs.Add(new BlogViewModel { Body = blog.Body }); 
    } 
} 
catch { } 
+0

Parfait. Exactement l'exemple que je cherchais. Je vous remercie. – AndrewPolland

+0

CreateQuery n'est pas pris en charge sur Windows Phone. – Senkwe

+0

Et si notre entité héritait déjà d'une autre classe? Y a-t-il une autre option pour faire la requête? – Paul

13

Mon référentiel de stockage actuel de la table fait ceci:

public IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> expression) 
{ 
    if (IsTableEmpty()) 
    { 
     return Enumerable.Empty<TEntity>().AsQueryable(); 
    } 
    else 
    { 
     return _cloudTable.CreateQuery<TEntity>().AsQueryable().Where(expression); 
    } 
} 

Mon _ cloudTable correspond à votre blogTable. TableServiceContext n'est plus nécessaire dans le nouveau Couche de service de table de la bibliothèque Azure Storage Client.

+0

Cela sauve ma journée :-) Nice! –

+0

Merci beaucoup, a également sauvé ma journée! –

1

Sur la base de la réponse précédente, j'ai créé des méthodes d'extensions pour soutenir First, FirstOrDefault, Single and SingleOrDefault:

/// <summary> 
/// Provides additional Linq support for the <see cref="TableQuery{TElement}"/> class. 
/// </summary> 
public static class LinqToTableQueryExtensions 
{ 
    /// <summary> 
    /// Returns the first element in a sequence. 
    /// </summary> 
    /// <typeparam name="TSource">The type of the elements of source.</typeparam> 
    /// <param name="tableQuery">A TableQuery{TSource} to return the first element of</param> 
    public static TSource First<TSource>(this TableQuery<TSource> tableQuery) where TSource : ITableEntity 
    { 
     return ((IEnumerable<TSource>)tableQuery.Take(1)).First(); 
    } 

    /// <summary> 
    /// Returns the first element in a sequence that satisfies a specified condition. 
    /// </summary> 
    /// <typeparam name="TSource">The type of the elements of source.</typeparam> 
    /// <param name="tableQuery">A TableQuery{TSource} to return the first element of</param> 
    /// <param name="predicate">A function to test an element for a condition.</param> 
    public static TSource First<TSource>(this TableQuery<TSource> tableQuery, Expression<Func<TSource, bool>> predicate) where TSource : ITableEntity 
    { 
     return tableQuery.Where(predicate).Take(1).First(); 
    } 

    /// <summary> 
    /// Returns the first element of the sequence or a default value if no such element is found. 
    /// </summary> 
    /// <typeparam name="TSource">The type of the elements of source.</typeparam> 
    /// <param name="tableQuery">A TableQuery{TSource} to return the first element of</param> 
    public static TSource FirstOrDefault<TSource>(this TableQuery<TSource> tableQuery) where TSource : ITableEntity 
    { 
     return ((IEnumerable<TSource>)tableQuery.Take(1)).FirstOrDefault(); 
    } 

    /// <summary> 
    /// Returns the first element of the sequence that satisfies a condition or a default value if no such element is found. 
    /// </summary> 
    /// <typeparam name="TSource">The type of the elements of source.</typeparam> 
    /// <param name="tableQuery">A TableQuery{TSource} to return the first element of</param> 
    /// <param name="predicate">A function to test an element for a condition.</param> 
    public static TSource FirstOrDefault<TSource>(this TableQuery<TSource> tableQuery, Expression<Func<TSource, bool>> predicate) where TSource : ITableEntity 
    { 
     return tableQuery.Where(predicate).Take(1).FirstOrDefault(); 
    } 

    /// <summary> 
    /// Return the only element of a sequence, and throws an exception if there is no exactly one element in the sequence. 
    /// </summary> 
    /// <typeparam name="TSource">The type of the elements of source.</typeparam> 
    /// <param name="tableQuery">A TableQuery{TSource}> to return the single element of</param> 
    /// <param name="predicate">A function to test an element for a condition.</param> 
    public static TSource Single<TSource>(this TableQuery<TSource> tableQuery, Expression<Func<TSource, bool>> predicate) where TSource : ITableEntity 
    { 
     // Get 2 and try to get single ^^ 
     return tableQuery.Where(predicate).Take(2).Single(); 
    } 

    /// <summary> 
    /// Returns the only element of a sequence, or a default value if the sequence is empty; this method throws an exception if there is more than one element in the sequence. 
    /// </summary> 
    /// <typeparam name="TSource">The type of the elements of source.</typeparam> 
    /// <param name="tableQuery">A TableQuery{TSource}> to return the single element of</param> 
    /// <param name="predicate">A function to test an element for a condition.</param> 
    public static TSource SingleOrDefault<TSource>(this TableQuery<TSource> tableQuery, Expression<Func<TSource, bool>> predicate) where TSource : ITableEntity 
    { 
     // Get 2 and try to get single ^^ 
     return tableQuery.Where(predicate).Take(2).SingleOrDefault(); 
    } 
} 

Alors vous pouvez l'utiliser comme ça:

public class CustomerEntity : TableEntity { public string Email { get; set; } } 
... 
var storageAccount = CloudStorageAccount.Parse("MyStorageAccountConnectionstring"); 
var tableClient = storageAccount.CreateCloudTableClient(); 
var table = tableClient.GetTableReference("myTable"); 

// Linq Query with Where And First 
var person = table.CreateQuery<CustomerEntity>() 
    .Where(c => c.Email == "[email protected]").First(); 

// Linq query that used the First() Extension method 
person = table.CreateQuery<CustomerEntity>() 
    .First(c => c.Email == "[email protected]"); 
+0

Quelle est l'utilisation prévue? 'TableQuery' ne contient aucune donnée et doit être exécutée en utilisant' CloudTable.Execute (query) 'ou une méthode similaire. 'First *()' & 'Single *()' retournera des non-queryables qui ne peuvent pas être passés à 'Execute()'. – JoeBrockhaus

+0

@JoeBrockhaus, j'ai édité ma réponse pour montrer comment utiliser ces méthodes d'extension. – Thomas

+0

Je vois, le verbiage dans la méthode CreateQuery donne l'impression que vous devez appeler explicitement l'une des méthodes Execute. C'est assez ennuyeux seulement une partie de l'interface IQueryable est supportée, et on ne sait pas lequel au moment de la compilation: - / – JoeBrockhaus

0

Voici quelques méthodes d'extension à portée de main pour envelopper ce .. (et un cas de bonus pour ServiceBus Propriétés personnalisées keystore)

namespace Microsoft.WindowsAzure.Storage.Table 
{ 
    public static class CloudTableExtensions 
    { 
     public static TEntity GetTableEntity<TEntity>(this CloudTable cloudTable, BrokeredMessage brokeredMessage, string partitionKeyPropertyName, string rowKeyPropertyName, TableRequestOptions requestOptions = null, OperationContext operationContext = null) 
      where TEntity : ITableEntity, new() 
     { 
      var partitionKey = brokeredMessage.Properties[partitionKeyPropertyName] as string; 
      var rowKey = brokeredMessage.Properties[rowKeyPropertyName] as string; 
      return GetTableEntity<TEntity>(cloudTable, partitionKey, rowKey, requestOptions, operationContext); 
     } 

     public static TEntity GetTableEntity<TEntity>(this CloudTable cloudTable, string partitionKey, string rowKey, TableRequestOptions requestOptions = null, OperationContext operationContext = null) 
      where TEntity : ITableEntity, new() 
     { 
      var singleInstanceQuery = (Expression<Func<TEntity, bool>>)(x => x.PartitionKey == partitionKey && x.RowKey == rowKey); 
      IEnumerable<TEntity> queryResults = cloudTable.ExecuteQuery(singleInstanceQuery, requestOptions, operationContext); 
      return queryResults.SingleOrDefault(); 
     } 

     public static IEnumerable<TEntity> ExecuteQuery<TEntity>(this CloudTable cloudTable, Expression<Func<TEntity, bool>> expression, TableRequestOptions requestOptions = null, OperationContext operationContext = null) 
      where TEntity : ITableEntity, new() 
     { 
      var query = cloudTable.CreateQuery<TEntity>().Where(expression) as TableQuery<TEntity>; 
      return cloudTable.ExecuteQuery(query, requestOptions, operationContext); 
     } 
    } 
} 
Questions connexes