Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在C中缓存具有多个键的对象#_C#_Asp.net Core_Caching_.net Core_Memorycache - Fatal编程技术网

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

C# 如何在C中缓存具有多个键的对象#,c#,asp.net-core,caching,.net-core,memorycache,C#,Asp.net Core,Caching,.net Core,Memorycache,我有两个或更多属性中唯一的模型。例如,类实体的对象在名称和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(i

我有两个或更多属性中唯一的模型。例如,类
实体
的对象在名称和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
    }
}
公共类实体报告
{
公共实体GetById(int-id)
{
List entities=Cache.GetOrCreate($“entities”,cacheEntry=>
{
//创建实体的空列表
返回新列表();
});
//寻找实体
var entity=entities.Where(e=>e.id==id).FirstOrDefault();
//如果没有,则将其添加到缓存列表中
if(实体==null)
{
entity=db.GetById(id);
实体。添加(实体)
//更新缓存
Cache.Set($“实体”,实体);
}
返回实体;
}
公共实体GetByName(字符串名称)
{
//身份证也是一样
}
public void RemoveById(int-id)
{
//加载列表,删除项并更新缓存
}
}
在任何其他情况下,您都需要用某种逻辑来包装您的实现。可能是多键字典,或者某种数据结构来保留历史并进行自定义清理。但是没有现成的东西

通过将实体列表提取到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); }
}
公共列表实体
{
get{return Cache.GetOrCreate($“entities”,cacheEntry=>
{
//创建实体的空列表
返回新列表();
}); 
}
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);
}
现在,当您使用
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}");
        }
    }
}

但使用这种方法,如何使缓存的实体过期?如果我为缓存设置了一个大小限制,那么当达到该限制时,整个列表将被删除,并且无法为单个实体设置过期时间。事实上,没有。应在整个列表上设置过期时间。因此,根据您的实现,可能您应该为不同的用户创建不同的列表,并根据每个用户过期。这就是为什么我要提到分区来处理这些情况。另一种方法是根据项目的引用保存项目日志,并逐个删除<代码>字典实体映射
。将某个内容添加到缓存时,可以将该键添加到字符串列表中,如“entityMap[entity].add(key)”。当你删除时,你会检索密钥并删除所有内容。但是当缓存增长时,这不是一个问题吗
FirstOrDefault
值。包含执行线性搜索,因此比通过键(id)访问要慢得多。@Gurgeller
字典
经过优化以通过键获取值,因此反向操作会消耗一些性能,尤其是在大数据中,因此,如果使用大型缓存,我建议构建一个自定义解决方案来搜索缓存中的值(而不是键)。(或许可以构建自己的字典)。
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}");
        }
    }
}