Table Storage对分页的支持

大家可能知道WCF Data Services最新版提供了server paging 的功能,意即在服务期端对数据进行分页,从而限制传回客户端的数据量。那么Windows Azure table storage是否提供分页功能呢?

Windows Azure table storage本身就限制了客户端一次性可以访问的数据量不能超过100条。当你需要访问的数据量超过100条时,只有前100条数据会被返回至客户端。 同时response会包含一个x-ms-continuation-NextPartitionKey和x-ms-continuation- NextRowKey (通称continuation token),它代表着第101条数据的key。在后续的请求中,你可以将之前或取得continuation token通过query string传递给table storage,从而取得第101至第200条数据。

同理,当你使用take这个query string强制限制返回的数据量时,continuation token也会被包含于response中。

使用过新版本WCF Data Services的server paging功能的用户应该清楚如何在客户端程序中控制continuation token,从而对数据进行分页。在table storage中,你也可以使用类似的方式。只不过用法稍有不同。有关continuation token的基本用法,请参考http://blog.smarx.com/posts/windows-azure-tables-expect-continuation-tokens-seriously

然而你或许很快会发现server paging的局限性。是的,server paging的作用主要在于限制客户端一次性可以访问的数据量,而不在于帮助客户端构建起分页导航的功能。通过continuation token,你可以方便地取得下一个页面的数据。但是如果你要反过来取得之前一个页面的数据,就不那么简单了……

通常客户端的分页导航功能需要client paging来实现。例如,在WCF Data Services中,你可以通过skip和take这样的query string来要求服务返回从特定的起始点到特定的终结点的数据。然后很遗憾,table storage是不支持skip的,于是也就无法完整地使用client paging。

一个部分的解决方法是,你必须手工保存一个continuation token的列表。只要有了continuation token,我们就可以要求table storage从特定的记录开始返回。至于如何保存continuation token的列表,方式是多种多样的。以下演示如何将历史记录信息存储在cookie中,从而在ASP.NET中实现分页导航功能。

MVC的controller(控制导航逻辑):

               public  ActionResult  Index()

               {

                      //  使用 JSON continuation token 的历史信息存储到 cookie 中。

                      string  historyString = null ;

                      if  (Request.Cookies["history" ] != null )

                      {

                            historyString = Request.Cookies["history" ].Value;

                      }

                      using  (MemoryStream  stream = new  MemoryStream ())

                      {

                            using  (StreamWriter  writer = new  StreamWriter (stream))

                            {

                                   writer.Write(historyString);

                                   writer.Flush();

                                   DataContractJsonSerializer  serializer = new DataContractJsonSerializer (typeof (List <string >));

                                   List <string > history = null ;

                                   try

                                   {

                                          stream.Position = 0;

                                          //  JSON 返序列化。

                                          history = serializer.ReadObject(stream) as  List <string >;

                                   }

                                   catch

                                   {

                                   }

                                   if  (history == null )

                                   {

                                          history = new  List <string >();

                                   }

                                   StorageCredentialsAccountAndKey  credential = new StorageCredentialsAccountAndKey ("[account]" , "[key]" );

                                  CloudStorageAccount  account = new  CloudStorageAccount (credential,false );

                                   CloudTableClient  tableClient = new CloudTableClient (account.TableEndpoint.ToString(), account.Credentials);

                                   CustomerServiceContext  ctx = new CustomerServiceContext (account.TableEndpoint.ToString(), account.Credentials);

 

                                   var  query = (DataServiceQuery <Customer >)(ctx.Customer.Take(10));

 

                                   //  currentPage 传入 query string

 

                                   string  currentPage = Request["currentPage" ];

 

                                   int  currentPageNumber = 0;

 

                                   if  (!string .IsNullOrEmpty(currentPage))

                                   {

 

                                          currentPageNumber = int .Parse(currentPage);

 

                                   }

 

                                 // action 表明用户想要导航至上一页,从历史记录中取得对应的 continuation token

 

                                   string  action = Request["action" ];

 

                                   if  (action == "previous" )

                                   {

 

                                          currentPageNumber--;

 

                                          if  (currentPageNumber != 0)

                                          {

 

                                                 string [] tokens = history[currentPageNumber].Split('/' );

 

                                                 var  partitionToken = tokens[0];

 

                                                 var  rowToken = tokens[1];

 

                                                 query = query

 

               .AddQueryOption("NextPartitionKey" , partitionToken)

 

               .AddQueryOption("NextRowKey" , rowToken);

 

                                          }

 

                                   }

 

                                 // action 表明用户想要导航至上一页,从历史记录中取得对应的 continuation token

                                   else  if  (action == "next" )

                                   {

 

                                          currentPageNumber++;

 

                                          string  continuation = Request["ct" ];

 

                                          if  (continuation != null )

                                          {

 

                                                 string [] tokens = continuation.Split('/' );

 

                                                 var  partitionToken = tokens[0];

 

                                                 var  rowToken = tokens[1];

 

                                                 query = query

 

                                                          .AddQueryOption("NextPartitionKey" , partitionToken)

 

                                                           .AddQueryOption("NextRowKey" , rowToken);

 

                                          }

 

                                   }

 

                                   var  res = query.Execute();

 

                                   var  qor = (QueryOperationResponse )res;

 

                                   string  nextPartition = null ;

 

                                   string  nextRow = null ;

 

                                   qor.Headers.TryGetValue("x-ms-continuation-NextPartitionKey" , out nextPartition);

 

                                   qor.Headers.TryGetValue("x-ms-continuation-NextRowKey" , out nextRow);

 

                                   if  (nextPartition != null  && nextRow != null )

                                   {

 

                                         ViewData["continuation" ] = string .Format("{0}/{1}" , nextPartition, nextRow);

 

                                   }

 

                                   var  result = res.ToList();

 

                                   var  firstEntity = result.First();

 

                                   //  将新的 continuation token 添加到历史记录中,如果它尚不存在的话……

 

                                   string  historyEntry = string .Format("{0}/{1}" , firstEntity.PartitionKey, firstEntity.RowKey);

 

                                   if  (!history.Contains(historyEntry))

                                   {

 

                                          history.Add(historyEntry);

 

                                   }

 

                                   //  序列化成 JSON

                                   stream.Position = 0;

                                   serializer.WriteObject(stream, history);

                                   stream.Position = 0;

                                   using  (StreamReader  reader = new  StreamReader (stream))

                                   {

                                          historyString = reader.ReadToEnd();

                                          Response.Cookies.Add(new  HttpCookie ("history" , historyString));

                                   }

                                   ViewData["currentPageNumber" ] = currentPageNumber;

 

                                   ViewData["result" ] = result;

 

                                   return  View();

                            }

                      }

               }

MVC的view(显示Previous/Next导航):

             < a  href ='?currentPage= <% =  ViewData["currentPageNumber"] %> &action=previous'> Previous</ a >  

             < a  href ='?ct= <% =  ViewData["continuation"] %> &currentPage= <% =  ViewData["currentPageNumber"]%> &action=next'> Next</ a >

当然,这种方式也是有局限性的。例如,用户无法从中间某个页面开始访问数据,必须从第一页开始访问,从而能 够建起一个continuation token的历史记录。由于table storage不支持count,我们也无法显示总共有几个页面。这是server paging的局限性,通常server paging被用于限制客户端一次性最多能访问多少数据,从而避免一些可能因数据量太多而发生的问题(诸如连接超时)。要构建起完整的分页导航功能,还需 要client paging的支持。然而在目前table storage只支持server paging的情况下,上述解决方案让你能够构建起基本的分页导航功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值