using System;

namespace Dpz.Core.SourceGenerator.Attributes;

/// <summary>
/// 标记需要由源生成器生成缓存装饰器的实现方法。
/// </summary>
/// <remarks>
/// <para>
/// 该特性应标记在实现类的方法上,而不是接口方法上。接口只描述服务契约,
/// 是否缓存属于具体实现的策略。
/// </para>
/// <para>
/// 源生成器会根据实现类型、方法名和参数生成缓存键,并要求目标类型可以注入
/// <c>IFusionCache</c>。遇到暂不支持的参数类型时,请显式设置 <see cref="CacheKey" />,
/// 或将参数调整为可稳定格式化的类型。
/// </para>
/// </remarks>
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public sealed class CacheAttribute : Attribute
{
    /// <summary>
    /// 缓存键前缀。未设置时使用实现类型的完整名称。
    /// </summary>
    public string? Prefix { get; set; }

    /// <summary>
    /// 显式缓存键。设置后优先使用该值,不再根据参数拼接缓存键。
    /// </summary>
    public string? CacheKey { get; set; }

    /// <summary>
    /// 缓存过期时间,单位为秒,默认一小时。
    /// </summary>
    public int ExpirationSeconds { get; set; } = 3600;

    /// <summary>
    /// 缓存命中或新写入缓存后需要执行的后处理方法名称。
    /// </summary>
    /// <remarks>
    /// 后处理方法必须定义在当前实现类上,并且能被生成的缓存装饰器访问。要求:
    /// <list type="bullet">
    ///   <item>实例方法(非 static),返回 <c>Task</c>、<c>ValueTask</c>、<c>Task&lt;T&gt;</c>
    ///         或 <c>ValueTask&lt;T&gt;</c>。</item>
    ///   <item>第一个参数类型必须与被缓存方法的返回值类型一致(即被缓存方法的
    ///         <c>Task&lt;T&gt;</c> 中的 <c>T</c>)。</item>
    ///   <item>推荐使用 <c>internal</c> 可见性。</item>
    /// </list>
    /// 找不到匹配方法时,源生成器会输出 <c>DPZ_CACHE003</c> 编译错误。
    /// </remarks>
    public string? PostProcess { get; set; }

    /// <summary>
    /// 额外缓存标签,用于跨业务域或组合场景清理缓存。
    /// </summary>
    /// <remarks>
    /// 源生成器始终会自动写入前缀标签和方法标签;这里只需要填写额外的业务标签。
    /// </remarks>
    public string[] AdditionalTags { get; set; } = [];
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

代码解释

这是一个用于源代码生成器(Source Generator)的特性(Attribute)类,用于标记需要自动生成缓存装饰器的方法。

核心功能

CacheAttribute 特性允许开发者在实现类的方法上标记,告诉源生成器为该方法自动生成缓存逻辑,使用 IFusionCache 作为缓存提供程序。

关键属性说明

1. Prefix(缓存键前缀)

public string? Prefix { get; set; }
  • 用于自定义缓存键的前缀部分
  • 未设置时默认使用实现类的完整类型名称

2. CacheKey(显式缓存键)

public string? CacheKey { get; set; }
  • 直接指定完整的缓存键
  • 设置后会忽略参数拼接,优先使用该值
  • 适用于参数类型不支持自动格式化的场景

3. ExpirationSeconds(过期时间)

public int ExpirationSeconds { get; set; } = 3600;
  • 缓存过期时间(秒)
  • 默认值:3600秒(1小时)

4. PostProcess(后处理方法)

public string? PostProcess { get; set; }
  • 指定缓存命中或写入后要执行的回调方法名
  • 方法要求:
    • 必须是实例方法(非静态)
    • 返回类型:TaskValueTaskTask<T>ValueTask<T>
    • 第一个参数类型必须与被缓存方法的返回值类型一致
    • 推荐使用 internal 访问级别
  • 找不到匹配方法时会产生 DPZ_CACHE003 编译错误

5. AdditionalTags(额外标签)

public string[] AdditionalTags { get; set; } = [];
  • 用于缓存失效策略的业务标签
  • 源生成器会自动添加前缀标签和方法标签
  • 这里只需填写额外的业务域标签

使用场景示例

public class UserService
{
    [Cache(ExpirationSeconds = 600, PostProcess = nameof(LogUserAccess))]
    public async Task<User> GetUserByIdAsync(int userId)
    {
        // 实际查询逻辑
    }
    
    internal async Task LogUserAccess(User user)
    {
        // 后处理逻辑
    }
}

设计要点

  1. 标记在实现类而非接口:缓存是实现策略,不是契约的一部分
  2. 依赖 IFusionCache:目标类必须能注入该缓存接口
  3. 类型限制:仅可用于方法,不可继承(Inherited = false

这个特性是声明式缓存的典型应用,通过元数据驱动代码生成,减少样板代码。

评论加载中...