如何将类型不同的泛型表达式集合传入方法生成正确缓存键
解决方案
问题原因
你原有逻辑通过泛型参数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
相关产品推荐
相关产品推荐

