PageRenderTime 71ms CodeModel.GetById 3ms app.highlight 58ms RepoModel.GetById 2ms 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

Large files files are truncated, but you can click here to view the full file

  1<a name="HOLTop" />
  2# Building Windows Azure Cloud Services with Cache Service #
  3
  4---
  5
  6<a name="Overview" />
  7## Overview ##
  8
  9Windows 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.
 10
 11<a name="Objectives" />
 12### Objectives ###
 13In this hands-on lab, you will learn how to:
 14
 15- Easily and quickly enable Cache service.
 16- Use Cache Service for your Asp.Net session state.
 17- Cache reference data from Windows Azure SQL Database in Cache Service.
 18- Create a reusable and extensible caching layer for your Cloud Services.
 19
 20During this lab, you will explore how to use these features in a simple Asp.Net MVC4 application.
 21
 22<a name="Prerequisites" />
 23### Prerequisites ###
 24
 25The following is required to complete this hands-on lab:
 26
 27- [Microsoft Visual Studio 2012 Express for Web][1] or higher
 28- [Windows Azure Tools for Microsoft Visual Studio 1.8][2]
 29- A Windows Azure subscription - [sign up for a free trial](http://aka.ms/WATK-FreeTrial)
 30
 31[1]: http://www.microsoft.com/visualstudio/
 32[2]: http://www.windowsazure.com/en-us/develop/downloads/
 33[3]: http://aka.ms/WATK-FreeTrial
 34
 35>**Note:** This lab was designed for Windows 8.
 36
 37<a name="Setup" />
 38### Setup ###
 39In order to run the exercises in this hands-on lab you need to set up your environment first.
 40
 411. Open a Windows Explorer window and browse to the lab’s **Source** folder.
 421. 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.
 431. If the User Account Control dialog is shown, confirm the action to proceed.
 44
 45>**Note:** Make sure you have checked all the dependencies for this lab before running the setup.
 46
 47>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 application’s configuration file to point to your database for each solution.
 48
 49>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.
 50
 51>![SQL database setup](Images/sql-database-setup.png?raw=true "Windows Azure SQL Database setup")
 52
 53>_Windows Azure SQL Database setup_
 54
 55<a name="CodeSnippets" />
 56### Using the Code Snippets ###
 57
 58Throughout 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. 
 59
 60>**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.
 61
 62---
 63
 64<a name="Exercises" />
 65## Exercises ##
 66This hands-on lab includes the following exercises:
 67
 681. [Enable Cache service for Session State](#Exercise1)
 691. [Caching Data with Cache service](#Exercise2)
 701. [Creating a Reusable and Extensible Caching Layer](#Exercise3)
 71
 72Estimated time to complete this lab: **60 minutes**.
 73
 74>**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.
 75
 76<a name="Exercise1" />
 77### Exercise 1: Enable Cache service for Session State ###
 78
 79In 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.
 80
 81<a name="Ex1Task1" />
 82#### Task 1 – Running the Azure Store Sample Site in the Compute Emulator ####
 83
 84In 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.
 85
 861. Start **Microsoft Visual Studio 2012 Express for Web** as administrator.
 871. Open the **Begin** solution located at **Source\\Ex1-CacheSessionState\\Begin**.
 88
 89	>**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.
 90
 91	>To set the start-up project, in **Solution Explorer**, right-click the **CloudShop.Azure** project and select **Set as StartUp Project**.
 92	
 93	>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.
 94
 951. 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.
 96
 97	>**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.
 98
 991. Press **CTRL** + **F5** to build and run the application withouth debugging in the compute emulator. 
100
101	>**Note:** Make sure that you run the application without debugging. With debugging mode you won't be able to recycle the web role
102
1031. Explore the main page of the application, the **Products** page, which displays a list of products obtained from a Windows Azure SQL Database.
104
105	![Azure Store products page](Images/azure-store-products-page.png?raw=true "Azure Store products page")
106
107	_Azure Store products page_
108
1091. 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.
1101. 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.
111
112	![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")
113
114	_Checkout page showing the contents of the shopping cart_
115
1161. Navigate back to **Products** page.
1171. 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.
1181. In the **Compute Emulator**, observe how the web role is recycled by the emulator:
119
120	![Suspending the service role instance](Images/suspending-the-service-role-instance.png?raw=true "Suspending the service role instance")
121
122	_Web role recycled_
123
1241. 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.
125
1261. Navigate to **Checkout** page. Notice that the order now appears empty.
127
128	>**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.
129
1301. Close the browser window to stop the application.
131
132<a name="Ex1Task2" />
133#### Task 2 – Adding a dedicated caching role ####
134In 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. 
135
1361. In solution explorer, expand **CloudShop.Azure** node, and then right-click on **Roles**. Then, select **Add**->**New Worker Role Project...***.
1372. In **Add New Role Project** dialog, select **Cache Worker Role** template. Name the role as **CacheWorkerRole**, and then click **Add**.
138
139  >**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. 
140
141<a name="Ex1Task3" />
142#### Task 3 – Configuring Session State Using Windows Azure Cache service ####
143
144In 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. 
145
1461. In Visual Studio 2012 Express for Web, open **Package manager Console** from **Tools**->**Library package Manager**->**Package Manager Console** menu.
147
1481. 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:  
149 
150	````PowerShell
151	Install-package Microsoft.WindowsAzure.Caching 
152	````
153   
1541. Open the **Web.config** file located in the root folder of the **CloudShop** project.
1551. Change **[cache cluster role name]** to **CacheWorkerRole**.
156
157	<!--mark: 4-->
158	```` XML
159	<dataCacheClients>
160     <tracing sinkType="DiagnosticSink" traceLevel="Error" />
161      <dataCacheClient name="default">
162       <autoDiscover isEnabled="true" identifier="CacheWorkerRole" />
163      <!--<localCache isEnabled="true" sync="TimeoutBased" objectCount="100000" ttlValue="300" />-->
164      </dataCacheClient>
165  </dataCacheClients>
166	  ...
167	````
168
1691. Add a new session state provider configuration under System.Web tag:  
170
171	````XML
172	<system.Web>
173	...
174	<sessionState mode="Custom" customProvider="NamedCacheBProvider">
175      <providers>
176        <add cacheName="default" name="NamedCacheBProvider" 
177             dataCacheClientName="default" applicationName="MyApp" 
178             type="Microsoft.Web.DistributedCache.DistributedCacheSessionStateStoreProvider, Microsoft.Web.DistributedCache" />
179      </providers>
180    </sessionState>
181	...
182	</system.web>
183   ````
1841. Press **CTRL + S** to save your changes to the **Web.config** file.
185
186<a name="Ex1Task4"></a>
187#### Task 4 – Verification ####
188
1891. Press **Ctrl + F5** to build and run the application. Wait for the browser to launch and show the **Products** page. 
1901. Select one product from the list and click Add item to cart. Repeat the process to store additional items in the cart.
1911. Click the **Checkout** link to view the contents of the shopping cart. Verify that the items you selected appear on the list.
1921. Navigate back to **Products** page and click on "Recycle" link.
1931. Observe the web role getting recycled in **Show Compute Emulator UI**. 
1941. Go back to browser, remove */Home/Recycle* from address, and then press Enter to reload the site.
1951. **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.
196
197	> **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.
198
1991. Close the browser window to stop the application.
200
201<a name="Exercise2" />
202### Exercise 2: Caching Data with Windows Azure Caching ###
203
204This 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.
205During 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.
206
207<a name="Ex2Task1" />
208#### Task 1 – Caching Data Retrieved from the SQL Reporting ####
209
210To 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.
211In this task, you update the data access code to cache the result of queries to Windows Azure SQL Database using the Windows Azure Caching. 
212
2131. Start **Microsoft Visual Studio 2012 Express for Web** as an administrator.
2141. Open the **Begin** solution located at **Source\\Ex2-CachingData\\Begin**.
215
216	>**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. 
217	> To set the startup project, in **Solution Explorer**, right-click the **CloudShop.Azure** project and then select **Set as StartUp Project**. 
218	> 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.
219
2201. 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.
221
222	> **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.
223
2241. Open the **ProductsRepository.cs** file in the **Services** folder of the **CloudShop** project.
2251. Add a namespace directive for **Microsoft.ApplicationServer.Caching**.
226
227	<!--mark: 5-->
228	````C#
229	using System;
230	using System.Collections.Generic;
231	using System.Linq;
232	using CloudShop.Models;
233	using Microsoft.ApplicationServer.Caching;
234	...
235	````
236
2371. 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.
238
239	(Code Snippet - _BuildingAppsWithCachingService-Ex2-ProductsRepository constructor-CS_)
240	<!--mark: 3-9-->
241	````C#
242	public class ProductsRepository : IProductRepository	
243	{
244	  private static DataCacheFactory cacheFactory = new DataCacheFactory();
245	  private bool enableCache = false;
246 
247	  public ProductsRepository(bool enableCache)
248	  {
249	    this.enableCache = enableCache;
250	  }
251 
252	  public List<string> GetProducts()
253	  {
254	    ...
255	  }
256	}
257	````
258
259	> **Note:** The **DataCacheFactory** member is declared as static and is used throughout the lifetime of the application.
260
2611. Locate the **GetProducts** method and insert the following (highlighted) code immediately after the line that declares the **products** local variable.
262	
263	(Code Snippet - _BuildingAppsWithCachingService-Ex2-GetProducts read cache-CS_)
264	<!--mark: 8-30-->
265	````C#
266	public class ProductsRepository : IProductRepository
267	{
268	  ...
269	  public List<string> GetProducts()
270	  {
271	    List<string> products = null;
272	
273	    DataCache dataCache = null;
274	    if (this.enableCache)
275	    {
276	      try
277	      {
278	        dataCache = cacheFactory.GetDefaultCache();
279	        products = dataCache.Get("products") as List<string>;
280	        if (products != null)
281	        {
282	          products[0] = "(from cache)";
283	          return products;
284	        }
285	      }
286	      catch (DataCacheException ex)
287	      {
288	        if (ex.ErrorCode != DataCacheErrorCode.RetryLater)
289	        {
290	          throw;
291	        }
292
293	        // ignore temporary failures
294	      }
295	    }
296 	    
297	    NorthwindEntities context = new NorthwindEntities();
298	    
299	    try
300	    {
301	      var query = from product in context.Products
302	                  select product.ProductName;
303	      products = query.ToList();
304	    }
305	    finally
306	    {
307	      if (context != null)
308	      {
309	        context.Dispose();
310	      }
311	    }
312	    
313	    return products;
314	  }
315	}
316	````
317
318	>**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.
319
3201. 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.
321
322	(Code Snippet - _BuildingAppsWithCachingService-Ex2-GetProducts write cache-CS_)
323	<!--mark: 30-35-->
324	````C#
325	public class ProductsRepository : IProductRepository
326	{
327		...
328		public List<string> GetProducts()
329		{
330			List<string> products = null;
331		
332			DataCache dataCache = null;
333			if (this.enableCache)
334			{
335			  ...
336			}
337		
338			NorthwindEntities context = new NorthwindEntities();
339		
340			try
341			{
342			  var query = from product in context.Products
343			             select product.ProductName;
344			  products = query.ToList();
345			}
346			finally
347			{
348			  if (context != null)
349			  {
350			    context.Dispose();
351			  }
352			}
353		
354			products.Insert(0, "(from data source)");
355		
356			if (this.enableCache && dataCache != null)
357			{
358			  dataCache.Add("products", products, TimeSpan.FromSeconds(30));
359			}
360			
361			return products;
362		}
363	}
364	````
365
366	>**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.
367
368<a name="Ex2Task2" />
369#### Task 2 – Measuring the Data Access Latency ####
370
371In 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.
372
3731. Open the **HomeController.cs** file in the **Controllers** folder and add the **System.Diagnostics** using directive at the top of the file.
374	
375	<!-- mark:1 -->
376	````C#
377	using System.Diagnostics;
378	````
379
3801. 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.
381
382	(Code Snippet - _BuildingAppsWithCachingService-Ex2-GetProducts latency-CS_)
383	<!--mark: 9-17; strike:6-8-->
384	````C#
385	public class HomeController : Controller
386	{
387	  ...                       
388	  public ActionResult Index()
389	  {
390	    Services.IProductRepository productRepository =
391	        new Services.ProductsRepository();
392	    var products = productRepository.GetProducts();
393	    bool enableCache = (bool)this.Session["EnableCache"];
394	
395	    // retrieve product catalog from repository and measure the elapsed time
396	    Services.IProductRepository productRepository =
397	        new Services.ProductsRepository(enableCache);
398	    Stopwatch stopWatch = new Stopwatch();
399	    stopWatch.Start();
400	    var products = productRepository.GetProducts();
401	    stopWatch.Stop();
402 
403	    // add all products currently not in session
404	    var itemsInSession = this.Session["Cart"] as List<string> ?? new List<string>();
405	    var filteredProducts = products.Where(item => !itemsInSession.Contains(item));
406	
407	    IndexViewModel model = new IndexViewModel()
408	    {
409	      Products = filteredProducts
410	    };
411	
412	    return View(model);
413	  }
414	  ...
415	}
416	````
417
4181. In the same method, locate the code that creates a new **IndexViewModel** instance and replace its initialization with the following (highlighted) code block.
419
420	(Code Snippet - _BuildingAppsWithCachingService-Ex2-IndexViewModel initialization-CS_)
421	<!--mark: 22-25-->
422	````C#
423	public class HomeController : Controller
424	{
425	  ...                       
426	  public ActionResult Index()
427	  {
428	    bool enableCache = (bool)this.Session["EnableCache"];
429	
430	    // retrieve product catalog from repository and measure the elapsed time
431	    Services.IProductRepository productRepository =
432	        new Services.ProductsRepository(enableCache);
433	    Stopwatch stopWatch = new Stopwatch();
434	    stopWatch.Start();
435	    var products = productRepository.GetProducts();
436	    stopWatch.Stop();
437	
438	    // add all products currently not in session
439	    var itemsInSession = this.Session["Cart"] as List<string> ?? new List<string>();
440	    var filteredProducts = products.Where(item => !itemsInSession.Contains(item));
441	
442	    IndexViewModel model = new IndexViewModel()
443	    {
444	      Products = filteredProducts,
445	      ElapsedTime = stopWatch.ElapsedMilliseconds,
446	      IsCacheEnabled = enableCache,
447	      ObjectId = products.GetHashCode().ToString()
448		};
449	
450	    return View(model);
451	  }
452	  ...
453	}
454	````
455
456	>**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.
457
458
4591. Add a new action method to the **HomeController** to enable or disable the cache from the UI of the application.
460
461	(Code Snippet - _BuildingAppsWithCachingService-Ex2-EnableCache method-CS_)
462	<!--mark: 4-8-->
463	````C#
464	public class HomeController : Controller
465	{
466	  ...
467	  public ActionResult EnableCache(bool enabled)
468	  {
469	    this.Session["EnableCache"] = enabled;
470	    return RedirectToAction("Index");
471	  }
472	 }
473	````
474
4751. Press **F5** to build and launch the application in the compute emulator.
476
477	>**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.
478
4791. 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.
480
481	>**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.
482
483	![Running the application without the cache](Images/running-the-application-without-the-cache.png?raw=true "Running the application without the cache")
484
485	_Running the application without the cache_
486
4871. 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.
488
4891. 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.
490
4911. 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.
492
493	![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")
494
495	_Running the application with the cache enabled_
496
4971. Close the browser.
498
499<a name="Ex2Task3"></a>
500#### Task 3 – Enabling the Local Cache ####
501
502When 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.
503
5041. Open the **ProductsRepository.cs** file in the **Services** folder of the **CloudShop** project.
505
506	>**Note:** Make sure your solution is not running before editing the files.
507
5081. 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.
509
510	(Code Snippet - _BuildingAppsWithCachingService-Ex2-ProductsRepository with local cache-CS_)
511	<!--mark: 2-34-->
512	````C#
513	...	
514	private static DataCacheFactory cacheFactory;
515	private static DataCacheFactoryConfiguration factoryConfig;
516	private bool enableCache = false;
517	private bool enableLocalCache = false;
518	
519	public ProductsRepository(bool enableCache, bool enableLocalCache)
520	{
521	    this.enableCache = enableCache;
522	    this.enableLocalCache = enableLocalCache;
523	
524	    if (enableCache)
525	    {
526	        if (enableLocalCache && (factoryConfig == null || !factoryConfig.LocalCacheProperties.IsEnabled))
527	        {
528	            TimeSpan localTimeout = new TimeSpan(0, 0, 30);
529	            DataCacheLocalCacheProperties localCacheConfig = new DataCacheLocalCacheProperties(10000, localTimeout, DataCacheLocalCacheInvalidationPolicy.TimeoutBased);
530	            factoryConfig = new DataCacheFactoryConfiguration();
531	
532	            factoryConfig.LocalCacheProperties = localCacheConfig;
533	            cacheFactory = new DataCacheFactory(factoryConfig);
534	        }
535	        else if (!enableLocalCache && (factoryConfig == null || factoryConfig.LocalCacheProperties.IsEnabled))
536	        {
537	            cacheFactory = null;
538	        }
539	    }
540	
541	    if (cacheFactory == null)
542	    {
543	        factoryConfig = new DataCacheFactoryConfiguration();
544	        cacheFactory = new DataCacheFactory(factoryConfig);
545	    }
546	} 
547	...
548	````
549
5501. 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:
551
552	(Code Snippet - _BuildingAppsWithCachingService-Ex2-GetProducts LocalCache-CS_)
553	<!--mark: 7-10; strike: 11-13-->
554	````C#
555	public class HomeController : Controller
556	{
557	  ...                       
558	  public ActionResult Index()
559	  {
560		bool enableCache = (bool)this.Session["EnableCache"];
561		bool enableLocalCache = (bool)this.Session["EnableLocalCache"];
562
563		// retrieve product catalog from repository and measure the elapsed time
564		Services.IProductRepository productRepository = new Services.ProductsRepository(enableCache, enableLocalCache);
565		// retrieve product catalog from repository and measure the elapsed time
566		Services.IProductRepository productRepository =
567		new Services.ProductsRepository(enableCache);
568		Stopwatch stopwatch = new Stopwatch();
569		stopWatch.Start();
570		var products = productRepository.GetProducts();
571	    ...
572	}
573	````
574
5751. In the same method, locate the code that creates a new **IndexViewModel** and add the following highlighted property.
576
577	<!--mark: 25-->
578	````C#
579	public class HomeController : Controller
580	{
581	  ...                       
582	  public ActionResult Index()
583	  {
584	      bool enableCache = (bool)this.Session["EnableCache"];
585	      bool enableLocalCache = (bool)this.Session["EnableLocalCache"];
586	      // retrieve product catalog from repository and measure the elapsed time
587	      Services.IProductRepository productRepository =
588	      new Services.ProductsRepository(enableCache, enableLocalCache);
589	      Stopwatch stopwatch = new Stopwatch();
590	      stopWatch.Start();
591	      var products = productRepository.GetProducts();
592	      stopWatch.Stop();
593	
594	      // add all products currently not in session
595	      var itemsInSession = this.Session["Cart"] as List<string> ?? new List<string>();
596	      var filteredProducts = products.Where(item => !itemsInSession.Contains(item));
597	
598	      IndexViewModel model = new IndexViewModel()
599	      {
600	          Products = filteredProducts,
601	          ElapsedTime = stopWatch.ElapsedMilliseconds,
602	          IsCacheEnabled = enableCache,
603	          IsLocalCacheEnabled = enableLocalCache,
604	          ObjectId = products.GetHashCode().ToString()
605	      };
606	      return View(model);
607	  }
608	}
609	````
610
6111. Add a new action method to the **HomeController** to enable or disable the local cache from the UI of the application.
612
613	(Code Snippet - _BuildingAppsWithCachingService-Ex2-EnableLocalCache method-CS_)
614	<!--mark: 4-8-->
615	````C#
616	public class HomeController : Controller
617	{
618	  ...
619	  public ActionResult EnableLocalCache(bool enabled)
620	  {
621	    this.Session["EnableLocalCache"] = enabled;
622	    return RedirectToAction("Index");
623	  }
624 }
625	````
626
6271. Open **Index.cshtml** file in the **Views\Home** folder and add the following highlighted code above the **elapsedTime** div.
628
629	(Code Snippet - _BuildingAppsWithCachingService-Ex2-EnableLocalCache Option-HTML_)
630	<!--mark: 12-23-->
631	````HTML
632	<fieldset>
633		 <legend>Cache settings for product data</legend>Enable Cache:
634		 @if (Model.IsCacheEnabled)
635		 {
636			  <span>Yes |</span><span>@Html.ActionLink("No", "EnableCache", new { enabled = false })</span>
637		 }
638		 else
639		 {
640			  <span>@Html.ActionLink("Yes", "EnableCache", new { enabled = true })</span><span> | No</span>
641		 }
642		 <br />
643		 @if(Model.IsCacheEnabled)
644		 {
645			  <span>Use Local Cache:</span>
646			  if (Model.IsLocalCacheEnabled)
647			  {
648					<span>Yes |</span><span>@Html.ActionLink("No", "EnableLocalCache", new { enabled = false })</span>
649			  }
650			  else
651			  {
652					<span>@Html.ActionLink("Yes", "EnableLocalCache", new { enabled = true })</span><span> | No</span>
653			  }
654		 }
655		 <div id="elapsedTime">Elapsed time: @Model.ElapsedTime.ToString() milliseconds.</div>
656	</fieldset>
657````
658
6591. Press **F5** to build and launch the application in the compute emulator.
660 
6611. 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. 
662
6631. 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. 
664
665	![Using the local cache](Images/using-the-local-cache.png?raw=true "Using the local cache")
666
667	_Using the local cache_
668
6691. 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.
670
671	>**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. 
672You 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.
673
6741. 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.
675
676<a name="Exercise3" />
677### Exercise 3: Creating a Reusable and Extensible Caching Layer ###
678
679In 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. 
680
681In 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.
682
683To 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.
684
685The 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.
686
687The 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). 
688
689To 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.
690
691Currently, 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.
692
693To 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:
694
695- Create a new **CachingProductsReposity** class that inherits from **CachedDataSource**.
696
697- 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.
698
699- 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.
700
701<a name="Ex3Task1" />
702#### Task 1 – Implementing a Caching Data Source Base Class ####
703
704In 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.
705
7061. Start **Microsoft Visual Studio 2012 Express for Web** as administrator.
707
7081. Open the **Begin** solution located at **Source\\Ex3-ReusableCachingImplementation**.
709
710	>**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.  
711	
712	> To set the start up project, in **Solution Explorer**, right-click the **CloudShop.Azure** project and then select **Set as StartUp Project**. 
713
714	> 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.
715
7161. 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.
717
718	>**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.
719
7201. Add a reference to the **System.Runtime.Caching** assembly in the **CloudShop** project.
721
7221. In the **Services** folder of the **CloudShop** project, add a new folder named **Caching**.
723
7241. Inside the **Caching** folder created in the previous step, add a new class file named **CachedDataSource.cs**.
725
7261. In the new class file, add a namespace directive for **System.Runtime.Caching**.
727
728	<!--mark: 5-->
729	````C#
730	using System;
731	using System.Collections.Generic;
732	using System.Linq;
733	using System.Web;
734	using System.Runtime.Caching;
735	...
736	````
737
7381. Specify an **abstract** modifier for the **CachedDataSource** class.
739
740	<!--mark: 1-3-->
741	````C#
742	public abstract class CachedDataSource
743	{
744	}
745	````
746
7471. Add the following (highlighted) member fields to the class.
748
749	(Code Snippet - _BuildingAppsWithCachingService-Ex3-CachedDataSource member fields-CS_)
750	<!--mark: 3,4-->
751	````C#
752	public abstract class CachedDataSource
753	{
754	  private readonly ObjectCache cacheProvider;
755	  private readonly string regionName;
756	}
757	````
758
7591. Now, define a constructor that receives an object cache and a region name as parameters, as shown (highlighted) below.
760
761	(Code Snippet - _BuildingAppsWithCachingService-Ex3-CachedDataSource constructor-CS_)
762	<!--mark: 4-18-->
763	````C#
764	public abstract class CachedDataSource
765	{
766	  ...
767	  public CachedDataSource(ObjectCache cacheProvider, string regionName)
768	  {
769	    if (cacheProvider == null)
770	    {
771	      throw new ArgumentNullException("cacheProvider");
772	    }
773	
774	    if (cacheProvider is MemoryCache)
775	    {
776	      regionName = null;
777	    }
778	
779	    this.cacheProvider = cacheProvider;
780	    this.regionName = regionName;
781	  }
782	}
783	````
784
785	>**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.
786
7871. Next, add the following (highlighted) method to retrieve data from the cache.
788
789	(Code Snippet - _BuildingAppsWithCachingService-Ex3-RetrieveCachedData method-CS_)
790	<!--mark: 4-19-->
791	````C#
792	public abstract class CachedDataSource
793	{
794	  ...
795	  protected T RetrieveCachedData<T>(string cacheKey, Func<T> fallbackFunction, CacheItemPolicy cachePolicy) where T : class
796	  {
797	    var data = this.cacheProvider.Get(cacheKey, this.regionName) as T;
798	    if (data != null)
799	    {
800	      return data;
801	    }
802	
803	    data = fallbackFunction();
804	    if (data != null)
805	    {
806	      this.cacheProvider.Add(new CacheItem(cacheKey, data, this.regionName), cachePolicy);
807	    }
808	
809	    return data;
810	  }
811	}
812	````
813
814	>**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.
815
8161. Finally, add a method to delete items from the cache.
817
818	(Code Snippet - _BuildingAppsWithCachingService-Ex3-RemoveCachedData method-CS_)
819	<!--mark: 4-7-->
820	````C#
821	public abstract class CachedDataSource
822	{
823	  ...
824	  protected void RemoveCachedData(string cacheKey)
825	  {
826	    this.cacheProvider.Remove(cacheKey, this.regionName);
827	  }
828	}
829	````
830
8311. Save the **CachedDataSource.cs** file.
832
833<a name="Ex3Task2" />
834#### Task 2 – Building a Caching Product Catalog Repository ####
835
836Once 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.
837
8381. Inside the **Services\Caching** folder of the **CloudShop** project, add a new class file named **CachedProductsRepository.cs**.
839
8401. In the new class file, append a namespace directive for **System.Runtime.Caching** and **CloudShop.Services**.
841
842	<!-- mark:5-6 -->
843	````C#
844	using System;
845	using System.Collections.Generic;
846	using System.Linq;
847	using System.Web;
848	using CloudShop.Services;
849	using System.Runtime.Caching;
850	...
851	````
852
8531. Change the declaration for the **CachedProductsRepository** class to derive from both **CachedDataSource** and **IProductRepository**, as shown (highlighted) below.
854
855	<!--mark: 2-->
856	````C#
857	public class CachedProductsRepository 
858	  : CachedDataSource, IProductRepository
859	{
860	}
861	````
862
863	>**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.
864
8651. 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.
866
867	(Code Snippet - _BuildingAppsWithCachingService-Ex3-CachedProductsRepository constructor-CS_)
868	<!--mark: 3-9-->
869	````C#
870	public class CachedProductsRepository : CachedDataSource, IProductRepository
871	{
872	  private readonly IProductRepository repository;
873	 
874	  public CachedProductsRepository(IProductRepository repository, ObjectCache cacheProvider) :
875	    base(cacheProvider, "Products")
876	  {
877	    this.repository = repository;
878	  }
879	}
880	````
881
882	>**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.
883
8841. Finally, fulfill the **IProductRepository** contract by implementing the **GetProducts** method, as shown (highlighted) below.
885
886	(Code Snippet - _BuildingAppsWithCachingService-Ex3-GetProducts method -CS_)
887	<!--mark: 4-10-->
888	````C#
889	public class CachedProductsRepository : CachedDataSource, IProductRepository
890	{
891	  ...
892	  public List<string> GetProducts()
893	  {
894		return RetrieveCachedData(
895		"allproducts",
896		() => this.repository.GetProducts(),
897		new CacheItemPolicy { AbsoluteExpiration = DateTime.UtcNow.AddMinutes(1) });
898	  }
899	}
900	````
901
902	>**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.
903
904	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.
905
906<a name="Ex3Task3" />
907#### Task 3 – Creating a Data Source Factory Class ####
908
909In this task, you will create a factory class that can return d…

Large files files are truncated, but you can click here to view the full file