/// <summary> /// Tries to get the result of the <see cref="FetchingExecutor{TReturn}"/> from the cache. If it can't it starts a new prefetch thread in /// the background. /// </summary> /// <param name="del"> /// A <see cref="FetchingExecutor{TReturn}"/> which will be prefetched by the class. /// </param> /// <param name="cacheTime"> /// The number of seconds of cache life as a <see cref="System.Int32"/>/ /// </param> /// <param name="prefetchCacheOptions"> /// A <see cref="PrefetchCacheOptions"/> flags, which specify the behavior of this cache. /// </param> /// <param name="args"> /// A series of <see cref="System.Object"/>s which represent the parameters upon which the <see cref="FetchingExecutor{TReturn}"/> /// operates. The same delegate with different parameters will be cached separately. /// </param> /// <returns> /// A TReturn, the result of the cache lookup. /// </returns> public static TReturn Get <TReturn>(FetchingExecutor <TReturn> del, int cacheTime, PrefetchCacheOptions prefetchCacheOptions, params object[] args) { if (cacheTime > 0) { StackTrace trace = new StackTrace(1, false); MethodBase caller = trace.GetFrame(0).GetMethod(); StringBuilder invokeSig = new StringBuilder(caller.DeclaringType.FullName.GetHashCode().ToString("X", CultureInfo.InvariantCulture)); invokeSig.Append(caller.Name.GetHashCode().ToString("X", CultureInfo.InvariantCulture)); invokeSig.AppendFormat(" {0}.{1}", caller.DeclaringType.FullName.Split(',')[0], caller.Name); foreach (object o in args) { invokeSig.AppendFormat(CultureInfo.InvariantCulture, " <{0}>", o.GetHashCode()); Console.WriteLine("{0} - {1}", o.GetType().FullName, o); } return(PrefetchCacheController.GetFromCache(invokeSig.ToString(), del, cacheTime, prefetchCacheOptions)); } return(del()); }
/// <summary> /// Creates a new instance of <see cref="PrefetchCacheAgent{TReturn}"/> /// </summary> /// <param name="name"> /// The name of this agent in cache /// </param> /// <param name="del"> /// A <see cref="FetchingExecutor{TReturn}"/> which will be executed repeatedly each interval /// to refresh the content. /// </param> /// <param name="cacheTime"> /// The length of time between attempts to refresh the content /// </param> public PrefetchCacheAgent(string name, FetchingExecutor <TReturn> del, int cacheTime) { #if DEBUG sig = name; Log.Instance.AddFormat("PrefetchCacheAgent - ctor invoked, id <{0}>, spawning...", sig); #endif myExecutor = del; this.cacheTime = cacheTime; this.name = name; content = new PrefetchCacheContent <TReturn>(); // Spawn Update Thread timerFunction = new PrefetchCacheTimer(10); timerFunction.Fetching += timerFunction_OnFetching; timerFunction.Idling += timerFunction_OnIdling; TimerCallback timerDelegate = timerFunction.Fetch; timer = new Timer(timerDelegate, null, 0, cacheTime * 1000); #if DEBUG Log.Instance.AddFormat("PrefetchCacheAgent - ...timer for '{0}' spawned!", sig); #endif }
internal static TReturn GetFromCache <TReturn>(string cacheName, FetchingExecutor <TReturn> del, int cacheTime, PrefetchCacheOptions prefetchCacheOptions) { if (!agents.ContainsKey(cacheName)) { lock (agents) { if (!agents.ContainsKey(cacheName)) { #if DEBUG Log.Instance.AddFormat("GetFromCache: Method '{0}', Creating Agent ", cacheName); #endif agents[cacheName] = new PrefetchCacheAgent <TReturn>(cacheName, del, cacheTime); agents[cacheName].Idling += agent_OnIdling; } } } IPrefetchCacheAgent agent = agents[cacheName]; // If we don't have the content in cache, we either return null or wait for it to be retrieved if (!agent.HasContent) { // If we can, we return null and don't keep the caller waiting if ((prefetchCacheOptions & PrefetchCacheOptions.AllowNoResult) != 0) { #if DEBUG Log.Instance.AddFormat("GetFromCache: Method '{0}', Returning null (allowed)", cacheName); #endif return(default(TReturn)); //return new ReturnMessage(null, null, 0, methodMessage.LogicalCallContext, methodMessage); } #if DEBUG Log.Instance.Add("Before wait for content (null check)"); #endif // We wait for the agent to retrieve the content agent.WaitForNotNullContent(10000); // If we, yet again, don't have content here, we must return an exception if (!agent.HasContent) { //return new ReturnMessage( throw new ContentNullException("Could not retrieve content from cache.", agent.Exception); //, methodMessage); } //return agent.Content.ReturnMessage; } // If caller doesn't care about expired items, we just give what we have // Or, if the content is up-to-date, we return it if ((prefetchCacheOptions & PrefetchCacheOptions.AllowExpired) != 0 || agent.Content.ExpiryDate >= DateTime.Now) { #if DEBUG Log.Instance.AddFormat("GetFromCache: Method '{0}', Returning unchecked expiry (allowed)", cacheName); #endif return((TReturn)agent.Content.ReturnMessage); } // The content must be up-to-date, but it's old. // We wait for new content #if DEBUG Log.Instance.AddFormat("Before wait for content (expiry check) {0:s:fffff}, {1:s:fffff}", agent.Content.ExpiryDate, DateTime.Now); #endif agent.WaitForFreshContent(10000); #if DEBUG Log.Instance.AddFormat("After wait for content (expiry check) {0:s:fffff}, {1:s:fffff}", agent.Content.ExpiryDate, DateTime.Now); #endif // If the content is expired we are DOOMED and we must return an exception if (agent.Content.ExpiryDate < DateTime.Now) { //return new ReturnMessage( throw new ContentExpiredException("Content in cache has expired.", agent.Exception); //, methodMessage); } #if DEBUG Log.Instance.AddFormat("GetFromCache: Method '{0}', Returning real", cacheName); #endif // All is well, return the content. return((TReturn)agent.Content.ReturnMessage); }