Geeks With Blogs
Rohit Gupta Engaging talk on Microsoft Technologies ....My Resume

Here are the steps:

I am using Windows Azure Caching preview on the cloud for caching purposes on Azure, which can be colocated Cache or a dedicated Cache Worker Role.

Note we have not used the Azure AppFabric cache which is available as a separate service from Azure and comes at a high cost.

I have an appSetting key that will be used to distinguish whether I will be using AppFabric Cache or System.Runtime.Caching.MemoryCache for my caching needs.

   1: var cacheToUse = ConfigurationManager.AppSettings["cacheToUse"] ?? "InMemoryCache";
   2: if (RoleEnvironment.IsAvailable || cacheToUse == "AppFabricCache")
   3: {
   4:     container.RegisterInstance(typeof(ObjectCache), new AppFabricCacheProvider(), new ContainerControlledLifetimeManager());
   5: }
   6: else
   7: {
   8:     container.RegisterInstance(typeof(ObjectCache), MemoryCache.Default, new ContainerControlledLifetimeManager());
   9: }

In the above code snippet… I am registering AppFabricCacheProvider as my Cache provider… if my appSetting states that I need to use On-Premise AppFabric Cache or the Azure Caching preview.

The code for AppFabricCacheProvider is listed at the bottom of this post.

Caching and MVC application on-premise

If we need to use the on-premise AppFabric Cache (when we are deploying the mvc app to self hosted datacenter or on-premise), here are the steps for setting up on-premise Cache.

First  in the MVC application, we need to add a nuget package (Server AppFabric Client) as show below:

image

On the Server where we will deploy this app, we need to install AppFabric Caching service.

Install the latest version of AppFabric which is AppFabric 1.1.

You can get AppFabric 1.1 from here:

http://www.microsoft.com/en-in/download/details.aspx?id=27115

If you are upgrading from AppFabric 1.0, please follow these steps to upgrade your AppFabric Caching service to 1.1:

http://msdn.microsoft.com/en-us/library/hh343304(v=azure.10).aspx

Please insure that your AppFabric server lists the following when you query it using powershell:

PS C:\Windows\system32> Get-CacheHost

HostName : CachePort   Service Name            Service Status Version Info
--------------------   ------------            -------------- ------------
ISQA.asl.mum.sst:22233 AppFabricCachingService UP             3 [3,3][1,3]

=====================

We also need to set permissions on the cache-cluster so that the MVC app can connect to it.

If we need to grant specific permissions, then we need to use the following powershell command to grant permissions to the ApplicationPoolIdentity account:

PS C:\Windows\system32> Grant-CacheAllowedClientAccount

cmdlet Grant-CacheAllowedClientAccount at command pipeline position 1
Supply values for the following parameters:
Account: "IIS AppPool\DefaultAppPool"

=======================================================

As an alternative we can configure the AppFabric Cache cluster to not look for permissions:

use the following command:

PS C:\Windows\system32> Set-CacheClusterSecurity

cmdlet Set-CacheClusterSecurity at command pipeline position 1
Supply values for the following parameters:
SecurityMode: None
ProtectionLevel: None

===============================================================

On the client (mvc app side) use the following in web.config:

   1: <dataCacheClients>
   2:   <dataCacheClient name="default">
   3:     <hosts>
   4:       <host name="localhost" cachePort="22233"/>
   5:     </hosts>
   6:     <securityProperties mode="None" protectionLevel="None" />
   7:   </dataCacheClient>
   8: </dataCacheClients>

After the Cache service has been up and running. we just need the right configuration in the web.config to be able to connect to it:

===================================================================================================================
   1: <configSections>
   2:   <section name="dataCacheClients" type="Microsoft.ApplicationServer.Caching.DataCacheClientsSection, Microsoft.ApplicationServer.Caching.Core" allowLocation="true" allowDefinition="Everywhere" />
   3:   <section name="AppFabricProviderSettings" type="NHibernate.Caches.AppFabric.AppFabricProviderSettings, NHibernate.Caches.AppFabric" />
   4: </configSections>
   5: <appSettings>
   6:   <add key="cacheToUse" value="AppFabricCache" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
   7: </appSettings>
   8: <dataCacheClients xdt:Transform="Replace">
   9:   <dataCacheClient name="default">
  10:     <hosts>
  11:       <host name="localhost" cachePort="22233"/>
  12:     </hosts>
  13:     <securityProperties mode="None" protectionLevel="None" />
  14:   </dataCacheClient>
  15: </dataCacheClients>

===================================================================================================================

We can configure the session state to use this caching provider.

   1: <sessionState xdt:Transform="Replace" mode="Custom" customProvider="AppFabricCacheSessionStoreProvider">
   2:   <providers>
   3:     <add name="AppFabricCacheSessionStoreProvider" type="Microsoft.Web.DistributedCache.DistributedCacheSessionStateStoreProvider, Microsoft.Web.DistributedCache" cacheName="default" useBlobMode="true" dataCacheClientName="default" applicationName="SCMProfitWeb" />
   4:   </providers>
   5: </sessionState>

We can also configure output cache to use this AppFabric Caching provider:

   1: <caching>
   2:   <outputCache xdt:Transform="Replace" defaultProvider="defaultProvider" >
   3:     <providers>
   4:       <add cacheName="default"
   5:            name="defaultProvider"
   6:            dataCacheClientName="default"
   7:            applicationName="SCMProfitWeb"
   8:            type= "Microsoft.Web.DistributedCache.DistributedCacheOutputCacheProvider, Microsoft.Web.DistributedCache" />
   9:     </providers>
  10:   </outputCache>
  11: </caching>

===================================================================================================================

Caching and MVC application on the cloud

Now the configuration steps for Azure Caching preview is very simple. (Caching and MVC application on the cloud)

There are no provisioning steps for setting up the Cache Cluster on the cloud. we only need to enable Caching preview in the Cloud project, or add a Caching worker role.

Once that is done we need to configure the web.config to use the AppFabric Cache:

   1: <dataCacheClients>
   2:   <dataCacheClient name="default">
   3:     <autoDiscover isEnabled="true" identifier="SCMProfitWeb" />
   4:     <localCache isEnabled="true" sync="TimeoutBased" objectCount="100000" ttlValue="300" />
   5:   </dataCacheClient>
   6: </dataCacheClients>
   7: <AppFabricProviderSettings CacheType="Named" NamedCacheTypeRegionName="nhibernate" NamedCachesMustExist="false" LockTimeout="30000" LocksRegionName="Locks" />

The steps for using Caching Preview as the caching provider for both session state and output cache is the same as shown above for on-premise Cache (isnt that cool…. note this is only possible with AppFabric 1.1)

===================================================================================================================

AppFabric Cache Provider implementation code:

===================================================================================================================

   1: /// <summary>
   2: /// Windows Azure AppFabric Cache implementation of ObjectCache
   3: /// </summary>
   4: public class AppFabricCacheProvider : ObjectCache, IDisposable
   5: {
   6:     private DataCacheFactory _cacheFactory;
   7:  
   8:     private const string RegionKeyTemplate = "{0}:{1}";
   9:  
  10:     /// <summary>
  11:     /// Initializes a new instance of the <see cref="AppFabricCacheProvider"/> class.
  12:     /// </summary>
  13:     public AppFabricCacheProvider()
  14:     {
  15:         _cacheFactory = new DataCacheFactory();
  16:     }
  17:  
  18:     public override string Name
  19:     {
  20:         get { return "AppFabricCacheService"; }
  21:     }
  22:  
  23:     public override DefaultCacheCapabilities DefaultCacheCapabilities
  24:     {
  25:         get { return DefaultCacheCapabilities.OutOfProcessProvider | DefaultCacheCapabilities.AbsoluteExpirations | DefaultCacheCapabilities.SlidingExpirations; }
  26:     }
  27:  
  28:     public override object this[string key]
  29:     {
  30:         get
  31:         {
  32:             return this.Get(key, null);
  33:         }
  34:  
  35:         set
  36:         {
  37:             throw new NotSupportedException();
  38:         }
  39:     }
  40:  
  41:     /// <summary>
  42:     /// When overridden in a derived class, inserts a cache entry into the cache, specifying a key and a value for the cache entry, and information about how the entry will be evicted.
  43:     /// </summary>
  44:     /// <param name="key">A unique identifier for the cache entry.</param>
  45:     /// <param name="value">The object to insert.</param>
  46:     /// <param name="policy">An object that contains eviction details for the cache entry. This object provides more options for eviction than a simple absolute expiration.</param>
  47:     /// <param name="regionName">Optional. A named region in the cache to which the cache entry can be added, if regions are implemented. The default value for the optional parameter is null.</param>
  48:     /// <returns>
  49:     /// If a cache entry with the same key exists, the specified cache entry's value; otherwise, null.
  50:     /// </returns>
  51:     public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null)
  52:     {
  53:         return this.AddOrGetExisting(new CacheItem(key, value, regionName), policy).Value;
  54:     }
  55:  
  56:     /// <summary>
  57:     /// When overridden in a derived class, inserts a cache entry into the cache, by using a key, an object for the cache entry, an absolute expiration value, and an optional region to add the cache into.
  58:     /// </summary>
  59:     /// <param name="key">A unique identifier for the cache entry.</param>
  60:     /// <param name="value">The object to insert.</param>
  61:     /// <param name="absoluteExpiration">The fixed date and time at which the cache entry will expire.</param>
  62:     /// <param name="regionName">Optional. A named region in the cache to which the cache entry can be added, if regions are implemented. The default value for the optional parameter is null.</param>
  63:     /// <returns>
  64:     /// If a cache entry with the same key exists, the specified cache entry's value; otherwise, null.
  65:     /// </returns>
  66:     public override object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
  67:     {
  68:         return this.AddOrGetExisting(new CacheItem(key, value, regionName), new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }).Value;
  69:     }
  70:  
  71:     /// <summary>
  72:     /// When overridden in a derived class, inserts the specified <see cref="T:System.Runtime.Caching.CacheItem"/> object into the cache, specifying information about how the entry will be evicted.
  73:     /// </summary>
  74:     /// <param name="value">The object to insert.</param>
  75:     /// <param name="policy">An object that contains eviction details for the cache entry. This object provides more options for eviction than a simple absolute expiration.</param>
  76:     /// <returns>
  77:     /// If a cache entry with the same key exists, the specified cache entry; otherwise, null.
  78:     /// </returns>
  79:     public override CacheItem AddOrGetExisting(CacheItem value, CacheItemPolicy policy)
  80:     {
  81:         var data = this.Get(value.Key, value.RegionName);
  82:         if (data != null)
  83:         {
  84:             return new CacheItem(value.Key, data, value.RegionName);
  85:         }
  86:  
  87:         this.Set(value, policy);
  88:         return value;
  89:     }
  90:  
  91:     /// <summary>
  92:     /// When overridden in a derived class, tries to insert a cache entry into the cache as a <see cref="T:System.Runtime.Caching.CacheItem"/> instance, and adds details about how the entry should be evicted.
  93:     /// </summary>
  94:     /// <param name="item">The object to add.</param>
  95:     /// <param name="policy">An object that contains eviction details for the cache entry. This object provides more options for eviction than a simple absolute expiration.</param>
  96:     /// <returns>
  97:     /// true if insertion succeeded, or false if there is an already an entry in the cache that has the same key as <paramref name="item"/>.
  98:     /// </returns>
  99:     public override bool Add(CacheItem item, CacheItemPolicy policy)
 100:     {
 101:         if (item.Value == null)
 102:             return false;
 103:  
 104:         if (policy != null)
 105:             return base.Add(item, policy);
 106:  
 107:         try
 108:         {
 109:             Set(item, null);
 110:         }
 111:         catch (Exception)
 112:         {
 113:             return false;
 114:         }
 115:         return true;
 116:     }
 117:  
 118:     /// <summary>
 119:     /// When overridden in a derived class, gets the specified cache entry from the cache as an object.
 120:     /// </summary>
 121:     /// <param name="key">A unique identifier for the cache entry to get.</param>
 122:     /// <param name="regionName">Optional. A named region in the cache to which the cache entry was added, if regions are implemented. The default value for the optional parameter is null.</param>
 123:     /// <returns>
 124:     /// The cache entry that is identified by <paramref name="key"/>.
 125:     /// </returns>
 126:     public override object Get(string key, string regionName = null)
 127:     {
 128:         try
 129:         {
 130:             return _cacheFactory.GetDefaultCache().Get(GetKey(key, regionName));
 131:         }
 132:         catch (DataCacheException ex)
 133:         {
 134:             if (ex.ErrorCode == DataCacheErrorCode.RetryLater)
 135:             {
 136:                 // temporal failure, ignore and continue
 137:                 return null;
 138:             }
 139:  
 140:             throw;
 141:         }
 142:     }
 143:  
 144:     /// <summary>
 145:     /// When overridden in a derived class, checks whether the cache entry already exists in the cache.
 146:     /// </summary>
 147:     /// <param name="key">A unique identifier for the cache entry.</param>
 148:     /// <param name="regionName">Optional. A named region in the cache where the cache can be found, if regions are implemented. The default value for the optional parameter is null.</param>
 149:     /// <returns>
 150:     /// true if the cache contains a cache entry with the same key value as <paramref name="key"/>; otherwise, false.
 151:     /// </returns>
 152:     public override bool Contains(string key, string regionName = null)
 153:     {
 154:         return this.Get(key, regionName) != null;
 155:     }
 156:  
 157:     /// <summary>
 158:     /// When overridden in a derived class, gets the specified cache entry from the cache as a <see cref="T:System.Runtime.Caching.CacheItem"/> instance.
 159:     /// </summary>
 160:     /// <param name="key">A unique identifier for the cache entry to get.</param>
 161:     /// <param name="regionName">Optional. A named region in the cache to which the cache was added, if regions are implemented. Because regions are not implemented in .NET Framework 4, the default is null.</param>
 162:     /// <returns>
 163:     /// The cache entry that is identified by <paramref name="key"/>.
 164:     /// </returns>
 165:     public override CacheItem GetCacheItem(string key, string regionName = null)
 166:     {
 167:         var data = this.Get(key, regionName);
 168:         if (data != null)
 169:         {
 170:             return new CacheItem(key, data, regionName); ;
 171:         }
 172:  
 173:         return null;
 174:     }
 175:  
 176:     /// <summary>
 177:     /// When overridden in a derived class, removes the cache entry from the cache.
 178:     /// </summary>
 179:     /// <param name="key">A unique identifier for the cache entry.</param>
 180:     /// <param name="regionName">Optional. A named region in the cache to which the cache entry was added, if regions are implemented. The default value for the optional parameter is null.</param>
 181:     /// <returns>
 182:     /// An object that represents the value of the removed cache entry that was specified by the key, or null if the specified entry was not found.
 183:     /// </returns>
 184:     public override object Remove(string key, string regionName = null)
 185:     {
 186:         var data = this.Get(key, regionName);
 187:         if (data != null)
 188:         {
 189:             this._cacheFactory.GetDefaultCache().Remove(GetKey(key, regionName));
 190:             return data;
 191:         }
 192:  
 193:         return null;
 194:     }
 195:  
 196:     /// <summary>
 197:     /// When overridden in a derived class, inserts a cache entry into the cache.
 198:     /// </summary>
 199:     /// <param name="key">A unique identifier for the cache entry.</param>
 200:     /// <param name="value">The object to insert.</param>
 201:     /// <param name="policy">An object that contains eviction details for the cache entry. This object provides more options for eviction than a simple absolute expiration.</param>
 202:     /// <param name="regionName">Optional. A named region in the cache to which the cache entry can be added, if regions are implemented. The default value for the optional parameter is null.</param>
 203:     public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null)
 204:     {
 205:         this.Set(new CacheItem(key, value, regionName), policy);
 206:     }
 207:  
 208:     /// <summary>
 209:     /// When overridden in a derived class, inserts a cache entry into the cache, specifying time-based expiration details.
 210:     /// </summary>
 211:     /// <param name="key">A unique identifier for the cache entry.</param>
 212:     /// <param name="value">The object to insert.</param>
 213:     /// <param name="absoluteExpiration">The fixed date and time at which the cache entry will expire.</param>
 214:     /// <param name="regionName">Optional. A named region in the cache to which the cache entry can be added, if regions are implemented. The default value for the optional parameter is null.</param>
 215:     public override void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
 216:     {
 217:         this.Set(new CacheItem(key, value, regionName), new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration });
 218:     }
 219:  
 220:     /// <summary>
 221:     /// When overridden in a derived class, inserts the cache entry into the cache as a <see cref="T:System.Runtime.Caching.CacheItem"/> instance, specifying information about how the entry will be evicted.
 222:     /// </summary>
 223:     /// <param name="item">The cache item to add.</param>
 224:     /// <param name="policy">An object that contains eviction details for the cache entry. This object provides more options for eviction than a simple absolute expiration.</param>
 225:     public override void Set(CacheItem item, CacheItemPolicy policy)
 226:     {
 227:         if (item.Value == null) return;
 228:  
 229:         try
 230:         {
 231:             TimeSpan timeout = TimeSpan.FromMinutes(1);
 232:             if (policy != null)
 233:             {
 234:                 if (policy.SlidingExpiration != TimeSpan.Zero)
 235:                 {
 236:                     timeout = policy.SlidingExpiration;
 237:                 }
 238:                 else
 239:                 {
 240:                     timeout = policy.AbsoluteExpiration - DateTime.UtcNow;
 241:                 }
 242:             }
 243:  
 244:             if (policy == null)
 245:                 this._cacheFactory.GetDefaultCache().Put(GetKey(item.Key, item.RegionName), item.Value);
 246:             else
 247:                 this._cacheFactory.GetDefaultCache().Put(GetKey(item.Key, item.RegionName), item.Value, timeout);
 248:         }
 249:         catch (DataCacheException ex)
 250:         {
 251:             if (ex.ErrorCode == DataCacheErrorCode.RetryLater)
 252:             {
 253:                 // temporal failure, ignore and continue
 254:                 return;
 255:             }
 256:  
 257:             throw;
 258:         }
 259:     }
 260:  
 261:     public override long GetCount(string regionName = null)
 262:     {
 263:         throw new NotSupportedException();
 264:     }
 265:  
 266:     protected override IEnumerator<System.Collections.Generic.KeyValuePair<string, object>> GetEnumerator()
 267:     {
 268:         throw new NotSupportedException();
 269:     }
 270:  
 271:     public override IDictionary<string, object> GetValues(System.Collections.Generic.IEnumerable<string> keys, string regionName = null)
 272:     {
 273:         throw new NotSupportedException();
 274:     }
 275:  
 276:     public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(System.Collections.Generic.IEnumerable<string> keys, string regionName = null)
 277:     {
 278:         throw new NotSupportedException();
 279:     }
 280:  
 281:     private static string GetKey(string key, string regionName)
 282:     {
 283:         if (string.IsNullOrWhiteSpace(regionName))
 284:         {
 285:             return key;
 286:         }
 287:         else
 288:         {
 289:             return string.Format(CultureInfo.InvariantCulture, RegionKeyTemplate, key, regionName);
 290:         }
 291:     }
 292:  
 293:     public void Dispose()
 294:     {
 295:         Dispose(true);
 296:         GC.SuppressFinalize(this);
 297:     }
 298:  
 299:     // The bulk of the clean-up code is implemented in Dispose(bool)
 300:     protected virtual void Dispose(bool disposing)
 301:     {
 302:         if (disposing)
 303:         {
 304:             // free managed resources
 305:             if (_cacheFactory != null)
 306:             {
 307:                 _cacheFactory.Dispose();
 308:                 _cacheFactory = null;
 309:             }
 310:         }
 311:     }
 312: }
Posted on Saturday, March 30, 2013 5:47 AM | Back to top


Comments on this post: Configure MVC app to use On-premise AppFabric Cache or Azure AppFabric Cache

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Rohit Gupta | Powered by: GeeksWithBlogs.net