You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将类型不同的泛型表达式集合传入方法生成正确缓存键

解决方案

问题原因

你原有逻辑通过泛型参数T获取控制器名称,当你把不同控制器的表达式统一转为Expression<Func<ApiController, object>>类型时,泛型T固定为ApiController,自然无法得到实际的控制器类名。需求完全可以实现,核心是不依赖泛型参数,直接从表达式结构中提取实际的控制器类型。

方案1:修改控制器名获取逻辑(推荐)

直接调整MakeCacheKey中控制器名的获取规则,从方法调用的声明类型,或者表达式中的强转类型提取,不需要依赖泛型参数。同时新增非泛型重载适配统一列表的场景:

// 保留原有泛型方法兼容旧逻辑
public string MakeCacheKey<T>(Expression<Func<T, object>> action) where T : ApiController
{
    if (action.Body is MethodCallExpression methodCall)
    {
        return BuildCacheKey(methodCall);
    }
    return string.Empty;
}

// 新增非泛型重载适配多控制器统一列表
public string MakeCacheKey(Expression<Func<ApiController, object>> action)
{
    // 处理带强转的方法调用
    if (action.Body is UnaryExpression { Operand: MethodCallExpression convertMethodCall })
    {
        return BuildCacheKey(convertMethodCall);
    }
    // 处理直接调用的方法
    else if (action.Body is MethodCallExpression directMethodCall)
    {
        return BuildCacheKey(directMethodCall);
    }
    return string.Empty;
}

// 抽离公共的缓存键生成逻辑
private string BuildCacheKey(MethodCallExpression methodCall)
{
    var parameters = new Dictionary<string, object>();
    ParameterInfo[] parameterInfos = methodCall.Method.GetParameters();
    for (var i = 0; i < methodCall.Arguments.Count; i++)
    {
        object value = GetExpressionValue(methodCall.Arguments[i]);
        parameters.Add(parameterInfos[i].Name, value);
    }
    // 直接从方法所属类获取实际控制器名称
    string controllerName = methodCall.Method.DeclaringType.Name;
    string actionName = methodCall.Method.Name;
    return this.MakeCacheKeyInternal(controllerName, actionName, parameters);
}

调整后你原来的列表定义可以直接使用:

var actions = new List<Expression<Func<ApiController, object>>> 
{
    c => ((HomeApiController)c).Get(1), 
    c => ((TeamApiController)c).GetAll()
};
var cacheKeys = actions.Select(action => this._cacheKeyGenerator.MakeCacheKey(action)).ToList();

方案2:包装类实现类型安全存储

如果你不想修改原有泛型方法的逻辑,可以用抽象包装类封装不同泛型的表达式,避免强转表达式的写法:

// 抽象缓存键表达式基类
public abstract class CacheKeyExpression
{
    public abstract string GenerateKey(ICacheKeyGenerator generator);
}

// 泛型实现类对应不同控制器
public class CacheKeyExpression<T> : CacheKeyExpression where T : ApiController
{
    private readonly Expression<Func<T, object>> _expression;
    public CacheKeyExpression(Expression<Func<T, object>> expression) => _expression = expression;
    
    public override string GenerateKey(ICacheKeyGenerator generator)
    {
        return generator.MakeCacheKey(_expression);
    }
}

使用方式如下:

var actions = new List<CacheKeyExpression>
{
    new CacheKeyExpression<HomeApiController>(c => c.Get(1)),
    new CacheKeyExpression<TeamApiController>(c => c.GetAll())
};
var cacheKeys = actions.Select(a => a.GenerateKey(_cacheKeyGenerator)).ToList();

该方案无需修改原有MakeCacheKey逻辑,且写法类型安全,不会出现强转错误的问题。

注意事项

如果你的Action方法是重写自父类的方法,Method.DeclaringType可能会返回父类类型,这种场景下可以从强转表达式的类型获取控制器名:如果方法调用外层有UnaryExpression强转操作,直接取UnaryExpression.Type作为控制器类型即可。

内容的提问来源于stack exchange,提问作者demo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 20:36:03