簡體   English   中英

如何在C#中緩存具有多個鍵的對象

[英]How to cache an object with multiple keys in C#

我的模型在 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);
    }
}

使用Microsoft.Extensions.Caching.Memory.IMemoryCache緩存對GetById調用和對GetByName調用的最佳方法是什么?

目前的解決方案:

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 從緩存中刪除它,但它仍然會與另一個鍵一起存在。 更新實體也有類似的問題。

有沒有比在緩存中兩次保存對象更好的解決方案?

在您的情況下,我會以不同的方式緩存,基本上,我會將緩存的條目保存在緩存中的列表中,然后檢索列表並在那里搜索。

如果您認為該列表太大而無法搜索,那么出於性能原因,您可能需要進行一些分區。

一般示例如下。

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

內存緩存將緩存項存儲為鍵值對,因此我們可以充分利用這一點。

所以不要這樣做:

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

我沒有測試上面的代碼,我只是想給你一些見解,可以引導你達到你想要的結果。

不確定是否有比在緩存中存儲對象兩次更好的解決方案,但有一個簡單的刪除/更新解決方案。 首先,通過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}");
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM