using System; using System.Runtime.ExceptionServices; namespace Umbraco.Core.Cache { public static class SafeLazy { // an object that represent a value that has not been created yet internal static readonly object ValueNotCreated = new object(); public static Lazy GetSafeLazy(Func getCacheItem) { // try to generate the value and if it fails, // wrap in an ExceptionHolder - would be much simpler // to just use lazy.IsValueFaulted alas that field is // internal return new Lazy(() => { try { return getCacheItem(); } catch (Exception e) { return new ExceptionHolder(ExceptionDispatchInfo.Capture(e)); } }); } public static object GetSafeLazyValue(Lazy lazy, bool onlyIfValueIsCreated = false) { // if onlyIfValueIsCreated, do not trigger value creation // must return something, though, to differentiate from null values if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated; // if execution has thrown then lazy.IsValueCreated is false // and lazy.IsValueFaulted is true (but internal) so we use our // own exception holder (see Lazy source code) to return null if (lazy.Value is ExceptionHolder) return null; // we have a value and execution has not thrown so returning // here does not throw - unless we're re-entering, take care of it try { return lazy.Value; } catch (InvalidOperationException e) { throw new InvalidOperationException("The method that computes a value for the cache has tried to read that value from the cache.", e); } } public class ExceptionHolder { public ExceptionHolder(ExceptionDispatchInfo e) { Exception = e; } public ExceptionDispatchInfo Exception { get; } } } }