On the native interpolation mode of MemoryCache

Posted by Runnion on Thu, 30 Dec 2021 23:20:13 +0100

. NET runtime has built-in common cache module: MemoryCache

The standard MemoryCache exposes the following properties and methods:

public int Count { get; }
public void Compact(double percentage);
public ICacheEntry CreateEntry(object key);
public void Dispose();
public void Remove(object key);
public bool TryGetValue(object key, out object result);
protected virtual void Dispose(bool disposing);

However, if you use the normal mode to interpolate / obtain values, unexpected situations may occur.

The general code is as follows:

var s = new MemoryCache(new MemoryCacheOptions { });
var entry = s.CreateEntry("WeChatID");
entry.Value = "Lean coder";

var f =  s.TryGetValue("WeChatID",out  object obj);

Console.WriteLine(f);
Console.WriteLine(obj);

The following results will be output:

Isn't it a surprise.

However, viewers generally do not use the native method of MemoryCache, but use the method in the same namespace
Extension method Set.

var s = new MemoryCache(new MemoryCacheOptions { });
s.Set("WeChatID", "Lean coder");
var f = s.TryGetValue("WeChatID", out object obj);

Console.WriteLine(f);
Console.WriteLine(obj);

This will output correctly.

Extension class source code have a look

 public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value)
 {
      using ICacheEntry entry = cache.CreateEntry(key);
      entry.Value = value;
      return value;
}

The difference between the extension method and the native method lies in the using keyword (it also shows that the CacheEntry inherits from the IDisposable interface).

Continue to trace the Dispose method implemented by CacheEntry:

        public void Dispose()
        {
            if (!_state.IsDisposed)
            {
                _state.IsDisposed = true;

                if (_cache.TrackLinkedCacheEntries)
                {
                    CacheEntryHelper.ExitScope(this, _previous);
                }

                // Don't commit or propagate options if the CacheEntry Value was never set.
                // We assume an exception occurred causing the caller to not set the Value successfully,
                // so don't use this entry.
                if (_state.IsValueSet)
                {
                    _cache.SetEntry(this);

                    if (_previous != null && CanPropagateOptions())
                    {
                        PropagateOptions(_previous);
                    }
                }

                _previous = null; // we don't want to root unnecessary objects
            }
        }

Pay attention to the_ cache.SetEntry(this) indicates that MemoryCache Insert cache entries into the underlying concurrentdictionary < object, cacheentry > collection,

To sum up: the cache item CacheEntry needs to be disposed before it can be inserted into the MemoeyCache.

What kind of design pattern is this?
Isn't the IDisposable interface used to release resources?
Why use the Dispose method to interpolate to MemoryCache?
Can't you use an explicit Commit method?

This is also available on Github issue discussion , since 2017, some leaders have questioned that this is an anti-human design idea. The official has maintained it until now in order not to introduce Break Change.

Based on this situation, if we use the native interpolation method of MemoryCache, we need to:

 var s = new MemoryCache(new MemoryCacheOptions { });
 using (var entry = s.CreateEntry("WeChatID"))
 {
      entry.Value = "Lean coder";
 }
 var f = s.TryGetValue("WeChatID", out object obj);
 ...

Try not to use the using syntax without braces introduced by C#8.0

 using var entry = s.CreateEntry("WeChatID");
 entry.Value = "Lean coder";
            
 var f = s.TryGetValue("WeChatID", out object obj);
 ...

This syntax that does not explicitly specify the scope of using will not execute the Dispose method until the end of the function, resulting in that the cache item has not been inserted when TryGetValue is executed!!!

Last
  1. The implementation process of MemoryCache interpolation is very wonderful
  2. Try to use the use syntax with explicit braces. The use syntax without braces introduced by C#8.0 is always at the end of the function, which will be misleading.

Topics: .NET