Caching in Wcf

MemoryCache and multiple per call WCF services

1.the services are all hosted in the same app in IIS

the answer is yes if you are using MemoryCache.Default as your default cache object

From MSDN

This property always returns a reference to the default cache instance. For typical application scenarios, only one instance of MemoryCache is required.

you could use it like the following

ObjectCache cache = MemoryCache.Default;

Is it possible to configure it in the following way

<system.runtime.caching>
<memoryCache>
<namedCaches>
<add name="Default" physicalMemoryLimitPercentage="20"/>
</namedCaches>
</memoryCache>
</system.runtime.caching>

from your others services instance you can access your memory cache like the following

List<string> cacheKeys = MemoryCache.Default.Select(kvp => kvp.Key).ToList();

foreach (string cacheKey in cacheKeys)
MemoryCache.Default.Remove(cacheKey);

2.the services are hosted in different IIS applications on the same server

this will be a bit tricky but it will remains a valid option you can create a dedicated webservice for caching that can be used by others webservices using the netnamedPipeBinding given that are on the same server

How to add caching to my MVC Application and WCF Service

To cache data from WCF service, you should have a Cache layer first. Sample code:

using System.Runtime.Caching;

public class CacheManager
{
private static MemoryCache _cache = MemoryCache.Default;

public static void AddToCache<T>(string key, T value)
{
_cache[key] = value;
}

public static T GetFromCache<T>(string key)
{
return (T)_cache[key];
}

public static void RemoveFromCache(string key)
{
_cache.Remove(key);
}
}

Then use it in your data layer, eg:

public List<Top_100_Result> GetTopProductsByTypeName()
{
var products = CacheManager.GetFromCache<List<Top_100_Result>>("TOP_100_RESULT");

//Add to cache if not existed
if (products == null)
{
using (EmbraceEntities ctx = new EmbraceEntities())
{
var productObjects = ctx.Top_100(null);

products = new List<Top_100_Result>(productObjects.Distinct());

CacheManager.AddToCache<List<Top_100_Result>>("TOP_100_RESULT", products);
}
}

return products;
}

You should also clear cache to refresh the data as soon as the cache data becomes invalid.

CacheManager.RemoveFromCache("TOP_100_RESULT");

WCF channel Factory caching

This is a complex topic with a lot of details to go over, but here it goes.

First, as a general rule you should be caching a ChannelFactory and not an individual Channel. A ChannelFactory is expensive to construct as well as thread-safe so it is a great candidate for caching. A Channel is cheap to construct and it is generally recommended to only create channels on an as-needed basis and to close them as early as possible. Additionally, when you cache a Channel then you have to worry about it timing out which will cause it to fault which invalidates the entire benefit of caching it in the first place.

The article you linked to by Michele Leroux Bustamante is one of the best resources out there. As she states, there are differences to consider between Windows clients and server-side clients. Mostly only Windows clients benefit from caching as typically the credentials differ from thread to thread on server-side clients. For your typical Windows clients, there are two main options: Caching the references yourself or leveraging the MRU cache.

Leveraging the MRU cache: Essentially this means that you are letting Microsoft take the wheel. The ClientBase class will use an MRU cache for the internal ChannelFactory instance. The caching behavior is controlled via a CacheSetting property and by default caching will be disabled if any of the "security-sensitive" properties are accessed. ClientBase properties which will invalidate and remove a ChannelFactory from the MRU cache when accessed include the Endpoint, ClientCredentials or the ChannelFactory itself. There is a way to override this behavior by setting the CacheSettings property to CacheSettings.AlwaysOn. Additionally, if the Binding is run-time defined then the ChannelFactory is no longer a candidate for the MRU cache. See more details here.

Caching the references yourself: This means that you are going to keep a collection of ChannelFactory references yourself. The snippet you provide in your question uses this approach. The best approach I have ever seen and admittedly use a modified version of at work is by Darin Dimitrov via this related SO question. For those of us who like to have more fine-grained control over the caching mechanism then this is the approach to use. This is typically used when credentials must be set at run-time like is often required by internet services.

Quite similarly, client proxies can be cached to improve performance - Wenlong Dong has an article about this topic.

(Update) Server-side clients as noted before are quite limited in their options when it comes to ChannelFactory caching. For this brief discussion, we will assume that our deployment scenario looks like this:

Client -> Service A -> Service B

The most likely method to use in order to leverage ChannelFactory caching in this scenario is to cache the references yourself for the session between the Client and Service A. This way Service A does not have to construct a different ChannelFactory instance every time Service A needs to call into Service B. However, if the properties of the ChannelFactory need change for each call, then this is no longer going to be appropriate.

Of course this also holds if Service A is a Singleton and each call to the downstream service (Service B) does not require new credentials, but Singleton services have their own set of performance problems.

Caching WCF Service based on output

if (_inquiryview.ValidTest == "1")
{
HttpContext context = HttpContext.Current;
HttpCachePolicy cachePolicy = HttpContext.Current.Response.Cache;
cachePolicy.SetCacheability(HttpCacheability.ServerAndPrivate);
cachePolicy.SetExpires(DateTime.Now.AddDays(15));
cachePolicy.VaryByHeaders["Accept"] = true;
cachePolicy.VaryByHeaders["Accept-Charset"] = true;
cachePolicy.VaryByHeaders["Accept-Encoding"] = true;
cachePolicy.VaryByParams["*"] = true;
cachePolicy.SetValidUntilExpires(true);
}


Related Topics



Leave a reply



Submit