PageRenderTime 64ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/Visual Studio 2008/CSWinFormDataGridView/JustInTimeDataLoading/Cache.cs

#
C# | 345 lines | 239 code | 49 blank | 57 comment | 17 complexity | 606819fae7e48bf41b4624fc1fd44620 MD5 | raw file
  1. /************************************* Module Header **************************************\
  2. * Module Name: JustInTimeDataLoading.Cache
  3. * Project: CSWinFormDataGridView
  4. * Copyright (c) Microsoft Corporation.
  5. *
  6. *
  7. * This sample demonstrates how to use virtual mode in the DataGridView control
  8. * with a data cache that loads data from a server only when it is needed.
  9. * This kind of data loading is called "Just-in-time data loading".
  10. *
  11. * This source is subject to the Microsoft Public License.
  12. * See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
  13. * All other rights reserved.
  14. *
  15. * History:
  16. * * 6/10/2009 3:00 PM Zhi-Xin Ye Created
  17. \******************************************************************************************/
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Linq;
  21. using System.Text;
  22. using System.Data;
  23. using System.Data.SqlClient;
  24. namespace CSWinFormDataGridView.JustInTimeDataLoading
  25. {
  26. #region Cache Class
  27. public class Cache
  28. {
  29. private static int RowsPerPage;
  30. /// <summary>
  31. /// Represents one page of data.
  32. /// </summary>
  33. public struct DataPage
  34. {
  35. public DataTable table;
  36. private int lowestIndexValue;
  37. private int highestIndexValue;
  38. public DataPage(DataTable table, int rowIndex)
  39. {
  40. this.table = table;
  41. lowestIndexValue = MapToLowerBoundary(rowIndex);
  42. highestIndexValue = MapToUpperBoundary(rowIndex);
  43. System.Diagnostics.Debug.Assert(lowestIndexValue >= 0);
  44. System.Diagnostics.Debug.Assert(highestIndexValue >= 0);
  45. }
  46. public int LowestIndex
  47. {
  48. get
  49. {
  50. return lowestIndexValue;
  51. }
  52. }
  53. public int HighestIndex
  54. {
  55. get
  56. {
  57. return highestIndexValue;
  58. }
  59. }
  60. public static int MapToLowerBoundary(int rowIndex)
  61. {
  62. // Return the lowest index of a page containing the given index.
  63. return (rowIndex / RowsPerPage) * RowsPerPage;
  64. }
  65. private static int MapToUpperBoundary(int rowIndex)
  66. {
  67. // Return the highest index of a page containing the given index.
  68. return MapToLowerBoundary(rowIndex) + RowsPerPage - 1;
  69. }
  70. }
  71. private DataPage[] cachePages;
  72. private IDataPageRetriever dataSupply;
  73. public Cache(IDataPageRetriever dataSupplier, int rowsPerPage)
  74. {
  75. dataSupply = dataSupplier;
  76. Cache.RowsPerPage = rowsPerPage;
  77. LoadFirstTwoPages();
  78. }
  79. /// <summary>
  80. /// Sets the value of the element parameter if the value is in the cache.
  81. /// </summary>
  82. /// <param name="rowIndex"></param>
  83. /// <param name="columnIndex"></param>
  84. /// <param name="element"></param>
  85. /// <returns></returns>
  86. private bool IfPageCached_ThenSetElement(int rowIndex,
  87. int columnIndex, ref string element)
  88. {
  89. if (IsRowCachedInPage(0, rowIndex))
  90. {
  91. element = cachePages[0].table
  92. .Rows[rowIndex % RowsPerPage][columnIndex].ToString();
  93. return true;
  94. }
  95. else if (IsRowCachedInPage(1, rowIndex))
  96. {
  97. element = cachePages[1].table
  98. .Rows[rowIndex % RowsPerPage][columnIndex].ToString();
  99. return true;
  100. }
  101. return false;
  102. }
  103. public string RetrieveElement(int rowIndex, int columnIndex)
  104. {
  105. string element = null;
  106. if (IfPageCached_ThenSetElement(rowIndex, columnIndex, ref element))
  107. {
  108. return element;
  109. }
  110. else
  111. {
  112. return RetrieveData_CacheIt_ThenReturnElement(
  113. rowIndex, columnIndex);
  114. }
  115. }
  116. private void LoadFirstTwoPages()
  117. {
  118. cachePages = new DataPage[]{
  119. new DataPage(dataSupply.SupplyPageOfData(
  120. DataPage.MapToLowerBoundary(0), RowsPerPage), 0),
  121. new DataPage(dataSupply.SupplyPageOfData(
  122. DataPage.MapToLowerBoundary(RowsPerPage),
  123. RowsPerPage), RowsPerPage)};
  124. }
  125. private string RetrieveData_CacheIt_ThenReturnElement(
  126. int rowIndex, int columnIndex)
  127. {
  128. // Retrieve a page worth of data containing the requested value.
  129. DataTable table = dataSupply.SupplyPageOfData(
  130. DataPage.MapToLowerBoundary(rowIndex), RowsPerPage);
  131. // Replace the cached page furthest from the requested cell
  132. // with a new page containing the newly retrieved data.
  133. cachePages[GetIndexToUnusedPage(rowIndex)] = new DataPage(table, rowIndex);
  134. return RetrieveElement(rowIndex, columnIndex);
  135. }
  136. /// <summary>
  137. /// Returns the index of the cached page most distant from the given index
  138. /// and therefore least likely to be reused.
  139. /// </summary>
  140. /// <param name="rowIndex"></param>
  141. /// <returns></returns>
  142. private int GetIndexToUnusedPage(int rowIndex)
  143. {
  144. if (rowIndex > cachePages[0].HighestIndex &&
  145. rowIndex > cachePages[1].HighestIndex)
  146. {
  147. int offsetFromPage0 = rowIndex - cachePages[0].HighestIndex;
  148. int offsetFromPage1 = rowIndex - cachePages[1].HighestIndex;
  149. if (offsetFromPage0 < offsetFromPage1)
  150. {
  151. return 1;
  152. }
  153. return 0;
  154. }
  155. else
  156. {
  157. int offsetFromPage0 = cachePages[0].LowestIndex - rowIndex;
  158. int offsetFromPage1 = cachePages[1].LowestIndex - rowIndex;
  159. if (offsetFromPage0 < offsetFromPage1)
  160. {
  161. return 1;
  162. }
  163. return 0;
  164. }
  165. }
  166. /// <summary>
  167. /// Returns a value indicating whether the given row index is contained
  168. /// in the given DataPage.
  169. /// </summary>
  170. /// <param name="pageNumber"></param>
  171. /// <param name="rowIndex"></param>
  172. /// <returns></returns>
  173. private bool IsRowCachedInPage(int pageNumber, int rowIndex)
  174. {
  175. return rowIndex <= cachePages[pageNumber].HighestIndex &&
  176. rowIndex >= cachePages[pageNumber].LowestIndex;
  177. }
  178. }
  179. #endregion
  180. #region IDataPageRetriever Interface
  181. public interface IDataPageRetriever
  182. {
  183. DataTable SupplyPageOfData(int lowerPageBoundary, int rowsPerPage);
  184. }
  185. #endregion
  186. #region DataRetriever Class
  187. public class DataRetriever : IDataPageRetriever
  188. {
  189. private string tableName;
  190. private SqlCommand command;
  191. public DataRetriever(string connectionString, string tableName)
  192. {
  193. SqlConnection connection = new SqlConnection(connectionString);
  194. connection.Open();
  195. command = connection.CreateCommand();
  196. this.tableName = tableName;
  197. }
  198. private int rowCountValue = -1;
  199. public int RowCount
  200. {
  201. get
  202. {
  203. // Return the existing value if it has already been determined.
  204. if (rowCountValue != -1)
  205. {
  206. return rowCountValue;
  207. }
  208. // Retrieve the row count from the database.
  209. command.CommandText = "SELECT COUNT(*) FROM " + tableName;
  210. rowCountValue = (int)command.ExecuteScalar();
  211. return rowCountValue;
  212. }
  213. }
  214. private DataColumnCollection columnsValue;
  215. public DataColumnCollection Columns
  216. {
  217. get
  218. {
  219. // Return the existing value if it has already been determined.
  220. if (columnsValue != null)
  221. {
  222. return columnsValue;
  223. }
  224. // Retrieve the column information from the database.
  225. command.CommandText = "SELECT * FROM " + tableName;
  226. SqlDataAdapter adapter = new SqlDataAdapter();
  227. adapter.SelectCommand = command;
  228. DataTable table = new DataTable();
  229. table.Locale = System.Globalization.CultureInfo.InvariantCulture;
  230. adapter.FillSchema(table, SchemaType.Source);
  231. columnsValue = table.Columns;
  232. return columnsValue;
  233. }
  234. }
  235. private string commaSeparatedListOfColumnNamesValue = null;
  236. private string CommaSeparatedListOfColumnNames
  237. {
  238. get
  239. {
  240. // Return the existing value if it has already been determined.
  241. if (commaSeparatedListOfColumnNamesValue != null)
  242. {
  243. return commaSeparatedListOfColumnNamesValue;
  244. }
  245. // Store a list of column names for use in the
  246. // SupplyPageOfData method.
  247. System.Text.StringBuilder commaSeparatedColumnNames =
  248. new System.Text.StringBuilder();
  249. bool firstColumn = true;
  250. foreach (DataColumn column in Columns)
  251. {
  252. if (!firstColumn)
  253. {
  254. commaSeparatedColumnNames.Append(", ");
  255. }
  256. commaSeparatedColumnNames.Append(column.ColumnName);
  257. firstColumn = false;
  258. }
  259. commaSeparatedListOfColumnNamesValue =
  260. commaSeparatedColumnNames.ToString();
  261. return commaSeparatedListOfColumnNamesValue;
  262. }
  263. }
  264. // Declare variables to be reused by the SupplyPageOfData method.
  265. private string columnToSortBy;
  266. private SqlDataAdapter adapter = new SqlDataAdapter();
  267. public DataTable SupplyPageOfData(int lowerPageBoundary, int rowsPerPage)
  268. {
  269. // Store the name of the ID column. This column must contain unique
  270. // values so the SQL below will work properly.
  271. if (columnToSortBy == null)
  272. {
  273. columnToSortBy = this.Columns[0].ColumnName;
  274. }
  275. if (!this.Columns[columnToSortBy].Unique)
  276. {
  277. throw new InvalidOperationException(String.Format(
  278. "Column {0} must contain unique values.", columnToSortBy));
  279. }
  280. // Retrieve the specified number of rows from the database, starting
  281. // with the row specified by the lowerPageBoundary parameter.
  282. command.CommandText = "Select Top " + rowsPerPage + " " +
  283. CommaSeparatedListOfColumnNames + " From " + tableName +
  284. " WHERE " + columnToSortBy + " NOT IN (SELECT TOP " +
  285. lowerPageBoundary + " " + columnToSortBy + " From " +
  286. tableName + " Order By " + columnToSortBy +
  287. ") Order By " + columnToSortBy;
  288. adapter.SelectCommand = command;
  289. DataTable table = new DataTable();
  290. table.Locale = System.Globalization.CultureInfo.InvariantCulture;
  291. adapter.Fill(table);
  292. return table;
  293. }
  294. }
  295. #endregion
  296. }