Upload
others
View
0
Download
0
Embed Size (px)
Citation preview
Page | 1
Hands-On Lab
Building Windows Azure Apps with Caching service
Lab version: 2.0.0
Last updated: 11/16/2010
CONTENTS
OVERVIEW ................................................................................................................................................... 3
Page | 2
GETTING STARTED: PROVISIONING THE SERVICE .............................................................................. 5 Task 1 – Provisioning the Service Bus Namespace ............................................................................... 5
EXERCISE 1: USING THE WINDOWS AZURE APPFABRIC CACHING FOR SESSION STATE ........... 9 Task 1 – Running the Azure Store Sample Site in the Development Fabric........................................ 10
Task 2 – Configuring Session State Using Windows Azure AppFabric Caching ................................... 14
Task 3 – Verification ............................................................................................................................ 17
EXERCISE 2: CACHING DATA WITH THE WINDOWS AZURE APPFABRIC CACHING ..................... 18 Task 1 – Caching Data Retrieved from the SQL Azure Repository ...................................................... 18
Task 2 – Measuring the Data Access Latency ..................................................................................... 22
Task 3 – Enabling the Local Cache....................................................................................................... 27
EXERCISE 3: CREATING A REUSABLE AND EXTENSIBLE CACHING LAYER .................................. 33 Task 1 – Implementing a Caching Data Source Base Class ................................................................. 34
Task 2 – Building a Caching Product Catalog Repository .................................................................... 37
Task 3 – Creating a Data Source Factory Class .................................................................................... 39
Task 4 – Configuring the Application for Caching ............................................................................... 42
SUMMARY .................................................................................................................................................. 44
Page | 3
Overview
The Windows Azure AppFabric Caching service provides a distributed, in-memory cache for your
applications. In this lab, learn how to use the Caching service for both your ASP.NET session state and
to cache data from your data-tier. You will see how the Caching service provides your application with a
cache that provides low latency and high throughput without having to configure, deploy, or manage
the service.
Objectives
In this hands-on lab, you will learn how to:
Easily and quickly provision your cache through the portal
Use the caching service for your ASP.NET session state
Cache reference data from SQL Azure in the caching service
Create a reusable and extensible caching layer for your applications
During this lab, you will explore how to use these features in a simple ASP.NET MVC application.
Prerequisites
The following is required to complete this hands-on lab:
Microsoft .NET Framework 4.0
Microsoft IIS 7
Microsoft Visual Studio 2010
Windows Azure Tools for Microsoft Visual Studio 1.2 (June 2010)
Windows Azure AppFabric SDK v2.0
Setup
For convenience, much of the code used in this hands-on lab is available as Visual Studio code snippets.
To check the prerequisites of the lab and install the code snippets:
1. Open a Windows Explorer window and browse to the lab’s Source\Setup folder.
2. Double-click the Dependencies.dep file in this folder to launch the Dependency Checker tool
and install any missing prerequisites and the Visual Studio code snippets.
Page | 4
3. If the User Account Control dialog is shown, confirm the action to proceed.
Note: This process may require elevation. The .dep extension is associated with the Dependency
Checker tool during its installation. For additional information about the setup procedure and how to
install the Dependency Checker tool, refer to the Setup.docx document in the Assets folder of the
training kit.
Note: This lab requires a SQL Azure database to start. To build the Northwind2 database automatically,
the Dependency Checker tool will prompt to you with your SQL Azure account information. Remember
to update the NorthwingEntities connection string in the application’s configuration file to point to
your database for each solution.
Note: Remember to configure the firewall setting your SQL Azure account to allow you to specify a list
of IP addresses that can access your SQL Azure 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 SQL Azure account, refer to the exercise 1 of the Introduction to SQL Azure lab in the training kit.
Figure 1
SQL Azure database setup
Using the Code Snippets
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
2010 to avoid having to add it manually.
Page | 5
If you are not familiar with the Visual Studio Code Snippets, and want to learn how to use them, you can
refer to the Setup.docx document in the Assets folder of the training kit, which contains a section
describing how to use them.
Exercises
This hands-on lab includes the following exercises:
1. Using the Windows Azure AppFabric Caching for Session State
2. Caching Data with the Windows Azure AppFabric Caching
3. Creating a Reusable and Extensible Caching Layer
Estimated time to complete this lab: 60 minutes.
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.
Getting Started: Provisioning the Service
To complete the exercises in this lab, you require an AppFabric Project. Once you have created a project,
you can use it for all the AppFabric labs as well as your own projects.
Task 1 – Provisioning the Service Bus Namespace
In this task, you create a new project and create a service namespace to use the Windows Azure
AppFabric Caching.
1. Navigate to the Windows Azure platform AppFabric portal and, if necessary, sign in using your
Windows Live ID credentials.
2. If this is the first time you access the AppFabric site, you need to create a new project.
Page | 6
Figure 2
AppFabric projects page
3. Enter a Project Name, such as your company name or your own name, and then click OK.
Figure 3
Creating a new Azure AppFabric project
4. In My Projects, click the newly created project to view its summary page.
Figure 4
Selecting a project to manage
5. Now, create a service namespace for this project. To add a service namespace, click the Project
Name link for your new project to open its summary page and then click Add Service
Namespace.
Page | 7
Figure 5
AppFabric project summary page
Note: A service namespace defines a boundary for each application exposed through the
Service Bus, allowing an application to uniquely address the AppFabric service endpoints.
6. Enter a name for your Service Namespace and then click Validate Name to ensure its
availability. Service names must be globally unique as they are hosted in the cloud and
accessible by whomever you decide to grant access. If the service is unavailable, you will need
to pick a different name. Once you have determined that the chosen namespace is valid and
available, click Create.
Figure 6
Creating a new service namespace
Page | 8
7. Locate the new entry in the list of configured service namespaces and then wait for its Status
column to show the namespace as Active.
8. After the namespace is provisioned, click its Cache link to display the Cache Settings page.
Figure 7
Project summary page listing available service namespaces
9. In the Cache Settings page, you can review the different shown values. Locate the Cache section
and review the value shown for the Service URL, the Service Port, Authentication and Token.
These values will be used later on this lab.
Figure 8
Service namespace cache settings
Page | 9
10. Locate Application Integration section, which contains the configuration required to leverage
Windows Azure AppFabric Caching. Do not close the browser, since you will have to come back
to this page later in the lab.
Figure 9
Copying xml from Application Integration section
Exercise 1: Using the Windows Azure
AppFabric Caching for Session State
In this exercise, you explore the use of the session state provider for Windows Azure AppFabric Caching
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 MVC. You will run this
Page | 10
application in the development fabric and then modify it to take advantage of the Windows Azure
AppFabric Caching as the back-end store for the ASP.NET session state. You start with a begin solution
and explore the sample using the default ASP.NET in-proc session state provider. Next, you add
references to the Windows Azure AppFabric Caching assemblies and configure the session state provider
to store the contents of the shopping cart in the distributed cache cluster provided by Window Azure
AppFabric.
Task 1 – Running the Azure Store Sample Site in the Development Fabric
In this task, you will run the Azure Store application in the development fabric using the default session
state provider; you will change that provider to take advantage of the Caching service later on.
1. Start Microsoft Visual Studio 2010 as an administrator.
2. Open the Begin solution located at Ex1-AppFabricCacheSessionState inside the Source folder of
the lab.
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.
To set the start-up project, in Solution Explorer, right-click the AzureStoreService project and
select Set as StartUp Project.
To set the start page, in Solution Explorer, right-click the MVCAzureStore 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.
3. In the Web.config file, update the NorthwindEntities connection string to point to your
database. Replace [YOUR-SQL-AZURE-SERVER-ADDRESS], [SQL-AZURE-USERNAME], and [SQL-
AZURE-PASSWORD] with the SQL Azure database server name, Administrator Username and
Administrator password that you registered at the portal and used for creating the database
during setup.
Note: Make sure that you follow the instructions of the setup section to create a copy of the
Northwind2 database in your own SQL Azure account and configure your SQL Azure firewall
settings.
4. Press F5 to build and run the application in the development fabric.
5. Explore the main page of the application, the Products page, which displays a list products
obtained from a SQL Azure database.
Page | 11
Figure 10
Azure Store products page
6. 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.
7. 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.
Page | 12
Figure 11
Checkout page showing the contents of the shopping cart
8. Do not close the browser window or navigate away from the checkout page.
9. In the task bar, right-click the development fabric icon and select Show Development Fabric UI.
10. In the Development Fabric, right-click the AzureStoreService node and choose Suspend.
Page | 13
Figure 12
Suspending the service role instance
11. Wait until the service stops as indicated by the instance icon turning red. Now, restart the
service instance once again. To do this, right-click the AzureStoreService node and choose Run,
then wait for the service to start.
12. Switch back to the browser window showing the checkout page and click Refresh. Notice that
the order now appears empty.
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 AppFabric 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.
13. Close the browser window to stop the application.
Page | 14
Task 2 – Configuring Session State Using Windows Azure AppFabric Caching
In this task, you will change the Session State provider to take advantage of the Windows Azure
AppFabric Cache as the storage mechanism. This requires adding the appropriate assemblies to the
MVCAzureStore project and then updating the corresponding configuration in the Web.config file.
1. Add a reference to the Microsoft.ApplicationServer.Caching.Client,
Microsoft.ApplicationServer.Caching.Core, and Microsoft.Web.DistributedCache assemblies.
In Solution Explorer, right-click the MVCAzureStore project, select Add Reference, click the
Browse tab, select the assemblies from the %ProgramFiles%\Windows Azure AppFabric
SDK\V2.0\Assemblies\Cache folder, and click OK.
2. Make sure to include these assemblies as part of the service package. To do this, right-click the
Microsoft.ApplicationServer.Caching.Client assembly and select Properties. In the Project
Properties window, verify that the value of the Copy Local setting is set to True. Do the same
for the other assemblies added in the previous step.
Note: In general, you need to set Copy Local = True for any assembly that is not installed by
default in the Windows Azure VMs to ensure that it is deployed with your application.
3. Open the Web.config file located in the root folder of the MVCAzureStore project.
4. Go back to the browser you left open when creating the service namespace, and locate
Application Integration section. Copy the configSections tag.
Figure 13
Copying configSections
5. Go back to Visual Studio 2010, and paste the section inside the configuration tag. Make sure
that this element is the first element inside the configuration tag.
XML
<configuration>
<configSections>
Page | 15
<section name="dataCacheClient"
type="Microsoft.ApplicationServer.Caching.DataCacheClientSection,
Microsoft.ApplicationServer.Caching.Core"
allowLocation="true"
allowDefinition="Everywhere"/>
</configSections>
<connectionStrings>
...
6. Go back to the browser once more, and copy the dataCacheClient configuration.
Figure 14
Copying dataCacheClient
7. Go back to Visual Studio 2010 and paste the configuration you have copied, under the closing
configSections tag, as shown in the image below.
XML
Page | 16
...
</configSections>
<dataCacheClient deployment="Simple">
<hosts>
<host name="[SERVICE-HOST-NAME]" cachePort="22233"/>
</hosts>
<securityProperties mode="Message">
<messageSecurity authorizationInfo="[AUTHORIZATION_INFO]" />
</securityProperties>
</dataCacheClient>
<connectionStrings>
...
8. The last setting you need to configure is the sessionState provider, to do so, go back to the
browser and copy sessionState element.
Figure 15
Copying sessionState
Page | 17
9. Go back to Visual Studio 2010 and paste the element you copied inside the system.web
configuration section.
XML
...
<system.web>
<sessionState mode="Custom"
customProvider="AppFabricCacheSessionStoreProvider">
<providers>
<!-- specify the named cache for session data -->
<add name="AppFabricCacheSessionStoreProvider"
type="Microsoft.Web.DistributedCache.DistributedCacheSessionStateStoreProvider
, Microsoft.Web.DistributedCache"
cacheName="default"
applicationName="AzureStore"
useBlobMode="false" />
</providers>
</sessionState>
<compilation debug="true" targetFramework="4.0">
...
Note: The DistributedCacheSessionStateStoreProvider session state provider enables out-of-
process storage of session state data using Windows Azure AppFabric Cache as the storage
mechanism.
10. Press CTRL + S to save your changes to the Web.config file.
Task 3 – Verification
1. Press F5 to build and run the application.
Wait for the browser to launch and show the Products page. Select one product from the list
and click Add item to cart. Repeat the process to store additional items in the cart.
2. Click the Check Out link to view the contents of the shopping cart. Verify that the items you
selected appear on the list.
3. Do not close the browser window or navigate away from the checkout page.
Page | 18
4. In the task bar, right-click the development fabric icon and select Show Development Fabric UI.
5. In the Development Fabric, right-click the AzureStoreService node and choose Suspend. Wait
until the service stops as indicated by the instance icon turning red.
6. Now, restart the service instance once again. To do this, right-click the AzureStoreService node
and choose Run, then wait for the service to start.
7. Switch back to the browser window showing the checkout page and click Refresh. Notice that
the order is intact. This confirms that with the Windows Azure AppFabric Caching provider, the
session state is stored outside the role instance and can persist through application restarts.
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 will continue to have access to their session data regardless of which instance responds
to the request.
8. Close the browser window to stop the application.
Exercise 2: Caching Data with the
Windows Azure AppFabric Caching
This exercise shows you how to use the Windows Azure AppFabric Caching to cache results from queries
to SQL Azure. You continue with a solution which is based on the one used for the previous exercise—
the only difference is in the home page that 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.
During the exercise, you update the data access code to use a trivial implementation of caching that
uses the canonical pattern where the code checks the cache first to retrieve the results of a query and, if
these are not available, executes the query against the database and then caches the results.
Task 1 – Caching Data Retrieved from the SQL Azure Repository
To make use of the Caching service, 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.
Page | 19
In this task, you update the data access code to cache the result of queries to SQL Azure using the
Windows Azure AppFabric Caching.
1. Start Microsoft Visual Studio 2010 as an administrator.
2. Open the Begin solution located at Ex2-CachingDataWithAppFabric inside the Source folder of
the lab.
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.
To set the startup project, in Solution Explorer, right-click the AzureStoreService project and
then select Set as StartUp Project.
To set the start page, in Solution Explorer, right-click the MVCAzureStore 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.
3. In the Web.config file, update the NorthwindEntities connection string to point to your
database. Replace [YOUR-SQL-AZURE-SERVER-ADDRESS], [SQL-AZURE-USERNAME], and [SQL-
AZURE-PASSWORD] with the SQL Azure database server name, Administrator Username and
Administrator password that you registered at the portal and used for creating the database
during setup.
Note: Make sure that you follow the instructions of the setup section to create a copy of the
Northwind2 database in your own SQL Azure account and configure your SQL Azure firewall
settings.
4. Open the ProductsRepository.cs file in the Services folder of the MVCAzureStore project.
5. Add a namespace directive for Microsoft.ApplicationServer.Caching.
C#
using System;
using System.Collections.Generic;
using System.Linq;
using MVCAzureStore.Models;
using Microsoft.ApplicationServer.Caching;
...
6. In the ProductsRepository class, add the following 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.
(Code Snippet – BuildingAppsWithCachingService-Ex2- ProductsRepository constructor-CS)
Page | 20
C#
public class ProductsRepository : IProductRepository
{
private static DataCacheFactory CacheFactory = new DataCacheFactory();
private bool enableCache = false;
public ProductsRepository(bool enableCache)
{
this.enableCache = enableCache;
}
public List<string> GetProducts()
{
...
}
}
Note: The DataCacheFactory member is declared as static and is used throughout the lifetime
of the application.
7. Locate the GetProducts method and insert the following (highlighted) code immediately after
the line that declares the products local variable.
(Code Snippet – BuildingAppsWithCachingService-Ex2- GetProducts read cache-CS)
C#
public class ProductsRepository : IProductRepository
{
...
public List<string> GetProducts()
{
List<string> products = null;
DataCache dataCache = null;
if (enableCache)
{
try
{
dataCache = CacheFactory.GetDefaultCache();
products = dataCache.Get("products") as List<string>;
if (products != null)
{
products[0] = "(from cache)";
return products;
}
Page | 21
}
catch (DataCacheException ex)
{
if (ex.ErrorCode != DataCacheErrorCode.RetryLater)
{
throw;
}
// ignore temporary failures
}
}
NorthwindEntities context = new NorthwindEntities();
var query = from product in context.Products
select product.ProductName;
products = query.ToList();
return products;
}
}
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 Caching service as a cache miss so that it can retrieve the item
from its data source instead.
8. 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.
(Code Snippet – BuildingAppsWithCachingService-Ex2- GetProducts write cache-CS)
C#
public class ProductsRepository : IProductRepository
{
...
public List<string> GetProducts()
{
List<string> products = null;
DataCache dataCache = null;
if (enableCache)
{
...
}
Page | 22
NorthwindEntities context = new NorthwindEntities();
var query = from product in context.Products
select product.ProductName;
products = query.ToList();
products.Insert(0, "(from data source)");
if (enableCache && dataCache != null)
{
dataCache.Add("products", products, TimeSpan.FromSeconds(30));
}
return products;
}
}
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.
Task 2 – Measuring the Data Access Latency
In this task, you 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.
1. Open the HomeController.cs file in the Controllers folder and 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.
(Code Snippet – BuildingAppsWithCachingService-Ex2- GetProducts latency-CS)
C#
public class HomeController : Controller
{
...
public ActionResult Index()
{
Services.IProductRepository productRepository =
new Services.ProductsRepository();
var products = productRepository.GetProducts();
bool enableCache = (bool)this.Session["EnableCache"];
// retrieve product catalog from repository and measure the elapsed time
Services.IProductRepository productRepository =
Page | 23
new Services.ProductsRepository(enableCache);
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
var products = productRepository.GetProducts();
stopWatch.Stop();
// add all products currently not in session
var itemsInSession = this.Session["Cart"] as List<string> ?? new
List<string>();
var filteredProducts = products.Where(item =>
!itemsInSession.Contains(item));
IndexViewModel model = new IndexViewModel()
{
Products = filteredProducts
};
return View(model);
}
...
}
2. In the same method, locate the code that creates a new IndexViewModel instance and replace
its initialization with the following (highlighted) code block.
(Code Snippet – BuildingAppsWithCachingService-Ex2-IndexViewModel initialization-CS)
C#
public class HomeController : Controller
{
...
public ActionResult Index()
{
bool enableCache = (bool)this.Session["EnableCache"];
// retrieve product catalog from repository and measure the elapsed time
Services.IProductRepository productRepository =
new Services.ProductsRepository(enableCache);
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
var products = productRepository.GetProducts();
stopWatch.Stop();
// add all products currently not in session
var itemsInSession = this.Session["Cart"] as List<string> ?? new
List<string>();
var filteredProducts = products.Where(item =>
!itemsInSession.Contains(item));
Page | 24
IndexViewModel model = new IndexViewModel()
{
Products = filteredProducts,
ElapsedTime = stopWatch.ElapsedMilliseconds,
IsCacheEnabled = enableCache,
ObjectId = products.GetHashCode().ToString()
};
return View(model);
}
...
}
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.
3. Add a new action method to the HomeController to enable or disable the cache from the UI of
the application.
(Code Snippet – BuildingAppsWithCachingService-Ex2-EnableCache method-CS)
C#
public class HomeController : Controller
{
...
public ActionResult EnableCache(bool enabled)
{
this.Session["EnableCache"] = !((bool)this.Session["EnableCache"]);
return RedirectToAction("Index");
}
}
4. Open the Web.config file and locate the dataCacheClient section. Replace the [SERVICE-HOST-
NAME] placeholder with the name of the host for the Caching service endpoint that you
provisioned earlier. For example, for a Service URL such as http://your-
namespace.cache.appfabriclabs.com/, use the value your-namespace.cache.appfabriclabs.com.
Replace the [AUTHORIZATION_INFO] placeholder with the authentication token that you
copied from the Cache Settings page.
Page | 25
5. Press F5 to build and launch the application in the development fabric.
Note: Ideally, you should test the code in Windows Azure. When you execute the application
in the development fabric, consider that accessing the SQL Azure data source and the
Windows AppFabric Caching both 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 Caching service, SQL Azure, the latency is much lower, and the
results should be more significant.
6. 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.
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.
Page | 26
Figure 16
Running the application without the cache
7. 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.
8. Now, click Yes in Use cache for product data 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.
Page | 27
9. Click Products, or refresh the page in the browser. This time, the application retrieves the
product data from the Windows Azure AppFabric 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.
Figure 17
Running the application with the cache enabled
Task 3 – Enabling the Local Cache
Page | 28
When using the Caching service, 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 enable the
local cache and compare the access time with the remote case.
1. Open the ProductsRepository.cs file in the Services folder of the MVCAzureStore project.
2. In the ProductsRepository class, replace the current fields and constructor with the following
code, to add the logic of managing the localCache configuration.
(Code Snippet – BuildingAppsWithCachingService-Ex2- ProductsRepository with local cache-CS)
C#
...
private static DataCacheFactory CacheFactory;
private static DataCacheFactoryConfiguration FactoryConfig;
private bool enableCache = false;
private bool enableLocalCache = false;
public ProductsRepository(bool enableCache, bool enableLocalCache)
{
this.enableCache = enableCache;
this.enableLocalCache = enableLocalCache;
if (enableCache)
{
if (enableLocalCache && (FactoryConfig == null ||
!FactoryConfig.LocalCacheProperties.IsEnabled))
{
TimeSpan localTimeout = new TimeSpan(0, 0, 30);
DataCacheLocalCacheProperties localCacheConfig = new
DataCacheLocalCacheProperties(10000, localTimeout,
DataCacheLocalCacheInvalidationPolicy.TimeoutBased);
FactoryConfig = new DataCacheFactoryConfiguration();
FactoryConfig.LocalCacheProperties = localCacheConfig;
CacheFactory = new DataCacheFactory(FactoryConfig);
}
else if (!enableLocalCache && (FactoryConfig == null ||
FactoryConfig.LocalCacheProperties.IsEnabled))
{
CacheFactory = null;
}
}
if (CacheFactory == null)
{
FactoryConfig = new DataCacheFactoryConfiguration();
CacheFactory = new DataCacheFactory(FactoryConfig);
Page | 29
}
}
...
3. 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
(Code Snippet – BuildingAppsWithCachingService-Ex2- GetProducts LocalCache-CS)
C#
public class HomeController : Controller
{
...
public ActionResult Index()
{
bool enableCache = (bool)this.Session["EnableCache"];
bool enableLocalCache = (bool)this.Session["EnableLocalCache"];
// retrieve product catalog from repository and measure the elapsed time
Services.IProductRepository productRepository = new
Services.ProductsRepository(enableCache, enableLocalCache);
Services.IProductRepository productRepository =
new Services.ProductsRepository(enableCache);
Stopwatch stopwatch = new Stopwatch();
stopWatch.Start();
var products = productRepository.GetProducts();
...
}
4. In the same method, locate the code that creates a new IndexViewModel add the following
property
C#
public class HomeController : Controller
{
...
public ActionResult Index()
{
bool enableCache = (bool)this.Session["EnableCache"];
bool enableLocalCache = (bool)this.Session["EnableLocalCache"];
// retrieve product catalog from repository and measure the elapsed time
Services.IProductRepository productRepository =
new Services.ProductsRepository(enableCache, enableLocalCache);
Page | 30
Stopwatch stopwatch = new Stopwatch();
stopWatch.Start();
var products = productRepository.GetProducts();
stopWatch.Stop();
// add all products currently not in session
var itemsInSession = this.Session["Cart"] as List<string> ?? new
List<string>();
var filteredProducts = products.Where(item =>
!itemsInSession.Contains(item));
IndexViewModel model = new IndexViewModel()
{
Products = filteredProducts,
ElapsedTime = stopWatch.ElapsedMilliseconds,
IsCacheEnabled = enableCache,
IsLocalCacheEnabled = enableLocalCache,
ObjectId = products.GetHashCode().ToString()
};
return View(model);
}
...
}
5. Add a new action method to the HomeController to enable or disable the local cache from the
UI of the application.
(Code Snippet – BuildingAppsWithCachingService-Ex2-EnableLocalCache method-CS)
C#
public class HomeController : Controller
{
...
public ActionResult EnableLocalCache(bool enabled)
{
this.Session["EnableLocalCache"] =
!((bool)this.Session["EnableLocalCache"]);
return RedirectToAction("Index");
}
}
6. Open Index.aspx file in the Views\Home folder and add the following code above the
elapsedTime div.
Page | 31
(Code Snippet – BuildingAppsWithCachingService-Ex2-EnableLocalCache Option-HTML)
HTML
<fieldset>
<legend>Cache settings for product data</legend>Enable Cache:
<%if (Model.IsCacheEnabled)
{ %>
Yes | <%=Html.ActionLink("No", "EnableCache", new { enabled = false
}).ToString()%>
<%}
else
{ %>
<%=Html.ActionLink("Yes", "EnableCache", new { enabled = true
}).ToString()%> | No
<%} %>
<br />
<%if (Model.IsCacheEnabled)
{ %>
Use Local Cache:
<%if (Model.IsLocalCacheEnabled)
{ %>
Yes |
<%=Html.ActionLink("No", "EnableLocalCache", new { enabled = false
}).ToString()%>
<%}
else
{ %>
<%=Html.ActionLink("Yes", "EnableLocalCache", new { enabled = true
}).ToString()%>
| No
<%} %>
<%} %>
<div id="elapsedTime">Elapsed time: <%:Model.ElapsedTime.ToString()%>
milliseconds.</div>
</fieldset>
7. Press F5 to build and launch the application in the development fabric.
8. When you start the application, the cache and the local cache are initially disabled. Enable
cache and then the local cache.
9. 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.
Page | 32
Figure 18
Using the local cache
10. 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.
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.
Page | 33
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.
11. 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.
Exercise 3: Creating a Reusable and
Extensible Caching Layer
In the previous exercise, you explored the fundamental aspects of using the Windows Azure AppFabric
Caching by directly updating the methods in the data access class to cache data retrieved from the
repository. While this was approach yields 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.
In this exercise, you 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.
To build this layer, you implement an abstract caching class named CachedDataSource that provides
support for storing and removing data in the cache. You 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.
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.
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
Page | 34
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.
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.
Currently, the Windows Azure AppFabric 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 AppFabricCacheProvider, in the Assets folder of this lab. This class
derives from ObjectCache to expose the services in the Windows Azure AppFabric Caching.
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 SQL Azure. To create a caching products catalog source, you need to perform the following steps:
Create a new CachingProductsReposity class that inherits from CachedDataSource.
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.
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.
Task 1 – Implementing a Caching Data Source Base Class
In this task, you 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.
1. Start Microsoft Visual Studio 2010 as an administrator.
2. Open the Begin solution located at Ex3-ReusableCachingImplementation inside the Source
folder of the lab.
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.
To set the start up project, in Solution Explorer, right-click the AzureStoreService project and
then select Set as StartUp Project.
Page | 35
To set the start page, in Solution Explorer, right-click the MVCAzureStore 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.
3. In the Web.config file, update the NorthwindEntities connection string to point to your
database. Replace [YOUR-SQL-AZURE-SERVER-ADDRESS], [SQL-AZURE-USERNAME], and [SQL-
AZURE-PASSWORD] with the SQL Azure database server name, Administrator Username and
Administrator password that you registered at the portal and used for creating the database
during setup.
Note: Make sure that you follow the instructions of the setup section to create a copy of the
Northwind2 database in your own SQL Azure account and configure your SQL Azure firewall
settings.
4. Add a reference to the System.Runtime.Caching assembly in the MVCAzureStore project.
5. In the Services folder of the MVCAzureStore project, add a new folder named Caching.
6. Inside the Caching folder created in the previous step, add a new class file named
CachedDataSource.cs.
7. In the new class file, add a namespace directive for System.Runtime.Caching.
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Caching;
...
8. Specify an abstract modifier for the CachedDataSource class.
C#
public abstract class CachedDataSource
{
}
9. Add the following (highlighted) member fields to the class.
(Code Snippet – BuildingAppsWithCachingService-Ex3-CachedDataSource member fields-CS)
C#
public abstract class CachedDataSource
{
Page | 36
private readonly ObjectCache cacheProvider;
private readonly string regionName;
}
10. Now, define a constructor that receives an object cache and a region name as parameters, as
shown (highlighted) below.
(Code Snippet – BuildingAppsWithCachingService-Ex3-CachedDataSource constructor-CS)
C#
public abstract class CachedDataSource
{
...
public CachedDataSource(ObjectCache cacheProvider, string regionName)
{
if (cacheProvider == null)
{
throw new ArgumentNullException("cacheProvider");
}
if (cacheProvider is MemoryCache)
{
regionName = null;
}
this.cacheProvider = cacheProvider;
this.regionName = regionName;
}
}
Note: The CachedDataSource constructor receives an ObjectCache 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.
11. Next, add the following (highlighted) method to retrieve data from the cache.
(Code Snippet – BuildingAppsWithCachingService-Ex3-RetrieveCachedData method-CS)
C#
public abstract class CachedDataSource
{
...
protected T RetrieveCachedData<T>(string cacheKey, Func<T> fallbackFunction,
CacheItemPolicy cachePolicy) where T : class
{
Page | 37
var data = this.cacheProvider.Get(cacheKey, this.regionName) as T;
if (data != null)
{
return data;
}
data = fallbackFunction();
if (data != null)
{
this.cacheProvider.Add(new CacheItem(cacheKey, data, this.regionName),
cachePolicy);
}
return data;
}
}
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.
12. Finally, add a method to delete items from the cache.
(Code Snippet – BuildingAppsWithCachingService-Ex3-RemoveCachedData method-CS)
C#
public abstract class CachedDataSource
{
...
protected void RemoveCachedData(string cacheKey)
{
this.cacheProvider.Remove(cacheKey, this.regionName);
}
}
13. Save the CachedDataSource.cs file.
-
Task 2 – Building a Caching Product Catalog Repository
Once you have created an abstract base class for caching data sources, you now create a concrete
implementation that will provide a caching alternative for the ProductsRepository class. This task
Page | 38
represents the steps you would typically follow when creating a caching layer for your data access code
using the CachedDataSource class.
1. Inside the Services\Caching folder of the MVCAzureStore project, add a new class file named
CachedProductsRepository.cs.
2. In the new class file, append a namespace directive for System.Runtime.Caching.
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Caching;
...
3. Change the declaration for the CachedProductsRepository class to derive from both
CachedDataSource and IProductRepository, as shown (highlighted) below.
C#
public class CachedProductsRepository
: CachedDataSource, IProductRepository
{
}
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.
4. 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.
(Code Snippet – BuildingAppsWithCachingService-Ex3-CachedProductsRepository constructor-
CS)
C#
public class CachedProductsRepository : CachedDataSource, IProductRepository
{
private readonly IProductRepository repository;
public CachedProductsRepository(IProductRepository repository, ObjectCache
cacheProvider) :
base(cacheProvider, "Products")
{
Page | 39
this.repository = repository;
}
}
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.
5. Finally, fulfill the IProductRepository contract by implementing the GetProducts method, as
shown (highlighted) below.
(Code Snippet – BuildingAppsWithCachingService-Ex3-GetProducts method -CS)
C#
public class CachedProductsRepository : CachedDataSource, IProductRepository
{
...
public List<string> GetProducts()
{
return RetrieveCachedData(
"allproducts",
() => this.repository.GetProducts(),
new CacheItemPolicy { AbsoluteExpiration = DateTime.UtcNow.AddMinutes(1)
});
}
}
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 to set the expiration of the item to 1 minute.
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.
Task 3 – Creating a Data Source Factory Class
Page | 40
In this task, you 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.
1. Add a copy of the AppFabricCacheProvider.cs file located in the Assets folder of the lab to the
MVCAzureStore project and place it in its Services\Caching folder.
Note: The AppFabricCacheProvider class implements an ObjectCache that wraps the services
provided by the Windows Azure AppFabric Cache Service.
2. Inside the Services folder of the MVCAzureStore project, add a new class file named
DataSourceFactory.cs.
3. In the new class file, insert namespace directives for System.Configuration,
System.Runtime.Caching, and MVCAzureStore.Services.Caching.
(Code Snippet – BuildingAppsWithCachingService-Ex3-DataSourceFactory namespaces-CS)
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;
using System.Runtime.Caching;
using MVCAzureStore.Services.Caching;
4. 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.
(Code Snippet – BuildingAppsWithCachingService-Ex3-DataSourceFactory class constructor-CS)
C#
public class DataSourceFactory
{
private static readonly ObjectCache cacheProvider;
static DataSourceFactory()
{
string provider =
ConfigurationManager.AppSettings["CacheService.Provider"];
if (provider != null)
{
switch
(ConfigurationManager.AppSettings["CacheService.Provider"].ToUpperInvariant())
Page | 41
{
case "APPFABRIC":
cacheProvider = new AppFabricCacheProvider();
break;
case "INMEMORY":
cacheProvider = MemoryCache.Default;
break;
}
}
}
}
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 AppFabric Caching
and another one for the default in-memory cache provider offered by the .NET Framework 4.
5. Next, add the following property to return the configured cache service provider.
(Code Snippet – BuildingAppsWithCachingService-Ex3- CacheProvider property-CS)
C#
public class DataSourceFactory
{
...
public static ObjectCache CacheProvider
{
get { return cacheProvider; }
}
}
6. Finally, add a method to return an instance of the IProductRepository data source initialized
with the configured cache service provider.
(Code Snippet – BuildingAppsWithCachingService-Ex3-GetProductsRepository method-CS)
C#
public class DataSourceFactory
{
...
public static IProductRepository GetProductsRepository(bool enableCache)
{
var dataSource = new ProductsRepository();
if (enableCache && CacheProvider != null)
{
Page | 42
return new CachedProductsRepository(dataSource, cacheProvider);
}
return dataSource;
}
}
Task 4 – Configuring the Application for Caching
In this task, you 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 define the necessary
configuration settings to select a caching provider.
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.
C#
public class HomeController : Controller
{
...
public ActionResult Index()
{
bool enableCache = (bool)this.Session["EnableCache"];
// retrieve product catalog from repository and measure the elapsed time
Services.IProductRepository productRepository =
MVCAzureStore.Services.DataSourceFactory.GetProductsRepository(enableCache);
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
...
}
...
}
2. To configure the DataSourceFactory, open the Web.config file and insert the following
(highlighted) appSettings section, ensuring that you add the content after the configSections
element and as a direct child of the configuration element.
(Code Snippet – BuildingAppsWithCachingService-Ex3-Web.config appSettings section-CS)
XML
<configuration>
Page | 43
<configSections>
...
</configSections>
...
<appSettings>
<add key="CacheService.Provider" value="InMemory" />
</appSettings>
...
</configuration>
Note: If you host the application in a single node, the in-memory cache provider would be a
good choice.
3. Locate the dataCacheClient section and replace the [SERVICE-HOST-NAME] placeholder with
the name of the host for the Caching service endpoint that you provisioned earlier. For
example, for a Service URL http://your-namespace.cache.appfabriclabs.com /, set the name
attribute to your-namespace.cache.appfabriclabs.com. Replace the [AUTHORIZATION_INFO]
placeholder with the authorization token that you copied from the Cache Settings page.
4. Press F5 to build and test the enhanced caching implementation in the development fabric.
5. When you start the application, the cache is initially disabled. Click Yes in Use cache for product
data 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.
6. 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.
7. Now, in the Web.config file, locate the appSettings section and set the value of the
CacheService.Provider setting to AppFabric.
XML
<configuration>
...
<appSettings>
<add key="CacheService.Provider" value="AppFabric" />
</appSettings>
...
</configuration>
Page | 44
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 AppFabric Caching.
8. Save the Web.config file.
9. Suspend and Run the development fabric to restart and reload the configuration.
10. 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 AppFabric Caching
provider instead of the in-memory provider.
Summary
In this hands-on lab, you explored the use of the Windows Azure AppFabric 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 AppFabric and in particular, how to cache the results of
queries to a SQL Azure 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.