使用对象作为键时的.Net MemoryCache丢失

时间:2019-01-04 19:34:13

标签: c# .net caching memorycache

IMemoryCacheobject一起使用时,TryGetValue总是会丢失。我正在尝试将tuple<string, object>作为密钥,而tuple<string, string>的效果很好。

此代码始终使我无法缓存:

_cache.TryGetValue(("typeOfCache", query), out var something);
if(something == null) _cache.CreateEntry(("typeOfCache", query));

我正在使用的对象内部具有列表列表,而不是没有字典/集合(没有随机排序的东西)。

这是.NET错误还是我做错了什么?

1 个答案:

答案 0 :(得分:3)

MemoryCache内部使用ConcurrentDictionary<object, CacheEntry>,而object类型使用默认比较器,该比较器根据实际类型对Object.EqualsObject.GetHashCode。在您的情况下,无论您的ValueTuple<string, Query>类是什么,键都是Query。如果比较实例的组件与当前实例的组件具有相同的类型,并且组件与当前实例的组件相同,并且由默认的相等比较器确定是否相等,则ValueTuple<T1,T2>.Equals的计算结果为true。每个组件。

因此,相等比较的执行方式取决于您Query类型的实现。如果此类型没有覆盖EqualsGetHashCode,也没有实现IEquatable<T>,则将执行引用相等,这意味着只有在传递相同的查询实例时,您才能获得相等。如果要更改此行为,则应扩展Query类以实现IEquatable<Query>

我还发现CreateEntry不会立即将新条目添加到缓存中。 .NET Core文档非常稀疏,因此我没有找到预期的行为。但是,您可以通过调用Set来确保添加了该条目。

示例:

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);
        }
    }
}