/// <inheritdoc /> public async Task <CacheResult <TValue> > GetOrCreateAsync(string key, Func <ICacheEntryContext, Task <TValue> > factory) { using var keyLock = await _keys .GetOrAdd(key, k => new SemaphoreSlim(1)) .WaitAsyncAndGetLockReleaser(); CacheItem <TValue> cacheItem; CacheHitMiss cacheHitMiss = CacheHitMiss.Miss; bool isCached = false; if (_memoryCache.TryGetValue(key, out object result)) { // from cache cacheItem = (CacheItem <TValue>)result; cacheHitMiss = CacheHitMiss.Hit; isCached = true; } else { // add new to cache var cacheEntry = _memoryCache.CreateEntry(key); var cacheEntryContext = new CacheEntryContext(_cacheSectionDescriptor, cacheEntry); var sw = Stopwatch.StartNew(); try { TValue value = default; Message?message = null; bool? shouldCache = null; try { // Create value with factory value = await factory(cacheEntryContext); // Optionally can check whether value is valid and get error message if (Settings.Validate != null) { var validationContext = new ValidationContext <TValue>(_cacheSectionDescriptor, value, cacheEntryContext.Metadata); Settings.Validate(validationContext); message = validationContext.Error; shouldCache = validationContext.ShouldCache; } } catch (Exception e) { if (Settings.HandleError != null) { var errorHandleContext = new ErrorHandleContext <TValue>(_cacheSectionDescriptor, e, cacheEntryContext.Metadata) { Error = CreateErrorMessage(), Value = default,
/// <summary> /// Initializes a new instance of the <see cref="CacheResult{TValue}"/> struct. /// </summary> public CacheResult( ICacheSectionDescriptor <TValue> cacheSection, string key, TValue value, Message?error, IPropertyContainer metadata, CacheHitMiss hitMiss, bool isCached) { SectionName = cacheSection.SectionName; Settings = cacheSection.CacheSettings; Key = key; Value = value; Error = error; Metadata = metadata; HitMiss = hitMiss; IsCached = isCached; }