.Net MemoryCache отсутствует при использовании объектов в качестве ключей - PullRequest
0 голосов
/ 04 января 2019

При использовании IMemoryCache с object, TryGetValue всегда отсутствует. Я пытаюсь получить ключ tuple<string, object>, а ключ tuple<string, string> работает отлично.

Этот код всегда дает мне промах кэша:

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

Используемый мной объект имеет списки списков внутри, а не словарь / набор (ничего, что имеет случайный порядок).

Это ошибка .net или я что-то делаю неправильно?

1 Ответ

0 голосов
/ 04 января 2019

MemoryCache внутренне использует ConcurrentDictionary<object, CacheEntry>, который, в свою очередь, использует компаратор по умолчанию для типа object, который выполняет сравнение на равенство на основе фактических переопределений типа Object.Equals и * 1007. *. В вашем случае ваши ключи ValueTuple<string, Query>, независимо от того, какой у вас класс Query. ValueTuple<T1,T2>.Equals оценивается как true, если компоненты сравниваемого экземпляра относятся к тем же типам, что и компоненты текущего экземпляра, и если компоненты равны компонентам текущего экземпляра, причем равенство определяется средство сравнения по умолчанию для каждого компонента.

Таким образом, то, как выполняется сравнение на равенство, зависит от реализации вашего Query типа. Если этот тип не переопределяет Equals и GetHashCode, а также не реализует 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);
        }
    }
}    
...