When using IMemoryCache
with an object
, TryGetValue
always miss. I am trying to have a tuple<string, object>
as the key, and a tuple<string, string>
works perfectly fine.
This code here always gets me a cache miss:
_cache.TryGetValue(("typeOfCache", query), out var something);
if(something == null) _cache.CreateEntry(("typeOfCache", query));
The object I'm using has lists of lists inside, not no dictionary/set (nothing that has a random ordering).
Is this a .net bug or am I doing something incorrectly?
MemoryCache
internally uses a ConcurrentDictionary<object, CacheEntry>
, which in turn uses the default comparer for the object
type, which performs equality comparisons based on the actual type's overrides of Object.Equals
and Object.GetHashCode
. In your case, your keys are ValueTuple<string, Query>
, whatever your Query
class is. ValueTuple<T1,T2>.Equals
evaluates to true if the components of the compared instance are of the same types as those of the current instance, and if the components are equal to those of the current instance, with equality being determined by the default equality comparer for each component.
Thus, how the equality comparison gets performed depends on the implementation of your Query
type. If this type does not override Equals
and GetHashCode
, nor implements IEquatable<T>
, then reference equality is performed, meaning that you only get equality when you pass in the same instance of the query. If you want to alter this behavior, you should extend your Query
class to implement IEquatable<Query>
.
I also found that CreateEntry
does not immediately add the new entry to the cache. .NET Core documentation is disappointingly sparse, so I haven't found the intended behavior; however, you can ensure that the entry is added by calling Set
instead.
Example:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Caching.Memory;
class Program
{
static void Main(string[] args)
{
var query1 = new Query { Parts = { new List<string> { "abc", "def", "ghi" } } };
var query2 = new Query { Parts = { new List<string> { "abc", "def", "ghi" } } };
var memoryCache = new MemoryCache(new MemoryCacheOptions());
memoryCache.Set(("typeOfCache", query1), new object());
var found = memoryCache.TryGetValue(("typeOfCache", query2), out var something);
Console.WriteLine(found);
}
public class Query : IEquatable<Query>
{
public List<List<string>> Parts { get; } = new List<List<string>>();
public bool Equals(Query other)
{
if (ReferenceEquals(this, other)) return true;
if (ReferenceEquals(other, null)) return false;
return this.Parts.Length == other.Parts.Length
&& this.Parts.Zip(other.Parts, (x, y) => x.SequenceEqual(y)).All(b => b);
}
public override bool Equals(object obj)
{
return Equals(obj as Query);
}
public override int GetHashCode()
{
return this.Parts.SelectMany(p => p).Take(10).Aggregate(17, (acc, p) => acc * 23 + p?.GetHashCode() ?? 0);
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With