public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { var cacheKey = _keyGenerator.GenerateKey(input); var methodIsTask = input.MethodBase.IsGenericTask(); var returnType = input.MethodBase.GetReturnType(); var cachedResult = typeof(ICache).GetMethod("Get") .MakeGenericMethod(returnType) .Invoke(_cache, new object[] { cacheKey }); object unwrappedResult = null; var scheduleDurableRefresh = false; //Force cache miss for durable results that are behind if (cachedResult != null && AbsoluteExpiration != null) { var lastUpdate = (DateTime)CacheExtensions.GetLastUpdate((dynamic)cachedResult); var expires = (DateTime)CacheExtensions.GetExpires((dynamic)cachedResult); if (DateTime.UtcNow.Subtract(lastUpdate).TotalSeconds > MaximumStaleness.TotalSeconds) { if (RescheduleStale && DateTime.UtcNow.Add(Expiration) < expires) { scheduleDurableRefresh = true; } else { cachedResult = null; } } } // Nothing is cached for this method if (cachedResult == null) { // Get a new result var newResult = getNext()(input, getNext); //Do not cache exceptions if (newResult.Exception == null) { if (methodIsTask) { newResult.ReturnValue = (dynamic)typeof(CacheExtensions).GetMethod("AddToCacheAsync") .MakeGenericMethod(returnType) .Invoke(null, new object[] { _cache, newResult.ReturnValue, cacheKey, DateTime.UtcNow.Add(AbsoluteExpiration ?? Expiration) }); } else { typeof(CacheExtensions).GetMethod("AddToCache") .MakeGenericMethod(returnType) .Invoke(null, new object[] { _cache, newResult.ReturnValue, cacheKey, DateTime.UtcNow.Add(AbsoluteExpiration ?? Expiration) }); } //queue refresh if configured by the user scheduleDurableRefresh = AbsoluteExpiration != null; } unwrappedResult = newResult.ReturnValue; } else { if (methodIsTask) { var unwrappedResultMissingTask = CacheExtensions.Unwrap((dynamic)cachedResult); unwrappedResult = typeof(Task).GetMethod("FromResult") .MakeGenericMethod(input.MethodBase.GetGenericReturnTypeArguments()) .Invoke(null, new object[] { unwrappedResultMissingTask }); } else { unwrappedResult = CacheExtensions.Unwrap((dynamic)cachedResult); } } if (scheduleDurableRefresh) { var parameters = new object[input.Inputs.Count]; input.Inputs.CopyTo(parameters, 0); var queue = _container.Resolve <IDurableCacheQueue>(); queue.ScheduleRefresh(new DurableCacheRefreshEvent { AbsoluteExpiration = AbsoluteExpiration.Value, CacheName = _cacheName, Key = cacheKey, Method = new DurableMethod { DeclaringType = input.Target.GetType(), Interface = input.MethodBase.DeclaringType, Name = input.MethodBase.Name, Parameters = input.MethodBase.GetParameters() .Select(p => p.ParameterType).ToArray() }, Parameters = parameters, RefreshTime = Expiration, UtcLifetime = cachedResult != null ? (DateTime)CacheExtensions.GetExpires((dynamic)cachedResult) : DateTime.UtcNow.Add(AbsoluteExpiration.Value), }); } var arguments = new object[input.Inputs.Count]; input.Inputs.CopyTo(arguments, 0); return(new VirtualMethodReturn(input, unwrappedResult, arguments)); }