I want to be able to decorate WCF services and endpoints like so:
[CacheBehavior]
public class MyService
{ }
When an operation is called, I want to be able to lookup the request URL as a cache key, and if a cached response is found then return the raw response immediately by fetching the preformatted JSON string from my cache. By doing so, I'd avoid invoking the actual endpoint method.
When an operation is not found in the cache, I want to execute some logic when the response is returned and write the raw response (JSON) to my cache with the same cache key.
I've looked into samples at https://github.com/dotnet/samples/tree/main/framework/wcf and have tried using ChannelMessageInterceptor, ChannelDispatcherBase, IDispatchMessageInspector, and IOperationInvoker but none of these seem to support this use case. Unless I'm missing something.
I was able to use IOperationInvoker
for this use case, with the only caveat that it's not able to cache the pre-serialized response since it expects an object to be returned. So I will have to serialize/deserialize the response when caching or fetching from the cache.
Mock code with an in memory cache:
public class CacheOperationInvoker : IOperationInvoker
{
private static readonly Dictionary<string, object> _cache = new Dictionary<string, object>();
private readonly IOperationInvoker _invoker;
public bool IsSynchronous => true;
public CacheOperationInvoker(IOperationInvoker baseInvoker)
{
this._invoker = baseInvoker;
}
public object[] AllocateInputs()
{
return _invoker.AllocateInputs();
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
var operationContext = WebOperationContext.Current;
var cacheKey = operationContext?.IncomingRequest.UriTemplateMatch.RequestUri.OriginalString;
if (_cache.ContainsKey(cacheKey))
{
outputs = new object[] { };
return _cache[cacheKey];
}
var result = _invoker.Invoke(instance, inputs, out outputs);
_cache[cacheKey] = result;
return result;
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
throw new Exception("The operation invoker is not asynchronous.");
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
throw new Exception("The operation invoker is not asynchronous.");
}
}
Otherwise, the part I was missing was the need to use OperationContext.Current
or WebOperationContext.Current
to access additional context from the incoming request, since it's not provided directly to IOperationInvoker
.