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

/HOL.md

https://github.com/dk123456/HOL-BuildingAppsWithCaching
Markdown | 1070 lines | 813 code | 257 blank | 0 comment | 0 complexity | 888ebab1783c6fbc387ca3c0f5790fb4 MD5 | raw file
  1. <a name="HOLTop" />
  2. # Building Windows Azure Cloud Services with Cache Service #
  3. ---
  4. <a name="Overview" />
  5. ## Overview ##
  6. Windows Azure Cache Service provides a distributed, cost-effective in-memory cache for your Cloud Services. With Cache Service enabled on your Cloud Services roles, you can utilize spare memory on your service hosts as high performance cache to improve response time and system throughput. And because the cache hosts are collocated with your Cloud Service roles, you get optimal access time by avoiding external service calls. In this lab, you will learn how easy it is to enable Cache Service on your Cloud Services roles, and how to use Cache Service to provide high performance in-memory caching to your Cloud Services.
  7. <a name="Objectives" />
  8. ### Objectives ###
  9. In this hands-on lab, you will learn how to:
  10. - Easily and quickly enable Cache service.
  11. - Use Cache Service for your Asp.Net session state.
  12. - Cache reference data from Windows Azure SQL Database in Cache Service.
  13. - Create a reusable and extensible caching layer for your Cloud Services.
  14. During this lab, you will explore how to use these features in a simple Asp.Net MVC4 application.
  15. <a name="Prerequisites" />
  16. ### Prerequisites ###
  17. The following is required to complete this hands-on lab:
  18. - [Microsoft Visual Studio 2012 Express for Web][1] or higher
  19. - [Windows Azure Tools for Microsoft Visual Studio 1.8][2]
  20. - A Windows Azure subscription - [sign up for a free trial](http://aka.ms/WATK-FreeTrial)
  21. [1]: http://www.microsoft.com/visualstudio/
  22. [2]: http://www.windowsazure.com/en-us/develop/downloads/
  23. [3]: http://aka.ms/WATK-FreeTrial
  24. >**Note:** This lab was designed for Windows 8.
  25. <a name="Setup" />
  26. ### Setup ###
  27. In order to run the exercises in this hands-on lab you need to set up your environment first.
  28. 1. Open a Windows Explorer window and browse to the labs **Source** folder.
  29. 1. Right-click on **Setup.cmd** and select Run as Administrator to launch the setup process that will configure your environment and install the Visual Studio code snippets for this lab.
  30. 1. If the User Account Control dialog is shown, confirm the action to proceed.
  31. >**Note:** Make sure you have checked all the dependencies for this lab before running the setup.
  32. >This lab requires a Windows Azure SQL Database to start. To build the Northwind2 database automatically, the **Setup.cmd** file will prompt to you with your Windows Azure SQL Database account information. Remember to update the NorthwingEntities connection string in the applications configuration file to point to your database for each solution.
  33. >Remember to configure the firewall setting your Windows Azure SQL Database account to allow you to specify a list of IP addresses that can access your Windows Azure SQL Database Server. The firewall will deny all connections by default, so **be sure to configure your allow list** so you can connect to the database. Changes to your firewall settings can take a few moments to become effective. For additional information on how to prepare your Windows Azure SQL Database account, refer to the exercise 1 of the Introduction to Windows Azure SQL Database lab in the training kit.
  34. >![SQL database setup](Images/sql-database-setup.png?raw=true "Windows Azure SQL Database setup")
  35. >_Windows Azure SQL Database setup_
  36. <a name="CodeSnippets" />
  37. ### Using the Code Snippets ###
  38. Throughout the lab document, you will be instructed to insert code blocks. For your convenience, most of that code is provided as Visual Studio Code Snippets, which you can use from within Visual Studio 2012 to avoid having to add it manually.
  39. >**Note**: Each exercise is accompanied by a starting solution located in the Begin folder of the exercise that allows you to follow each exercise independently of the others. Please be aware that the code snippets that are added during an exercise are missing from these starting solutions and that they will not necessarily work until you complete the exercise. Inside the source code for an exercise, you will also find an End folder containing a Visual Studio solution with the code that results from completing the steps in the corresponding exercise. You can use these solutions as guidance if you need additional help as you work through this hands-on lab.
  40. ---
  41. <a name="Exercises" />
  42. ## Exercises ##
  43. This hands-on lab includes the following exercises:
  44. 1. [Enable Cache service for Session State](#Exercise1)
  45. 1. [Caching Data with Cache service](#Exercise2)
  46. 1. [Creating a Reusable and Extensible Caching Layer](#Exercise3)
  47. Estimated time to complete this lab: **60 minutes**.
  48. >**Note:** When you first start Visual Studio, you must select one of the predefined settings collections. Every predefined collection is designed to match a particular development style and determines window layouts, editor behavior, IntelliSense code snippets, and dialog box options. The procedures in this lab describe the actions necessary to accomplish a given task in Visual Studio when using the **General Development Settings** collection. If you choose a different settings collection for your development environment, there may be differences in these procedures that you need to take into account.
  49. <a name="Exercise1" />
  50. ### Exercise 1: Enable Cache service for Session State ###
  51. In this exercise, you will explore the use of the session state provider for Cache service as the mechanism for out-of-process storage of session state data. For this purpose, you will use the Azure Store-a sample shopping cart application implemented with Asp.Net MVC4. You will run this application in the compute emulator and then modify it to take advantage of the Windows Azure Cache service as the back-end store for the Asp.Net session state. You will start with a begin solution and explore the sample using the default Asp.Net in-proc session state provider. Next, you will add references to the Cache assemblies and configure the session state provider to store the contents of the shopping cart in the distributed cache cluster provided by Cache service.
  52. <a name="Ex1Task1" />
  53. #### Task 1 Running the Azure Store Sample Site in the Compute Emulator ####
  54. In this task, you will run the Azure Store application in the compute emulator using the default session state provider; you will change that provider to take advantage of the Windows Azure Cache service later on.
  55. 1. Start **Microsoft Visual Studio 2012 Express for Web** as administrator.
  56. 1. Open the **Begin** solution located at **Source\\Ex1-CacheSessionState\\Begin**.
  57. >**Important:** Before you execute the solution, make sure that the start-up project is set. For MVC projects, the start page must be left blank.
  58. >To set the start-up project, in **Solution Explorer**, right-click the **CloudShop.Azure** project and select **Set as StartUp Project**.
  59. >To set the start page, in **Solution Explorer**, right-click the **CloudShop** project and select **Properties**. In the **Properties** window, select the **Web** tab and in the **Start Action**, select **Specific Page**. Leave the value of this field blank.
  60. 1. In the **Web.config** file, update the _NorthwindEntities_ connection string to point to your database. Replace **[YOUR-SQL-DATABASE-SERVER-ADDRESS]**, **[SQL-DATABASE-USERNAME]**, and **[SQL-DATABASE-PASSWORD]** in the connectionStrings section with the Windows Azure SQL Database server name, Administrator Username and Administrator password that you registered at the portal and used for creating the database during setup.
  61. >**Note:** Make sure that you follow the instructions of the setup section to create a copy of the Northwind2 database in your own Windows Azure SQL Database account and configure your Windows Azure SQL Database firewall settings.
  62. 1. Press **CTRL** + **F5** to build and run the application withouth debugging in the compute emulator.
  63. >**Note:** Make sure that you run the application without debugging. With debugging mode you won't be able to recycle the web role
  64. 1. Explore the main page of the application, the **Products** page, which displays a list of products obtained from a Windows Azure SQL Database.
  65. ![Azure Store products page](Images/azure-store-products-page.png?raw=true "Azure Store products page")
  66. _Azure Store products page_
  67. 1. Select a product from the list and click **Add item to cart**. You may repeat the process to store additional items in the shopping cart.
  68. 1. Click the **Checkout** link to view the contents of the cart. Verify that the items you selected appear on the list. These items are stored in the current session.
  69. ![Checkout page showing the contents of the shopping cart](Images/checkout-page-showing-the-contents-of-the-sho.png?raw=true "Checkout page showing the contents of the shopping cart")
  70. _Checkout page showing the contents of the shopping cart_
  71. 1. Navigate back to **Products** page.
  72. 1. Click on **Recycle** link. This link forces the web role to be recycled. Once you click on the link, the Products page will turn blank.
  73. 1. In the **Compute Emulator**, observe how the web role is recycled by the emulator:
  74. ![Suspending the service role instance](Images/suspending-the-service-role-instance.png?raw=true "Suspending the service role instance")
  75. _Web role recycled_
  76. 1. Got back to browser, remove */Home/Recylce* from address bar, and then press Enter to reload the site. The **Products** page should come back normal after a short delay.
  77. 1. Navigate to **Checkout** page. Notice that the order now appears empty.
  78. >**Note:** The application is currently using in-proc session state, which maintains the session state in-memory. When you stop the service instance, it discards all session state including the contents of the shopping cart. In the following task, you will configure the application to store session state using Windows Azure Caching as the storage mechanism, which allows the application to maintain the session state in the presence of restarts and across multiple role instances hosting the application.
  79. 1. Close the browser window to stop the application.
  80. <a name="Ex1Task2" />
  81. #### Task 2 Adding a dedicated caching role ####
  82. In this task, you will add a new worker role that serves as a dedicated cache host. All other web roles and worker roles in the Cloud Service will be able to access the Cache service hosted by this role. You can set up multiple such dedicated work roles within your Cloud Service. In addition, you can also enable Cache service on any of the existing roles and allocate certain percentage of virtual machine memory to be used as cache.
  83. 1. In solution explorer, expand **CloudShop.Azure** node, and then right-click on **Roles**. Then, select **Add**->**New Worker Role Project...***.
  84. 2. In **Add New Role Project** dialog, select **Cache Worker Role** template. Name the role as **CacheWorkerRole**, and then click **Add**.
  85. >**Note:** All Cache hosts in your Cloud Service share their runtime states via a Windows Azure Blog Storage. By default, a cache work role is configured to use development storage. You can change this setting in **Caching** tab on the role property page.
  86. <a name="Ex1Task3" />
  87. #### Task 3 Configuring Session State Using Windows Azure Cache service ####
  88. In this task, you will change the Session State provider to take advantage of the Windows Azure Cache as the storage mechanism. This requires adding the appropriate assemblies to the **CloudShop** project and then updating the corresponding configuration in the **Web.config** file.
  89. 1. In Visual Studio 2012 Express for Web, open **Package manager Console** from **Tools**->**Library package Manager**->**Package Manager Console** menu.
  90. 1. Make sure that **CloudShop** is selected in the **Default project** drop-down list. Issue the following command to install the Nuget package for Cache service:
  91. ````PowerShell
  92. Install-package Microsoft.WindowsAzure.Caching
  93. ````
  94. 1. Open the **Web.config** file located in the root folder of the **CloudShop** project.
  95. 1. Change **[cache cluster role name]** to **CacheWorkerRole**.
  96. <!--mark: 4-->
  97. ```` XML
  98. <dataCacheClients>
  99. <tracing sinkType="DiagnosticSink" traceLevel="Error" />
  100. <dataCacheClient name="default">
  101. <autoDiscover isEnabled="true" identifier="CacheWorkerRole" />
  102. <!--<localCache isEnabled="true" sync="TimeoutBased" objectCount="100000" ttlValue="300" />-->
  103. </dataCacheClient>
  104. </dataCacheClients>
  105. ...
  106. ````
  107. 1. Add a new session state provider configuration under System.Web tag:
  108. ````XML
  109. <system.Web>
  110. ...
  111. <sessionState mode="Custom" customProvider="NamedCacheBProvider">
  112. <providers>
  113. <add cacheName="default" name="NamedCacheBProvider"
  114. dataCacheClientName="default" applicationName="MyApp"
  115. type="Microsoft.Web.DistributedCache.DistributedCacheSessionStateStoreProvider, Microsoft.Web.DistributedCache" />
  116. </providers>
  117. </sessionState>
  118. ...
  119. </system.web>
  120. ````
  121. 1. Press **CTRL + S** to save your changes to the **Web.config** file.
  122. <a name="Ex1Task4"></a>
  123. #### Task 4 Verification ####
  124. 1. Press **Ctrl + F5** to build and run the application. Wait for the browser to launch and show the **Products** page.
  125. 1. Select one product from the list and click Add item to cart. Repeat the process to store additional items in the cart.
  126. 1. Click the **Checkout** link to view the contents of the shopping cart. Verify that the items you selected appear on the list.
  127. 1. Navigate back to **Products** page and click on "Recycle" link.
  128. 1. Observe the web role getting recycled in **Show Compute Emulator UI**.
  129. 1. Go back to browser, remove */Home/Recycle* from address, and then press Enter to reload the site.
  130. 1. **Products** page should load correctly. Navigate to **Checkout** page. Notice that the order is intact. This confirms that with the Windows Azure Caching provider, the session state is stored outside the role instance and can persist through application restarts.
  131. > **Note:** You should infer from the verification that for an application hosted in multiple servers or Windows Azure role instances where a load balancer distributes requests to the application, clients would continue to have access to their session data regardless of which instance responds to the request.
  132. 1. Close the browser window to stop the application.
  133. <a name="Exercise2" />
  134. ### Exercise 2: Caching Data with Windows Azure Caching ###
  135. This exercise will show you how to use the Windows Azure Caching to cache results from queries to Windows Azure SQL Database. You will continue with a solution based on the one used for the previous exercise the only difference is in the home page, which has been updated to show the elapsed time to retrieve the list of products in the catalog, and now has a link to enable or disable the use of the cache.
  136. During the exercise, you will update the data access code with a trivial implementation of caching. It uses the canonical pattern, in which the code checks the cache first to retrieve the results of a query and, if there is no data available, executes the query against the database to cache the results.
  137. <a name="Ex2Task1" />
  138. #### Task 1 Caching Data Retrieved from the SQL Reporting ####
  139. To make use of Windows Azure Caching, you first need to create a **DataCacheFactory** object. This object determines the cache cluster connection information, which is set programmatically or by reading settings from the configuration file. Typically, you create an instance of the factory class and use it for the lifetime of the application. To store data in the cache, you request a **DataCache** instance from the **DataCacheFactory** and then use it to add or retrieve items from the cache.
  140. In this task, you update the data access code to cache the result of queries to Windows Azure SQL Database using the Windows Azure Caching.
  141. 1. Start **Microsoft Visual Studio 2012 Express for Web** as an administrator.
  142. 1. Open the **Begin** solution located at **Source\\Ex2-CachingData\\Begin**.
  143. >**Important:** Before you execute the solution, make sure that the start-up project is set. For MVC projects, the start page must be left blank.
  144. > To set the startup project, in **Solution Explorer**, right-click the **CloudShop.Azure** project and then select **Set as StartUp Project**.
  145. > To set the start page, in **Solution Explorer**, right-click the **CloudShop** project and select **Properties**. In the **Properties** window, select the **Web** tab and in the **Start Action**, select **Specific Page**. Leave the value of this field blank.
  146. 1. In the **Web.config** file, update the _NorthwindEntities_ connection string to point to your database. Replace **[YOUR-SQL-DATABASE-SERVER-ADDRESS]**, **[SQL-DATABASE-USERNAME]**, and **[SQL-DATABASE-PASSWORD]** with the Windows Azure SQL Database server name, Administrator Username and Administrator password that you registered at the portal and used for creating the database during setup.
  147. > **Note:** Make sure that you follow the instructions of the setup section to create a copy of the Northwind2 database in your own Windows Azure SQL Database account and configure your Windows Azure SQL Database firewall settings.
  148. 1. Open the **ProductsRepository.cs** file in the **Services** folder of the **CloudShop** project.
  149. 1. Add a namespace directive for **Microsoft.ApplicationServer.Caching**.
  150. <!--mark: 5-->
  151. ````C#
  152. using System;
  153. using System.Collections.Generic;
  154. using System.Linq;
  155. using CloudShop.Models;
  156. using Microsoft.ApplicationServer.Caching;
  157. ...
  158. ````
  159. 1. In the **ProductsRepository** class, add the following highlighted code to define a constructor and declare a static member variable for a **DataCacheFactory** object instance, in addition to a boolean instance variable to control the use of the cache.
  160. (Code Snippet - _BuildingAppsWithCachingService-Ex2-ProductsRepository constructor-CS_)
  161. <!--mark: 3-9-->
  162. ````C#
  163. public class ProductsRepository : IProductRepository
  164. {
  165. private static DataCacheFactory cacheFactory = new DataCacheFactory();
  166. private bool enableCache = false;
  167. public ProductsRepository(bool enableCache)
  168. {
  169. this.enableCache = enableCache;
  170. }
  171. public List<string> GetProducts()
  172. {
  173. ...
  174. }
  175. }
  176. ````
  177. > **Note:** The **DataCacheFactory** member is declared as static and is used throughout the lifetime of the application.
  178. 1. Locate the **GetProducts** method and insert the following (highlighted) code immediately after the line that declares the **products** local variable.
  179. (Code Snippet - _BuildingAppsWithCachingService-Ex2-GetProducts read cache-CS_)
  180. <!--mark: 8-30-->
  181. ````C#
  182. public class ProductsRepository : IProductRepository
  183. {
  184. ...
  185. public List<string> GetProducts()
  186. {
  187. List<string> products = null;
  188. DataCache dataCache = null;
  189. if (this.enableCache)
  190. {
  191. try
  192. {
  193. dataCache = cacheFactory.GetDefaultCache();
  194. products = dataCache.Get("products") as List<string>;
  195. if (products != null)
  196. {
  197. products[0] = "(from cache)";
  198. return products;
  199. }
  200. }
  201. catch (DataCacheException ex)
  202. {
  203. if (ex.ErrorCode != DataCacheErrorCode.RetryLater)
  204. {
  205. throw;
  206. }
  207. // ignore temporary failures
  208. }
  209. }
  210. NorthwindEntities context = new NorthwindEntities();
  211. try
  212. {
  213. var query = from product in context.Products
  214. select product.ProductName;
  215. products = query.ToList();
  216. }
  217. finally
  218. {
  219. if (context != null)
  220. {
  221. context.Dispose();
  222. }
  223. }
  224. return products;
  225. }
  226. }
  227. ````
  228. >**Note:** The inserted code uses the **DataCacheFactory** object to return an instance of the default cache object and then attempts to retrieve an item from this cache using a key with the value "_products_". If the cache contains an object with the requested key, it sets the text of the first entry to indicate that the list was retrieved from the cache and then returns it. The code treats temporary failures from the Windows Azure Caching service as a cache miss so that it can retrieve the item from its data source instead.
  229. 1. Next, add the following (highlighted) code block to the **GetProducts** method, immediately before the line that returns the **products** list at the end of the method.
  230. (Code Snippet - _BuildingAppsWithCachingService-Ex2-GetProducts write cache-CS_)
  231. <!--mark: 30-35-->
  232. ````C#
  233. public class ProductsRepository : IProductRepository
  234. {
  235. ...
  236. public List<string> GetProducts()
  237. {
  238. List<string> products = null;
  239. DataCache dataCache = null;
  240. if (this.enableCache)
  241. {
  242. ...
  243. }
  244. NorthwindEntities context = new NorthwindEntities();
  245. try
  246. {
  247. var query = from product in context.Products
  248. select product.ProductName;
  249. products = query.ToList();
  250. }
  251. finally
  252. {
  253. if (context != null)
  254. {
  255. context.Dispose();
  256. }
  257. }
  258. products.Insert(0, "(from data source)");
  259. if (this.enableCache && dataCache != null)
  260. {
  261. dataCache.Add("products", products, TimeSpan.FromSeconds(30));
  262. }
  263. return products;
  264. }
  265. }
  266. ````
  267. >**Note:** The inserted code stores the result of the query against the data source into the cache and sets its expiration policy to purge the item from the cache after 30 seconds.
  268. <a name="Ex2Task2" />
  269. #### Task 2 Measuring the Data Access Latency ####
  270. In this task, you will update the application to allow control of the use of the cache from the UI and to display the time required to retrieve catalog data, allowing you to compare the latency of retrieving data from the cache against the time required to access the data source.
  271. 1. Open the **HomeController.cs** file in the **Controllers** folder and add the **System.Diagnostics** using directive at the top of the file.
  272. <!-- mark:1 -->
  273. ````C#
  274. using System.Diagnostics;
  275. ````
  276. 1. Find the **Index** action, locate the lines that instantiate a new **ProductsRepository** and call its **GetProducts** method, and replace them with the highlighted code, as shown below.
  277. (Code Snippet - _BuildingAppsWithCachingService-Ex2-GetProducts latency-CS_)
  278. <!--mark: 9-17; strike:6-8-->
  279. ````C#
  280. public class HomeController : Controller
  281. {
  282. ...
  283. public ActionResult Index()
  284. {
  285. Services.IProductRepository productRepository =
  286. new Services.ProductsRepository();
  287. var products = productRepository.GetProducts();
  288. bool enableCache = (bool)this.Session["EnableCache"];
  289. // retrieve product catalog from repository and measure the elapsed time
  290. Services.IProductRepository productRepository =
  291. new Services.ProductsRepository(enableCache);
  292. Stopwatch stopWatch = new Stopwatch();
  293. stopWatch.Start();
  294. var products = productRepository.GetProducts();
  295. stopWatch.Stop();
  296. // add all products currently not in session
  297. var itemsInSession = this.Session["Cart"] as List<string> ?? new List<string>();
  298. var filteredProducts = products.Where(item => !itemsInSession.Contains(item));
  299. IndexViewModel model = new IndexViewModel()
  300. {
  301. Products = filteredProducts
  302. };
  303. return View(model);
  304. }
  305. ...
  306. }
  307. ````
  308. 1. In the same method, locate the code that creates a new **IndexViewModel** instance and replace its initialization with the following (highlighted) code block.
  309. (Code Snippet - _BuildingAppsWithCachingService-Ex2-IndexViewModel initialization-CS_)
  310. <!--mark: 22-25-->
  311. ````C#
  312. public class HomeController : Controller
  313. {
  314. ...
  315. public ActionResult Index()
  316. {
  317. bool enableCache = (bool)this.Session["EnableCache"];
  318. // retrieve product catalog from repository and measure the elapsed time
  319. Services.IProductRepository productRepository =
  320. new Services.ProductsRepository(enableCache);
  321. Stopwatch stopWatch = new Stopwatch();
  322. stopWatch.Start();
  323. var products = productRepository.GetProducts();
  324. stopWatch.Stop();
  325. // add all products currently not in session
  326. var itemsInSession = this.Session["Cart"] as List<string> ?? new List<string>();
  327. var filteredProducts = products.Where(item => !itemsInSession.Contains(item));
  328. IndexViewModel model = new IndexViewModel()
  329. {
  330. Products = filteredProducts,
  331. ElapsedTime = stopWatch.ElapsedMilliseconds,
  332. IsCacheEnabled = enableCache,
  333. ObjectId = products.GetHashCode().ToString()
  334. };
  335. return View(model);
  336. }
  337. ...
  338. }
  339. ````
  340. >**Note:** The elements added to the view model provide the time taken to load the product catalog from the repository, a flag to indicate whether the cache is enabled, and an identifier for the catalog object returned by the call to **GetProducts**. The view displays the object ID to allow you to determine whether the instance returned by the call to the repository has changed. This feature will be used later in the exercise, when you enable the local cache.
  341. 1. Add a new action method to the **HomeController** to enable or disable the cache from the UI of the application.
  342. (Code Snippet - _BuildingAppsWithCachingService-Ex2-EnableCache method-CS_)
  343. <!--mark: 4-8-->
  344. ````C#
  345. public class HomeController : Controller
  346. {
  347. ...
  348. public ActionResult EnableCache(bool enabled)
  349. {
  350. this.Session["EnableCache"] = enabled;
  351. return RedirectToAction("Index");
  352. }
  353. }
  354. ````
  355. 1. Press **F5** to build and launch the application in the compute emulator.
  356. >**Note:** Ideally, you should test the code in Windows Azure. When you execute the application in the compute emulator, consider that accessing the Windows Azure SQL Database data source and the Windows Azure Caching require executing requests to resources located outside the bounds of your own network. Depending on your geographic location, both requests may exhibit a relatively high latency, which may overshadow the difference between the cached and non-cached scenarios. Once you deploy the application to Windows Azure, it is co-located in the same data center as the Windows Azure Caching service in Windows Azure SQL Database. As the latency is much lower, the results should be more significant.
  357. 1. When you start the application, the cache is initially disabled. Refresh the page and notice the elapsed time displayed at the bottom of the page that indicates the time required to retrieve the product catalog. Note that the first item in the list indicates that the application retrieved the product catalog from the data source.
  358. >**Note:** You may need to refresh the page several times to obtain a stable reading. The value shown for the first request may be greater because ASP.NET needs to compile the page.
  359. ![Running the application without the cache](Images/running-the-application-without-the-cache.png?raw=true "Running the application without the cache")
  360. _Running the application without the cache_
  361. 1. Observe the **Object ID** indicator shown above the product catalog and notice how it changes every time you refresh the page indicating that the repository returns a different object for each call.
  362. 1. Now, click **Yes** in **Enable Cache** and wait for the page to refresh. Notice that the first item in the list indicates that it was still necessary for the application to retrieve the product catalog from the data source because the information has yet to be cached.
  363. 1. Click **Products**, or refresh the page in the browser. This time, the application retrieves the product data from the Windows Azure Caching and the elapsed time should be lower. Confirm that the first item in the list indicates that the source of the information is the cache.
  364. ![Running the application with the cache enabled](Images/running-the-application-with-the-cache-enable.png?raw=true "Running the application with the cache enabled")
  365. _Running the application with the cache enabled_
  366. 1. Close the browser.
  367. <a name="Ex2Task3"></a>
  368. #### Task 3 Enabling the Local Cache ####
  369. When using Windows Azure Caching, you have the option of using a local cache that allows objects to be cached in-memory at the client, as well as being stored in the cache cluster. In this task, you will enable the local cache and then compare the access time with the remote case.
  370. 1. Open the **ProductsRepository.cs** file in the **Services** folder of the **CloudShop** project.
  371. >**Note:** Make sure your solution is not running before editing the files.
  372. 1. In the **ProductsRepository** class, replace the current member fields and the constructor with the following code, to add the logic for managing the localCache configuration.
  373. (Code Snippet - _BuildingAppsWithCachingService-Ex2-ProductsRepository with local cache-CS_)
  374. <!--mark: 2-34-->
  375. ````C#
  376. ...
  377. private static DataCacheFactory cacheFactory;
  378. private static DataCacheFactoryConfiguration factoryConfig;
  379. private bool enableCache = false;
  380. private bool enableLocalCache = false;
  381. public ProductsRepository(bool enableCache, bool enableLocalCache)
  382. {
  383. this.enableCache = enableCache;
  384. this.enableLocalCache = enableLocalCache;
  385. if (enableCache)
  386. {
  387. if (enableLocalCache && (factoryConfig == null || !factoryConfig.LocalCacheProperties.IsEnabled))
  388. {
  389. TimeSpan localTimeout = new TimeSpan(0, 0, 30);
  390. DataCacheLocalCacheProperties localCacheConfig = new DataCacheLocalCacheProperties(10000, localTimeout, DataCacheLocalCacheInvalidationPolicy.TimeoutBased);
  391. factoryConfig = new DataCacheFactoryConfiguration();
  392. factoryConfig.LocalCacheProperties = localCacheConfig;
  393. cacheFactory = new DataCacheFactory(factoryConfig);
  394. }
  395. else if (!enableLocalCache && (factoryConfig == null || factoryConfig.LocalCacheProperties.IsEnabled))
  396. {
  397. cacheFactory = null;
  398. }
  399. }
  400. if (cacheFactory == null)
  401. {
  402. factoryConfig = new DataCacheFactoryConfiguration();
  403. cacheFactory = new DataCacheFactory(factoryConfig);
  404. }
  405. }
  406. ...
  407. ````
  408. 1. Open the **HomeController.cs** file in the **Controllers** folder and find the **Index** action. Locate the line that instantiates a new **ProductsRepository** and Replace those lines with the following highlighted code:
  409. (Code Snippet - _BuildingAppsWithCachingService-Ex2-GetProducts LocalCache-CS_)
  410. <!--mark: 7-10; strike: 11-13-->
  411. ````C#
  412. public class HomeController : Controller
  413. {
  414. ...
  415. public ActionResult Index()
  416. {
  417. bool enableCache = (bool)this.Session["EnableCache"];
  418. bool enableLocalCache = (bool)this.Session["EnableLocalCache"];
  419. // retrieve product catalog from repository and measure the elapsed time
  420. Services.IProductRepository productRepository = new Services.ProductsRepository(enableCache, enableLocalCache);
  421. // retrieve product catalog from repository and measure the elapsed time
  422. Services.IProductRepository productRepository =
  423. new Services.ProductsRepository(enableCache);
  424. Stopwatch stopwatch = new Stopwatch();
  425. stopWatch.Start();
  426. var products = productRepository.GetProducts();
  427. ...
  428. }
  429. ````
  430. 1. In the same method, locate the code that creates a new **IndexViewModel** and add the following highlighted property.
  431. <!--mark: 25-->
  432. ````C#
  433. public class HomeController : Controller
  434. {
  435. ...
  436. public ActionResult Index()
  437. {
  438. bool enableCache = (bool)this.Session["EnableCache"];
  439. bool enableLocalCache = (bool)this.Session["EnableLocalCache"];
  440. // retrieve product catalog from repository and measure the elapsed time
  441. Services.IProductRepository productRepository =
  442. new Services.ProductsRepository(enableCache, enableLocalCache);
  443. Stopwatch stopwatch = new Stopwatch();
  444. stopWatch.Start();
  445. var products = productRepository.GetProducts();
  446. stopWatch.Stop();
  447. // add all products currently not in session
  448. var itemsInSession = this.Session["Cart"] as List<string> ?? new List<string>();
  449. var filteredProducts = products.Where(item => !itemsInSession.Contains(item));
  450. IndexViewModel model = new IndexViewModel()
  451. {
  452. Products = filteredProducts,
  453. ElapsedTime = stopWatch.ElapsedMilliseconds,
  454. IsCacheEnabled = enableCache,
  455. IsLocalCacheEnabled = enableLocalCache,
  456. ObjectId = products.GetHashCode().ToString()
  457. };
  458. return View(model);
  459. }
  460. }
  461. ````
  462. 1. Add a new action method to the **HomeController** to enable or disable the local cache from the UI of the application.
  463. (Code Snippet - _BuildingAppsWithCachingService-Ex2-EnableLocalCache method-CS_)
  464. <!--mark: 4-8-->
  465. ````C#
  466. public class HomeController : Controller
  467. {
  468. ...
  469. public ActionResult EnableLocalCache(bool enabled)
  470. {
  471. this.Session["EnableLocalCache"] = enabled;
  472. return RedirectToAction("Index");
  473. }
  474. }
  475. ````
  476. 1. Open **Index.cshtml** file in the **Views\Home** folder and add the following highlighted code above the **elapsedTime** div.
  477. (Code Snippet - _BuildingAppsWithCachingService-Ex2-EnableLocalCache Option-HTML_)
  478. <!--mark: 12-23-->
  479. ````HTML
  480. <fieldset>
  481. <legend>Cache settings for product data</legend>Enable Cache:
  482. @if (Model.IsCacheEnabled)
  483. {
  484. <span>Yes |</span><span>@Html.ActionLink("No", "EnableCache", new { enabled = false })</span>
  485. }
  486. else
  487. {
  488. <span>@Html.ActionLink("Yes", "EnableCache", new { enabled = true })</span><span> | No</span>
  489. }
  490. <br />
  491. @if(Model.IsCacheEnabled)
  492. {
  493. <span>Use Local Cache:</span>
  494. if (Model.IsLocalCacheEnabled)
  495. {
  496. <span>Yes |</span><span>@Html.ActionLink("No", "EnableLocalCache", new { enabled = false })</span>
  497. }
  498. else
  499. {
  500. <span>@Html.ActionLink("Yes", "EnableLocalCache", new { enabled = true })</span><span> | No</span>
  501. }
  502. }
  503. <div id="elapsedTime">Elapsed time: @Model.ElapsedTime.ToString() milliseconds.</div>
  504. </fieldset>
  505. ````
  506. 1. Press **F5** to build and launch the application in the compute emulator.
  507. 1. When you start the application, the cache option is initially disabled and the local cache option is hidden (it will be shown once you enable cache). Enable cache and then the local cache.
  508. 1. Refresh the page several times until the elapsed time stabilizes. Notice that the reading is now significantly lower, possibly under a millisecond, showing that the application now retrieves the data from the local in-memory cache.
  509. ![Using the local cache](Images/using-the-local-cache.png?raw=true "Using the local cache")
  510. _Using the local cache_
  511. 1. Observe that, each time you refresh the page, the **Object ID** shown above the product catalog remains constant indicating that the repository now returns the same object each time.
  512. >**Note:** This is an important aspect to consider. Previously, with the local cache disabled, changing an object retrieved from the cache had no effect on the cached data and subsequent fetches always returned a fresh copy. Once you enable the local cache, it stores references to in-memory objects and any changes to the object directly affect the cached data.
  513. You should be aware of this when using the cache in your own applications and consider that, after changing a cached object and later retrieving the same object from the cache, it may or may not include these changes depending on whether it is returned by the local or remote cache.
  514. 1. Wait for at least 30 seconds and then refresh the page one more time. Notice that the elapsed time is back to its original value and that the object ID has changed, showing that the cached item has expired and been purged from the cache due to the expiration policy set on the object when it was stored.
  515. <a name="Exercise3" />
  516. ### Exercise 3: Creating a Reusable and Extensible Caching Layer ###
  517. In the previous exercise, you explored the fundamental aspects of using the Windows Azure Caching by directly updating the methods in the data access class to cache data retrieved from the repository. While this approach can yield significant benefits, it requires you to change each one of your data access methods to enable caching. An alternative approach that does not require changes to your existing data access classes would be advantageous.
  518. In this exercise, you will explore building a caching layer on top of your existing data access classes that will allow you to plug in different caching providers, or even remove them altogether, through simple configuration changes.
  519. To build this layer, you will implement an abstract caching class named **CachedDataSource** that will provide support for storing and removing data in the cache. You will then derive from this class to create a caching equivalent for any data source in your application. The only requirement is that your data source implements a contract to define its data access operations. The caching class encapsulates a caching provider, which you need to provide in its constructor, and provides methods to retrieve and remove data from the cache.
  520. The data retrieval method in the caching class receives a cache key that uniquely identifies a cached item, a delegate that retrieves data from the data source, and a cache expiration policy that determines when to purge the item from the cache. This method implements the classic caching pattern where it first attempts to retrieve an item from the cache and, if it does not find a copy, uses the supplied delegate to retrieve the data from the source and then stores it in the cache.
  521. The implementation of the **CachedDataSource** class is completely reusable, allowing you to use any caching provider that fits your requirements. To specify a caching provider, you supply an [ObjectCache](http://msdn.microsoft.com/en-us/library/system.runtime.caching.objectcache.aspx) instance to its constructor. The **ObjectCache** class, part of the **System.Runtime.Caching** namespace, was introduced in the .NET Framework 4 to make caching available for all applications. This abstract class represents an object cache and provides base methods and properties for accessing an underlying cache provider. The .NET Framework already offers a concrete implementation of this class that provides an in-memory cache, the [MemoryCache](http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.aspx).
  522. To use a given cache service with the **CachedDataSource** derived class, you need to supply an **ObjectCache** implementation specific to the caching provider. A good approach is to create a data source factory that allows you to choose a suitable caching implementation based on your needs. Replacing the caching provider is then simply a matter of changing a setting in the configuration file.
  523. Currently, the Windows Azure Caching does not supply its own **ObjectCache** implementation. Nevertheless, you can create one that provides a wrapper around its services. You will find an example of such an implementation, the **AzureCacheProvider**, in the **BuildingAppsWithCacheService\\Source\\Assets** folder. This class derives from **ObjectCache** to expose the services in the Windows Azure Caching.
  524. To take advantage of this caching implementation in the Azure Store application, you will create a caching counterpart of the **ProductsRepository** class. The application uses this class, which implements an **IProductsRepository** contract with a single **GetProducts** operation, to retrieve catalog information from Windows Azure SQL Database. To create a caching products catalog source, you need to perform the following steps:
  525. - Create a new **CachingProductsReposity** class that inherits from **CachedDataSource**.
  526. - Add a constructor to the new class that receives an **IProductRepository** parameter with an instance of the non-caching data source class as well as an **ObjectCache** parameter with an instance of the caching provider to use.
  527. - Implement each method in the **IProductRepository** interface by calling the **RetrievedCachedData** method in the base class and supplying a delegate that calls the original data source class.
  528. <a name="Ex3Task1" />
  529. #### Task 1 Implementing a Caching Data Source Base Class ####
  530. In this task, you will create the abstract class that you will use as the base class for your caching data source classes. You can take advantage of this general-purpose class in any project that requires a caching layer.
  531. 1. Start **Microsoft Visual Studio 2012 Express for Web** as administrator.
  532. 1. Open the **Begin** solution located at **Source\\Ex3-ReusableCachingImplementation**.
  533. >**Important:** Before you execute the solution, make sure that the start-up project is set. For MVC projects, the start page must be left blank.
  534. > To set the start up project, in **Solution Explorer**, right-click the **CloudShop.Azure** project and then select **Set as StartUp Project**.
  535. > To set the start page, in **Solution Explorer**, right-click the **CloudShop** project and select **Properties**. In the **Properties** window, select the **Web** tab and in the **Start Action**, select **Specific Page**. Leave the value of this field blank.
  536. 1. In the **Web.config** file, update the _NorthwindEntities_ connection string to point to your database. Replace **[YOUR-SQL-DATABASE-SERVER-ADDRESS]**, **[SQL-DATABASE-USERNAME]**, and **[SQL-DATABASE-PASSWORD]** with the Windows Azure SQL Database server name, Administrator Username and Administrator password that you registered at the portal and used for creating the database during setup.
  537. >**Note:** Make sure that you follow the instructions of the setup section to create a copy of the Northwind2 database in your own Windows Azure SQL Database account and configure your Windows Azure SQL Database firewall settings.
  538. 1. Add a reference to the **System.Runtime.Caching** assembly in the **CloudShop** project.
  539. 1. In the **Services** folder of the **CloudShop** project, add a new folder named **Caching**.
  540. 1. Inside the **Caching** folder created in the previous step, add a new class file named **CachedDataSource.cs**.
  541. 1. In the new class file, add a namespace directive for **System.Runtime.Caching**.
  542. <!--mark: 5-->
  543. ````C#
  544. using System;
  545. using System.Collections.Generic;
  546. using System.Linq;
  547. using System.Web;
  548. using System.Runtime.Caching;
  549. ...
  550. ````
  551. 1. Specify an **abstract** modifier for the **CachedDataSource** class.
  552. <!--mark: 1-3-->
  553. ````C#
  554. public abstract class CachedDataSource
  555. {
  556. }
  557. ````
  558. 1. Add the following (highlighted) member fields to the class.
  559. (Code Snippet - _BuildingAppsWithCachingService-Ex3-CachedDataSource member fields-CS_)
  560. <!--mark: 3,4-->
  561. ````C#
  562. public abstract class CachedDataSource
  563. {
  564. private readonly ObjectCache cacheProvider;
  565. private readonly string regionName;
  566. }
  567. ````
  568. 1. Now, define a constructor that receives an object cache and a region name as parameters, as shown (highlighted) below.
  569. (Code Snippet - _BuildingAppsWithCachingService-Ex3-CachedDataSource constructor-CS_)
  570. <!--mark: 4-18-->
  571. ````C#
  572. public abstract class CachedDataSource
  573. {
  574. ...
  575. public CachedDataSource(ObjectCache cacheProvider, string regionName)
  576. {
  577. if (cacheProvider == null)
  578. {
  579. throw new ArgumentNullException("cacheProvider");
  580. }
  581. if (cacheProvider is MemoryCache)
  582. {
  583. regionName = null;
  584. }
  585. this.cacheProvider = cacheProvider;
  586. this.regionName = regionName;
  587. }
  588. }
  589. ````
  590. >**Note:** The **CachedDataSource** constructor receives an ObjectCache ([http://msdn.microsoft.com/en-us/library/system.runtime.caching.objectcache.aspx](http://msdn.microsoft.com/en-us/library/system.runtime.caching.objectcache.aspx)) instance as a parameter, which provides methods and properties for accessing an object cache, as well as a region name. A cache region is a partition in the cache used to organize cache objects.
  591. 1. Next, add the following (highlighted) method to retrieve data from the cache.
  592. (Code Snippet - _BuildingAppsWithCachingService-Ex3-RetrieveCachedData method-CS_)
  593. <!--mark: 4-19-->
  594. ````C#
  595. public abstract class CachedDataSource
  596. {
  597. ...
  598. protected T RetrieveCachedData<T>(string cacheKey, Func<T> fallbackFunction, CacheItemPolicy cachePolicy) where T : class
  599. {
  600. var data = this.cacheProvider.Get(cacheKey, this.regionName) as T;
  601. if (data != null)
  602. {
  603. return data;
  604. }
  605. data = fallbackFunction();
  606. if (data != null)
  607. {
  608. this.cacheProvider.Add(new CacheItem(cacheKey, data, this.regionName), cachePolicy);
  609. }
  610. return data;
  611. }
  612. }
  613. ````
  614. >**Note:** The **RetrieveCachedData** method uses the provided key to retrieve a copy of the requested item from the cache. If the data is available, it returns it; otherwise, it uses the provided fallback delegate to obtain the information from the data source and then caches the result using the supplied cache expiration policy.
  615. 1. Finally, add a method to delete items from the cache.
  616. (Code Snippet - _BuildingAppsWithCachingService-Ex3-RemoveCachedData method-CS_)
  617. <!--mark: 4-7-->
  618. ````C#
  619. public abstract class CachedDataSource
  620. {
  621. ...
  622. protected void RemoveCachedData(string cacheKey)
  623. {
  624. this.cacheProvider.Remove(cacheKey, this.regionName);
  625. }
  626. }
  627. ````
  628. 1. Save the **CachedDataSource.cs** file.
  629. <a name="Ex3Task2" />
  630. #### Task 2 Building a Caching Product Catalog Repository ####
  631. Once you have created an abstract base class for caching data sources, you will now create a concrete implementation that will provide a caching alternative for the **ProductsRepository** class. This task represents the steps you would typically follow when creating a caching layer for your data access code using the **CachedDataSource** class.
  632. 1. Inside the **Services\Caching** folder of the **CloudShop** project, add a new class file named **CachedProductsRepository.cs**.
  633. 1. In the new class file, append a namespace directive for **System.Runtime.Caching** and **CloudShop.Services**.
  634. <!-- mark:5-6 -->
  635. ````C#
  636. using System;
  637. using System.Collections.Generic;
  638. using System.Linq;
  639. using System.Web;
  640. using CloudShop.Services;
  641. using System.Runtime.Caching;
  642. ...
  643. ````
  644. 1. Change the declaration for the **CachedProductsRepository** class to derive from both **CachedDataSource** and **IProductRepository**, as shown (highlighted) below.
  645. <!--mark: 2-->
  646. ````C#
  647. public class CachedProductsRepository
  648. : CachedDataSource, IProductRepository
  649. {
  650. }
  651. ````
  652. >**Note:** The caching data source class derives from **CachedDataSource** to provide the necessary caching behavior, as well as implementing the same contract used by the original data source class.
  653. 1. Add the following code to define a constructor and declare a member field that holds a reference to the underlying data source, as shown (highlighted) below.
  654. (Code Snippet - _BuildingAppsWithCachingService-Ex3-CachedProductsRepository constructor-CS_)
  655. <!--mark: 3-9-->
  656. ````C#
  657. public class CachedProductsRepository : CachedDataSource, IProductRepository
  658. {
  659. private readonly IProductRepository repository;
  660. public CachedProductsRepository(IProductRepository repository, ObjectCache cacheProvider) :
  661. base(cacheProvider, "Products")
  662. {
  663. this.repository = repository;
  664. }
  665. }
  666. ````
  667. >**Note:** The **CachedProductsRepository** constructor initializes its base class using the supplied cache provider and saves a reference to the underlying data source in a member field. The class defines a "_Products_" cache region.
  668. 1. Finally, fulfill the **IProductRepository** contract by implementing the **GetProducts** method, as shown (highlighted) below.
  669. (Code Snippet - _BuildingAppsWithCachingService-Ex3-GetProducts method -CS_)
  670. <!--mark: 4-10-->
  671. ````C#
  672. public class CachedProductsRepository : CachedDataSource, IProductRepository
  673. {
  674. ...
  675. public List<string> GetProducts()
  676. {
  677. return RetrieveCachedData(
  678. "allproducts",
  679. () => this.repository.GetProducts(),
  680. new CacheItemPolicy { AbsoluteExpiration = DateTime.UtcNow.AddMinutes(1) });
  681. }
  682. }
  683. ````
  684. >**Note:** The **GetProducts** method calls **RetrieveCachedData** in the base class, passing in a key that identifies the cached item, in this case "_allproducts_", a fallback delegate in the form of a lambda expression that simply calls the **GetProducts** method in the original data source, and a [CacheItemPolicy](http://msdn.microsoft.com/en-us/library/system.runtime.caching.cacheitempolicy.aspx) to set the expiration of the item to 1 minute.
  685. Because the **IProductRepository** contract is so simple, this is all that is required to provide a caching implementation. Typically, your data sources will have more than one method, but the basic approach should not change, allowing you to implement every method by copying this same pattern.
  686. <a name="Ex3Task3" />
  687. #### Task 3 Creating a Data Source Factory Class ####
  688. In this task, you will create a factory class that can return data source instances. The factory determines the cache provider to use from the application configuration settings and returns a data source suitably configured to use the chosen cache provider.
  689. 1. Add a copy of the **AzureCacheProvider.cs** file located in the **\\Source\\Assets** folder to the **CloudShop** project and place it in its **Services\Caching** folder.
  690. >**Note:** The **AzureCacheProvider** class implements an **ObjectCache** that wraps the services provided by the Windows Azure Cache Service.
  691. 1. Inside the **Services** folder of the **CloudShop** project, add a new class file named **DataSourceFactory.cs**.
  692. 1. In the new class file, insert namespace directives for **System.Configuration**, **System.Runtime.Caching**, **CloudShop.Services** and **CloudShop.Services.Caching**.
  693. (Code Snippet - _BuildingAppsWithCachingService-Ex3-DataSourceFactory namespaces-CS_)
  694. <!--mark: 5-8-->
  695. ````C#
  696. using System;
  697. using System.Collections.Generic;
  698. using System.Linq;
  699. using System.Web;
  700. using System.Configuration;
  701. using System.Runtime.Caching;
  702. using CloudShop.Services;
  703. using CloudShop.Services.Caching;
  704. ````
  705. 1. Now, add the following code to define a type constructor for the **DataSourceFactory** class and declare a static field that holds a reference to the configured cache service provider, as shown (highlighted) below.
  706. (Code Snippet - _BuildingAppsWithCachingService-Ex3-DataSourceFactory class constructor-CS_)
  707. <!--mark: 3-20-->
  708. ````C#
  709. public class DataSourceFactory
  710. {
  711. private static readonly ObjectCache cacheProvider;
  712. static DataSourceFactory()
  713. {
  714. string provider = ConfigurationManager.AppSettings["CacheService.Provider"];
  715. if (provider != null)
  716. {
  717. switch (ConfigurationManager.AppSettings["CacheService.Provider"].ToUpperInvariant())
  718. {
  719. case "AZURE":
  720. cacheProvider = new AzureCacheProvider();
  721. break;
  722. case "INMEMORY":
  723. cacheProvider = MemoryCache.Default;
  724. break;
  725. }
  726. }
  727. }
  728. }
  729. ````
  730. >**Note:** The class constructor reads the _CacheService.Provider_ setting from the configuration and initializes the cache provider for the application based on its value. In this example, two different values for the setting are recognized, one for the Windows Azure Caching and another one for the default in-memory cache provider offered by the .NET Framework 4.
  731. 1. Next, add the following property to return the configured cache service provider.
  732. (Code Snippet - _BuildingAppsWithCachingService-Ex3-CacheProvider property-CS_)
  733. <!--mark: 4-7-->
  734. ````C#
  735. public class DataSourceFactory
  736. {
  737. ...
  738. public static ObjectCache CacheProvider
  739. {
  740. get { return cacheProvider; }
  741. }
  742. }
  743. ````
  744. 1. Finally, add a method to return an instance of the **IProductRepository** data source initialized with the configured cache service provider.
  745. (Code Snippet - _BuildingAppsWithCachingService-Ex3-GetProductsRepository method-CS_)
  746. <!--mark: 4-13-->
  747. ````C#
  748. public class DataSourceFactory
  749. {
  750. ...
  751. public static IProductRepository GetProductsRepository(bool enableCache)
  752. {
  753. var dataSource = new ProductsRepository();
  754. if (enableCache && CacheProvider != null)
  755. {
  756. return new CachedProductsRepository(dataSource, cacheProvider);
  757. }
  758. return dataSource;
  759. }
  760. }
  761. ````
  762. <a name="Ex3Task4" />
  763. #### Task 4 Configuring the Application for Caching ####
  764. In this task, you will update the application to take advantage of the data source factory to instantiate the product catalog data source. To complete the setup of the caching layer, you will define the necessary configuration settings to select a caching provider.
  765. 1. Open the **HomeController.cs** file in the **Controllers** folder and find the **Index** method. Inside this method, replace the line that initializes the **productRepository** local variable with the code shown (highlighted) below that uses the **DataSourceFactory** to retrieve an **IProductRepository** instance.
  766. <!--mark: 10-->
  767. ````C#
  768. public class HomeController : Controller
  769. {
  770. ...
  771. public ActionResult Index()
  772. {
  773. bool enableCache = (bool)this.Session["EnableCache"];
  774. // retrieve product catalog from repository and measure the elapsed time
  775. Services.IProductRepository productRepository =
  776. CloudShop.Services.DataSourceFactory.GetProductsRepository(enableCache);
  777. Stopwatch stopWatch = new Stopwatch();
  778. stopWatch.Start();
  779. ...
  780. }
  781. ...
  782. }
  783. ````
  784. 1. To configure the **DataSourceFactory**, open the **Web.config** file and add the following (highlighted) setting to the **appSettings** section.
  785. (Code Snippet - _BuildingAppsWithCachingService-Ex3-Web.config appSettings section-CS_)
  786. <!--mark: 3-->
  787. ````XML
  788. <appSettings>
  789. ...
  790. <add key="CacheService.Provider" value="InMemory" />
  791. </appSettings>
  792. ````
  793. >**Note:** If you host the application in a single node, the in-memory cache provider would be a good choice.
  794. 1. Press **CTRL+F5** to build and test the enhanced caching implementation in the compute emulator.
  795. 1. When you start the application, the cache is initially disabled. Click **Yes** in **Enable Cache** and wait for the page to refresh. Remember that the initial request after you enable the cache includes the overhead required to retrieve the data and insert it into the cache.
  796. 1. Click **Products**, or refresh the page in the browser once again. This time, the application retrieves the product data from the cache and the elapsed time should be lower, most likely under a millisecond given that you have currently configured it to use the in-memory cache provided by the .NET Framework.
  797. 1. Now, in the **Web.config** file, locate the **appSettings** section and set the value of the **CacheService.Provider** setting to _Azure_.
  798. <!--mark: 3-->
  799. ````XML
  800. <appSettings>
  801. ...
  802. <add key="CacheService.Provider" value="Azure" />
  803. </appSettings>
  804. ````
  805. >**Note:** If you host the application in multiple nodes, the in-memory cache provider is no longer a good choice. Instead, you can take advantage of the distributed cache offered by the Windows Azure Caching.
  806. 1. Save the **Web.config** file.
  807. 1. Click the **Recycle** link to recycle the role and reload the configuration. Once you click on the link, the Products page will turn blank.
  808. 1. Got back to browser, remove _/Home/Recylce_ from address bar, and then press Enter to reload the site. The **Products** page should come back normal after a short delay.
  809. 1. Make sure that the cache is still enabled and then refresh the page in the browser **twice** to prime the cache with data. Notice that the elapsed times for the cached scenario have increased indicating that the application is now using the Windows Azure Caching provider instead of the in-memory provider.
  810. ---
  811. <a name="Summary" />
  812. ## Summary ##
  813. In this hands-on lab, you explored the use of the Windows Azure Caching. You saw how to configure session state to be cached across a cache cluster, allowing sessions to be preserved in the presence of restarts and across multiple role instances hosting the application. In addition, you learnt the basics of data caching with Windows Azure and in particular, how to cache the results of queries to a Windows Azure SQL Database. Finally, you looked at a reusable caching layer implementation that will allow you to add a caching layer to your applications in a very simple manner.