PageRenderTime 42ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/Visual Studio 2010/VBAzureTableStoragePaging/MvcWebRole/Utilities/TableStoragePagingUtility.vb

#
Visual Basic | 251 lines | 153 code | 12 blank | 86 comment | 0 complexity | e54616dd6487361647e69146a70bc87c MD5 | raw file
  1. '***************************** Module Header ******************************\
  2. '* Module Name: TableStoragePagingUtility.vb
  3. '* Project: AzureTableStoragePaging
  4. '* Copyright (c) Microsoft Corporation.
  5. '*
  6. '* This class can be reused for other applications. If you want
  7. '* to reuse the code, what you need to do is to implement custom ICachedDataProvider<T>
  8. '* class to store data required by TableStoragePagingUtility<T>.
  9. '*
  10. '* This source is subject to the Microsoft Public License.
  11. '* See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
  12. '* All other rights reserved.
  13. '*
  14. '* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
  15. '* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
  16. '* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
  17. '\**************************************************************************
  18. Imports Microsoft.WindowsAzure
  19. Imports Microsoft.WindowsAzure.StorageClient
  20. Namespace Utilities
  21. Public Class TableStoragePagingUtility(Of T)
  22. Private _cloudStorageAccount As CloudStorageAccount
  23. Private _provider As ICachedDataProvider(Of T)
  24. Private _tableServiceContext As TableServiceContext
  25. Private Property RC() As ResultContinuation
  26. Get
  27. Return _provider.GetResultContinuation()
  28. End Get
  29. Set(ByVal value As ResultContinuation)
  30. _provider.SetResultContinuation(value)
  31. End Set
  32. End Property
  33. Private _entitySetName As String
  34. Private _pageSize As Integer
  35. Public Property CurrentPageIndex() As Integer
  36. Get
  37. Return _provider.GetCurrentIndex()
  38. End Get
  39. Private Set(ByVal value As Integer)
  40. _provider.SetCurrentIndex(value)
  41. End Set
  42. End Property
  43. Public Property PageSize() As Integer
  44. Get
  45. Return _pageSize
  46. End Get
  47. Private Set(ByVal value As Integer)
  48. _pageSize = value
  49. End Set
  50. End Property
  51. Public Sub New(ByVal provider As ICachedDataProvider(Of T), ByVal cloudStorageAccount As CloudStorageAccount, ByVal tableServiceContext As TableServiceContext, ByVal pageSize As Integer, ByVal entitySetName As String)
  52. Me._provider = Provider
  53. Me._cloudStorageAccount = CloudStorageAccount
  54. Me._entitySetName = entitySetName
  55. Me._tableServiceContext = TableServiceContext
  56. If PageSize <= 0 Then
  57. Throw New IndexOutOfRangeException("pageSize out of range")
  58. End If
  59. Me.PageSize = PageSize
  60. End Sub
  61. ''' <summary>
  62. ''' Get the next page
  63. ''' </summary>
  64. ''' <returns>The next page. If current page is the last page it returns the last page.</returns>
  65. Public Function GetNextPage() As IEnumerable(Of T)
  66. ' Get cached data
  67. Dim cachedData = Me._provider.GetCachedData()
  68. Dim pageCount As Integer = 0
  69. If Not cachedData Is Nothing Then
  70. pageCount = Convert.ToInt32(Math.Ceiling(CDbl(cachedData.Count()) / CDbl(Me.PageSize)))
  71. End If
  72. ' If there still has entities in table storage to read and the current page is the last page,
  73. ' request table storage to get new data.
  74. If (Not Me._provider.HasReachedEnd) AndAlso CurrentPageIndex = pageCount - 1 Then
  75. Dim q = Me._tableServiceContext.CreateQuery(Of T)(Me._entitySetName).Take(PageSize).AsTableServiceQuery()
  76. Dim r As IAsyncResult
  77. r = q.BeginExecuteSegmented(RC, Function(ar) 1 = 1, q)
  78. r.AsyncWaitHandle.WaitOne()
  79. Dim result As ResultSegment(Of T) = q.EndExecuteSegmented(r)
  80. Dim results = result.Results
  81. Me._provider.AddDataToCache(results)
  82. ' If there's any entity returns we need to increase pageCount
  83. If results.Count() > 0 Then
  84. pageCount += 1
  85. End If
  86. RC = result.ContinuationToken
  87. ' If the returned token is null it means there's no more entities in table
  88. If result.ContinuationToken Is Nothing Then
  89. Me._provider.HasReachedEnd = True
  90. End If
  91. End If
  92. If CurrentPageIndex + 1 < pageCount Then
  93. CurrentPageIndex = CurrentPageIndex + 1
  94. Else
  95. CurrentPageIndex = pageCount - 1
  96. End If
  97. If cachedData Is Nothing Then
  98. cachedData = Me._provider.GetCachedData()
  99. End If
  100. Return cachedData.Skip((Me.CurrentPageIndex) * Me.PageSize).Take(Me.PageSize)
  101. End Function
  102. ''' <summary>
  103. ''' Get the previous page
  104. ''' </summary>
  105. ''' <returns>The previous page. If current page is the first page it returns the first page. If there's no data in cache,
  106. ''' returns an empty collection/></returns>
  107. Public Function GetPreviousPage() As IEnumerable(Of T)
  108. Dim cachedData = Me._provider.GetCachedData()
  109. If Not cachedData Is Nothing AndAlso cachedData.Count() > 0 Then
  110. If CurrentPageIndex - 1 < 0 Then
  111. CurrentPageIndex = 0
  112. Else
  113. CurrentPageIndex = CurrentPageIndex - 1
  114. End If
  115. Return cachedData.Skip(Me.CurrentPageIndex * Me.PageSize).Take(Me.PageSize)
  116. Else
  117. Return New List(Of T)()
  118. End If
  119. End Function
  120. ''' <summary>
  121. ''' If there're entities cached, return the current page. Else retrieve table storage and
  122. ''' return the first page.
  123. ''' </summary>
  124. ''' <returns>The current page if there're entities cached. If there's no data cached first
  125. ''' page will be retrieved from table storage and returned.</returns>
  126. Public Function GetCurrentOrFirstPage() As IEnumerable(Of T)
  127. Dim cachedData = Me._provider.GetCachedData()
  128. If Not cachedData Is Nothing AndAlso cachedData.Count() > 0 Then
  129. Return cachedData.Skip(Me.CurrentPageIndex * Me.PageSize).Take(Me.PageSize)
  130. Else
  131. Return GetNextPage()
  132. End If
  133. End Function
  134. End Class
  135. ''' <summary>
  136. ''' The class implements this interface must take the responsibility of cache storage, including
  137. ''' data, ResultContinuation and HasReachedEnd flag.
  138. ''' </summary>
  139. ''' <typeparam name="T"></typeparam>
  140. Public Interface ICachedDataProvider(Of T)
  141. ''' <summary>
  142. ''' Return all cached data
  143. ''' </summary>
  144. ''' <returns></returns>
  145. Function GetCachedData() As IEnumerable(Of T)
  146. ''' <summary>
  147. ''' Save data to cache
  148. ''' </summary>
  149. ''' <param name="data">Data that user of this provider wants to add to cache</param>
  150. Sub AddDataToCache(ByVal data As IEnumerable(Of T))
  151. ''' <summary>
  152. ''' Save Current index
  153. ''' </summary>
  154. ''' <param name="index">Current page index sent from user of this provider</param>
  155. Sub SetCurrentIndex(ByVal index As Integer)
  156. ''' <summary>
  157. ''' Get Current index
  158. ''' </summary>
  159. ''' <returns>Current page index preserved in cache</returns>
  160. Function GetCurrentIndex() As Integer
  161. ''' <summary>
  162. ''' Set continuation token
  163. ''' </summary>
  164. ''' <param name="rc">ResultContinuation sent from user of this provider</param>
  165. Sub SetResultContinuation(ByVal rc As ResultContinuation)
  166. ''' <summary>
  167. ''' Get continuation token
  168. ''' </summary>
  169. ''' <returns>ResultContinuation preserved in cache</returns>
  170. Function GetResultContinuation() As ResultContinuation
  171. ''' <summary>
  172. ''' A flag tells the user of this provider whether he can fully rely on cache without
  173. ''' the need to fetch new data from table storage.
  174. ''' </summary>
  175. Property HasReachedEnd() As Boolean
  176. End Interface
  177. ''' <summary>
  178. ''' A sample implementation of ICachedDataProvider<T> that caches data in session for MVC
  179. ''' applications. Because the implementation of it uses Session of MVC the user of this class
  180. ''' need to be educated not to use reserved keywords of this class in their other session variables,
  181. ''' (such as one starts with "currentindex"). If they have to use it they can specify a special id
  182. ''' to distinguish them.
  183. ''' </summary>
  184. ''' <typeparam name="T"></typeparam>
  185. Public Class MVCSessionCachedDataProvider(Of T)
  186. Implements ICachedDataProvider(Of T)
  187. Private _session As HttpSessionStateBase
  188. Private _id As String
  189. ''' <summary>
  190. ''' Constructor
  191. ''' </summary>
  192. ''' <param name="c">Generally specify this for this parameter</param>
  193. ''' <param name="id">The id of the cache provider. You need to use the same id to access
  194. ''' the same cache store</param>
  195. Public Sub New(ByVal c As Controller, ByVal id As String)
  196. _session = c.Session
  197. _id = id
  198. ' Initialize currentindex
  199. If _session("currentindex" & Me._id) Is Nothing Then
  200. _session("currentindex" & Me._id) = -1
  201. End If
  202. ' Initialize hasreachedend flag to indicate whether there's no need to retrieve new data
  203. If _session("hasreachedend" & Me._id) Is Nothing Then
  204. _session("hasreachedend" & Me._id) = False
  205. End If
  206. End Sub
  207. Public Function GetCachedData() As IEnumerable(Of T) Implements ICachedDataProvider(Of T).GetCachedData
  208. Return TryCast(_session("inmemorycacheddata" & Me._id), List(Of T))
  209. End Function
  210. Public Sub AddDataToCache(ByVal data As IEnumerable(Of T)) Implements ICachedDataProvider(Of T).AddDataToCache
  211. Dim inmemorycacheddata = TryCast(_session("inmemorycacheddata" & Me._id), List(Of T))
  212. If inmemorycacheddata Is Nothing Then
  213. inmemorycacheddata = New List(Of T)()
  214. End If
  215. inmemorycacheddata.AddRange(data)
  216. _session("inmemorycacheddata" & Me._id) = inmemorycacheddata
  217. End Sub
  218. Public Sub SetCurrentIndex(ByVal index As Integer) Implements ICachedDataProvider(Of T).SetCurrentIndex
  219. _session("currentindex" & Me._id) = index
  220. End Sub
  221. Public Function GetCurrentIndex() As Integer Implements ICachedDataProvider(Of T).GetCurrentIndex
  222. Return Convert.ToInt32(_session("currentindex" & Me._id))
  223. End Function
  224. Public Function GetResultContinuation() As ResultContinuation Implements ICachedDataProvider(Of T).GetResultContinuation
  225. Return TryCast(_session("resultcontinuation" & Me._id), ResultContinuation)
  226. End Function
  227. Public Sub SetResultContinuation(ByVal rc As ResultContinuation) Implements ICachedDataProvider(Of T).SetResultContinuation
  228. _session("resultcontinuation" & Me._id) = rc
  229. End Sub
  230. Public Property HasReachedEnd() As Boolean Implements ICachedDataProvider(Of T).HasReachedEnd
  231. Get
  232. Return Convert.ToBoolean(_session("hasreachedend" & Me._id))
  233. End Get
  234. Set(ByVal value As Boolean)
  235. _session("hasreachedend" & Me._id) = value
  236. End Set
  237. End Property
  238. End Class
  239. End Namespace