2014-04-30 48 views
12

我不确定究竟在哪里,但我在这个地方有错误的想法。如何使用Linq查询Azure存储表?

我试图在第一个实例中使用linq查询一个azure存储表。但我无法弄清楚它是如何完成的。从各种来源看,我有以下几种:

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

在我弄乱它之前,我可能已经更接近它了。要么,要么我误解了TableService的内容。下面的代码对我有用,但我试图将它切换为使用Linq。

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

我一直无法在我应该做的任何地方找到一个很好的实例。但从我一直在阅读的内容来看,它确实表明使用Linq是可能的。任何帮助或指针赞赏。谢谢。


轻微更新。以下是我当前在AsTableServiceQuery上得到的语法错误:

'System.Linq.IQueryable'不包含'AsTableServiceQuery'的定义,也没有接受'System.Linq'类型的第一个参数的扩展方法'AsTableServiceQuery'。 IQueryable“可以找到(你是否缺少使用指令或程序集引用?)

但是,我不认为这反映了真正的问题,我认为我刚刚把它放在一起错了,只是不能在任何地方找到一个可靠的例子。

+0

究竟是怎么回事?你是否收到错误消息?没有数据? – Igorek

+0

@Igorek对不起,我已更新错误。但是,我不认为代码足够接近错误的真正含义。 – AndrewPolland

回答

23

Azure Storage Client Library的新表服务层不再需要TableServiceContext。有关此更改的更多信息,请参阅我们的博客文章Announcing Storage Client Library 2.1 RTM & CTP for Windows Phone

请确保BlogEntry实现ITableEntity然后将下面的代码应该只是罚款:

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

完美。正是我期待的例子。谢谢。 Windows Phone不支持 – AndrewPolland

+0

CreateQuery 。 – Senkwe

+0

如果我们的实体已经从另一个类继承,该怎么办?是否有任何其他选项来执行查询? – Paul

13

我现在的表存储库这样处理:

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

我_ cloudTable对应于你blogTable

+0

这节省了我的一天:-)很好! –

+0

非常感谢,也救了我的一天! –

1

基于前面的回答中,我创建的扩展方式,支持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(); 
    } 
} 

所以你可以这样使用它:

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

这是什么意图? 'TableQuery'不包含任何数据,并且必须使用'CloudTable.Execute(query)'或类似方法执行。 'First *()'&'Single *()'将返回不能传递给'Execute()'的非查询。 – JoeBrockhaus

+0

@JoeBrockhaus,我编辑了我的答案以展示如何使用这些扩展方法。 – Thomas

+0

我看到,CreateQuery方法中的错误使得它看起来像你必须明确地调用其中一个Execute方法。这很烦人,只支持IQueryable接口的一部分,并且不清楚在编译时: - / – JoeBrockhaus

0

这里是一些便于扩展方法来包装这个.. (和奖金情况下ServiceBus自定义属性密钥库)

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