如何在C#中缓存具有多个键的对象

问题描述 投票:0回答:3

我的模型在 2 个或更多属性中是唯一的。例如,

Entity
类的对象在名称和 ID 上都是唯一的。

public class Entity
{
    public int Id { get; set; }
    public string Name { get; set; }
}

我有一个模型存储库:

public class EntityRepository
{
    ...
    public Entity GetById(int id)
    {
        return db.GetById(id);
    }
    public Entity GetByName(string name)
    {
        return db.GetByName(name);
    }
}

使用

GetById
缓存对
GetByName
的调用和对
Microsoft.Extensions.Caching.Memory.IMemoryCache
的调用的最佳方法是什么?

目前的解决方案:

public class EntityRepository
{
    ...
    public Entity GetById(int id)
    {
        return Cache.GetOrCreate($"id:{id}", cacheEntry =>
        {
            return db.GetById(id);
        });
    }
    public Entity GetByName(string name)
    {
        return Cache.GetOrCreate($"name:{name}", cacheEntry =>
        {
            return db.GetByName(name);
        });
    }
    public void RemoveById(int id)
    {
        db.RemoveById(id);
        Cache.Remove($"id:{id}");
    }
}

这里的问题是,如果我通过 ID 删除一个实体,我可以通过 ID 将其从缓存中删除,但它仍然会与另一个键一起存在。更新实体也存在类似的问题。

有没有比在缓存中保存对象两次更好的解决方案?

c# asp.net-core caching .net-core memorycache
3个回答
0
投票

在您的情况下,我会以不同的方式进行缓存,基本上,我会将缓存的条目保留在缓存中的列表中,然后检索列表并在那里进行搜索。

如果您认为列表太大而无法搜索,那么出于性能原因您可能需要进行一些分区。

一般示例如下。

public class EntityRepository
{

    public Entity GetById(int id)
    {
        List<Entity> entities = Cache.GetOrCreate($"entities", cacheEntry =>
        {
            // Create an empty list of entities
            return new List<Entity>();
        });

        // Look for the entity
        var entity = entities.Where(e => e.id == id).FirstOrDefault();
        // if not there, then add it to the cached list
        if (entity == null)
        {
            entity = db.GetById(id);
            entities.Add(entity)
            // Update the cache
            Cache.Set($"entities", entities);
        }
        return entity;
    }
    public Entity GetByName(string name)
    {
        // Same thing with id    
    }
    public void RemoveById(int id)
    {
        // load the list, remove item and update cache
    }
}

在任何其他情况下,您都需要使用某种逻辑来包装您的实现。也许是多键字典,或者某种数据结构来保留历史记录并进行自定义清理。但没有任何开箱即用的东西。

您还可以通过将实体列表提取到 getter 和 setter 中来简化代码和重复,如下所示:

public List<Entity> Entities
{
    get { return Cache.GetOrCreate($"entities", cacheEntry =>
                 {
                     // Create an empty list of entities
                     return new List<Entity>();
                 }); 
    }
    set { Cache.Set($"entities", value); }
}

0
投票

内存缓存将缓存项存储为键值对,因此我们可以利用这一点。

所以不要这样做:

public Entity GetById(int id)
{
    return Cache.GetOrCreate($"id:{id}", cacheEntry =>
    {
        return db.GetById(id);
    });
}

你可以做这样的事情:

public Entity GetById(int id)
{
    string _name = string.Empty;

    if (!_cache.TryGetValue(id, out _name))
    {
        var _entity = db.GetById(id);

        _cache.Set(_entity.Id, _entity.Name);
    }

    return _cache.Get<Entity>(id);
}

在获取时,它会检查缓存,如果不存在,然后它会从源中获取值并将其存储在缓存中,然后返回缓存。

现在,由于您还需要检查

GetByName
方法上的值而不是键,因此您可以将缓存转换为字典,并使用
Linq
通过值检索键。

    public Entity GetByName(string name)
    {
        int id;

        var dic = _cache as IDictionary<int, string>;

        if (!dic.Values.Contains(name))
        {
            var _entity = db.GetByName(name);

            _cache.Set(_entity.Id, _entity.Name);

            id = _entity.Id;
        }
        else
        {
            id = dic.FirstOrDefault(x => x.Value == name).Key;
        }

        return _cache.Get<Entity>(id);
    }

现在,当您使用

RemoveById
时,您只需传递 id :

public void RemoveById(int id)
{
    db.RemoveById(id);
    _cache.Remove(id);
}

我没有测试上面的代码,我只是想给你一些见解,可以引导你得到你想要的结果。


0
投票

不确定是否有比在缓存中存储对象两次更好的解决方案,但有一个简单的删除/更新解决方案。首先,通过

Id
获取对象,然后通过已知属性值删除其所有缓存条目

public class EntityRepository
{
    public Entity GetById(int id)
    {
        return Cache.GetOrCreate($"id:{id}", cacheEntry =>
        {
            return db.GetById(id);
        });
    }
    public Entity GetByName(string name)
    {
        return Cache.GetOrCreate($"name:{name}", cacheEntry =>
        {
            return db.GetByName(name);
        });
    }
    public void RemoveById(int id)
    {
        db.RemoveById(id);
        if (Cache.TryGetValue(id, out Entity entity))
        {
            Cache.Remove($"id:{entity.Id}");
            Cache.Remove($"name:{entity.Name}");
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.