[英]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.