Asp.Net Core:在缓存服务上,我应该使用AddScoped还是AddSingleton?

7
在一个asp.net core应用程序中,我有一个依赖注入的缓存服务,它实质上是内置MemoryCache的包装器。
这里是被缓存的示例类,它包含了一些枚举列表,这些枚举始终被Web应用程序使用:
public class GetEnums
{
    public List<MyEnum1> Ones { get; set; }
    public List<MyEnum2> Twos { get; set; }
}

下面是我缓存服务类的一个示例方法调用,它只是获取 GetEnums 类:

以下是缓存服务类的方法示例,它仅检索 GetEnums 类:

public class CacheService: ICacheService
{
    private IMemoryCache MemoryCache {get;set;}
    public CacheService(IMemoryCache memoryCache)
    {
        MemoryCache = memoryCache;
    }
    public GetEnums GetEnums()
    {
        if (MemoryCache.TryGetValue("GetEnums", out GetEnums getEnums))
            return getEnums;

        getEnums = MyRepository.GetEnums();
        MemoryCache.Set(CacheKeys.GetEnums, getEnums, _enumMemoryCacheEntryOptions);

        return getEnums;
    }
}

在我的ConfigureServices方法中,我想将这个类作为依赖注入服务。我应该使用AddScoped还是AddSingleton?也就是说,我应该这样做吗?
services.AddScoped(<ICacheService,CacheService>);

还是这个呢?

services.AddSingleton(<ICacheService,CacheService>);

我有两个问题。

第一个问题,如果我选择使用AddScoped:我猜想由于我的缓存服务类仅仅是一个在MemoryCache外面的包装,唯一的不同之处就是每次Web请求都会使用轻微的开销创建一个缓存服务对象(使用AddScoped),而不是应用程序中一个实例(使用AddSingleton)。我猜测,如果我使用AddScoped,.Net运行时不会创建单独的MemoryCache实例。

第二个问题,如果我选择使用AddSingleton,我是否需要在我的缓存服务的每个方法周围添加“lock”语句来管理“MemoryCache.Set”调用,如下:

    private readonly object _cachelock = new ();
    public GetEnums GetEnums()
    {
        if (MemoryCache.TryGetValue(CacheKeys.GetEnums, out GetEnums getEnums))
            return getEnums;

        lock(_cachelock)
        {
            if (MemoryCache.TryGetValue(CacheKeys.GetEnums, out getEnums))
                return getEnums;

            getEnums = MyRepository.GetEnums();
            MemoryCache.Set(CacheKeys.GetEnums, getEnums, _enumMemoryCacheEntryOptions);
        }

        return getEnums;
    }

1
使用 AddScoped 对于这个有什么意义? - Ids van der Zee
没有“点”@IdsvanderZee,我刚接触Core,想在前进的过程中充实最佳模式,我需要了解这是如何工作的。 - Tom Regan
1个回答

11
当您使用services.AddMemoryCache()注册内存缓存时,它被添加为单例模式,因此注册服务为单例模式似乎是合理的。

源代码

MemoryCache也是线程安全的,因此没有必要添加锁定。如果对给定键进行多次调用Set(),它将简单地更新现有值。

MemoryCache文档


感谢 @RichMercer。我知道MemoryCache是线程安全的。我想知道是否需要一个“lock”语句来防止可能浪费的多次填充缓存对象(例如4个线程发现缓存项为null,因此所有4个线程都调用方法来设置它)。我的问题是,我需要担心这个吗? - Tom Regan
个人而言,我不会添加锁定,因为你不太可能看到任何有意义的性能提升。 - RichMercer
2
对于任何遇到这个答案并想了解有关services.AddMemoryCache的人,大多数情况下不需要显式调用services.AddMemoryCache()。例如,如果你已经在调用services.AddMvc()或services.AddRazorPages(),那么该调用会为您完成。 - Tom Regan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接