using System.Collections.Concurrent;

namespace Dpz.Core.Service;

[Obsolete("请使用 Dpz.Core.SourceGenerator.Attributes.CacheAttribute 配合源生成缓存装饰器。")]
public abstract class AbstractCacheService(IFusionCache fusionCache) : ICacheService
{
    private static readonly ConcurrentDictionary<Type, string> PrefixKeyCacheSets = new();

    protected virtual string CachePrefixKey =>
        PrefixKeyCacheSets.GetOrAdd(
            GetType(),
            type =>
                type.FullName
                ?? throw new InvalidOperationException(
                    $"The type {GetType().Name} does not have a FullName."
                )
        );

    protected abstract TimeSpan CacheDefaultExpiration { get; }

    public virtual string BuildCacheKey(string method, object? parameters = null) =>
        GenerateCacheKey.Build(CachePrefixKey, method, parameters);

    public virtual async Task<T> GetOrSetCacheAsync<T>(
        string methodName,
        Func<FusionCacheFactoryExecutionContext<T>, CancellationToken, Task<T>> factory,
        object? parameters = null,
        Action<FusionCacheEntryOptions>? setupAction = null,
        CancellationToken cancellationToken = default
    )
    {
        var cacheKey = BuildCacheKey(methodName, parameters);
        return await fusionCache.GetOrSetAsync<T>(
            cacheKey,
            async (context, factoryCancellationToken) =>
            {
                context.Tags = [CachePrefixKey, CachePrefixKey + methodName];
                return await factory(context, factoryCancellationToken);
            },
            setupAction ?? (options => options.SetDuration(CacheDefaultExpiration)),
            token: cancellationToken
        );
    }

    public virtual async ValueTask<MaybeValue<T>> TryGetFromCacheAsync<T>(
        string methodName,
        object? parameters = null,
        CancellationToken cancellationToken = default
    )
    {
        var cacheKey = BuildCacheKey(methodName, parameters);
        return await fusionCache.TryGetAsync<T>(cacheKey, token: cancellationToken);
    }

    public virtual async Task SetCacheAsync<T>(
        string methodName,
        T value,
        object? parameters = null,
        CancellationToken cancellationToken = default,
        params string[] additionalTags
    )
    {
        var cacheKey = BuildCacheKey(methodName, parameters);
        var tags = new List<string> { CachePrefixKey, CachePrefixKey + methodName };
        tags.AddRange(additionalTags);
        await fusionCache.SetAsync(
            cacheKey,
            value,
            options => options.SetDuration(CacheDefaultExpiration),
            tags.ToArray(),
            token: cancellationToken
        );
    }

    public virtual async Task RemoveCacheAsync(
        string methodName,
        object? parameters = null,
        CancellationToken cancellationToken = default
    )
    {
        var cacheKey = BuildCacheKey(methodName, parameters);
        await fusionCache.RemoveAsync(cacheKey, token: cancellationToken);
    }

    public virtual async Task<IPagedList<T>> GetOrSetPagedListAsync<T>(
        string methodName,
        Func<List<string>, CancellationToken, Task<IPagedList<T>>> factory,
        object? parameters = null,
        Action<FusionCacheEntryOptions>? setupAction = null,
        CancellationToken cancellationToken = default,
        params string[] additionalTags
    )
    {
        var cacheKey = BuildCacheKey(methodName, parameters);
        var cache = await fusionCache.TryGetAsync<PagedListWarp<T>>(
            cacheKey,
            token: cancellationToken
        );
        if (cache.HasValue)
        {
            return cache.Value.ToPagedList();
        }

        var tags = new List<string> { CachePrefixKey, CachePrefixKey + methodName };
        if (additionalTags.Length > 0)
        {
            tags.AddRange(additionalTags);
        }
        var pagedList = await factory(tags, cancellationToken);

        await fusionCache.SetAsync(
            cacheKey,
            pagedList.ToPagedListWarp(),
            setupAction ?? (options => options.SetDuration(CacheDefaultExpiration)),
            tags,
            token: cancellationToken
        );
        return pagedList;
    }

    public virtual ValueTask RemoveByMethodAsync(
        string methodName,
        CancellationToken cancellationToken = default
    )
    {
        return fusionCache.RemoveByTagAsync(CachePrefixKey + methodName, token: cancellationToken);
    }

    protected virtual ValueTask OnRemoveByPrefixBeforeAsync(
        CancellationToken cancellationToken = default
    )
    {
        return ValueTask.CompletedTask;
    }

    protected virtual ValueTask OnRemoveByPrefixAfterAsync(
        CancellationToken cancellationToken = default
    )
    {
        return ValueTask.CompletedTask;
    }

    public async ValueTask RemoveByPrefixAsync(CancellationToken cancellationToken = default)
    {
        await OnRemoveByPrefixBeforeAsync(cancellationToken);
        await fusionCache.RemoveByTagAsync(CachePrefixKey, token: cancellationToken);
        await OnRemoveByPrefixAfterAsync(cancellationToken);
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

代码解释

这是一个抽象缓存服务基类,用于提供基于 FusionCache 的缓存功能封装。该类已被标记为过时(Obsolete),建议使用源生成器方式实现缓存装饰器。

主要组成部分

1. 类定义与依赖注入

public abstract class AbstractCacheService(IFusionCache fusionCache) : ICacheService
  • 使用主构造函数语法注入 IFusionCache 依赖
  • 实现 ICacheService 接口
  • 抽象类,需要子类继承并实现抽象成员

2. 缓存键前缀管理

private static readonly ConcurrentDictionary<Type, string> PrefixKeyCacheSets
protected virtual string CachePrefixKey
  • 使用静态并发字典缓存类型的全名作为缓存键前缀
  • 每个服务类型对应唯一的前缀,基于类的 FullName
  • 防止不同服务的缓存键冲突

3. 核心方法

BuildCacheKey - 构建缓存键

根据方法名和参数生成唯一缓存键

GetOrSetCacheAsync - 获取或设置缓存

public virtual async Task<T> GetOrSetCacheAsync<T>(...)
  • 如果缓存存在则返回,否则执行工厂方法并缓存结果
  • 自动设置缓存标签(Tag):类前缀和方法前缀
  • 支持自定义过期时间配置

TryGetFromCacheAsync - 尝试获取缓存

返回 MaybeValue<T> 类型,表示可能存在的值

SetCacheAsync - 设置缓存

  • 手动设置缓存值
  • 支持附加自定义标签

RemoveCacheAsync - 删除指定缓存

根据方法名和参数删除特定缓存项

GetOrSetPagedListAsync - 分页列表缓存

public virtual async Task<IPagedList<T>> GetOrSetPagedListAsync<T>(...)
  • 专门处理分页数据的缓存
  • 使用 PagedListWarp<T> 包装类进行序列化
  • 先尝试获取缓存,不存在则执行工厂方法

RemoveByMethodAsync - 按方法删除

删除特定方法相关的所有缓存(基于标签)

RemoveByPrefixAsync - 按前缀删除

public async ValueTask RemoveByPrefixAsync(...)
  • 删除当前服务所有缓存
  • 提供前后钩子方法 OnRemoveByPrefixBeforeAsync 和 OnRemoveByPrefixAfterAsync

设计特点

  1. 标签系统:使用双层标签(类级别+方法级别)方便批量清除缓存
  2. 类型安全:使用泛型确保缓存数据类型安全
  3. 扩展性:虚方法允许子类重写行为
  4. 抽象配置:子类必须实现 CacheDefaultExpiration 定义默认过期时间
  5. 并发安全:使用 ConcurrentDictionary 确保多线程安全

使用场景

适合作为服务层的基类,为数据访问提供统一的缓存能力,减少重复的缓存代码编写。

评论加载中...