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

Redis Cache-Aside模式代码问题:为何需双重await才能编译?

问题根源分析

你遇到的await await问题,核心是泛型参数的类型推断错误:

  • 你的FetchMessage方法返回Task<Message>,而ResultFromCache的fromSource参数定义为Func<T2, T1>,编译器会把T1推断为Task<Message>
  • 这导致ResultFromCache的返回值变成了Task<Task<Message>>,必须用两次await才能拿到最终的Message对象
解决方案

修改BaseService中的ResultFromCache方法,让它支持异步数据源委托,同时正确处理异步调用:

修改后的完整代码

using StackExchange.Redis;
using System.Text.Json;

namespace TestCache
{
    public class Service: BaseService
    {
        private Context context { get; }

        // 修改后:只需一次await
        public async Task<Message> GetMessage(int accountId)
        {
            return await ResultFromCache(accountId, FetchMessage, "Message");
        }

        private async Task<Message> FetchMessage(int parentId)
        {
            // 补全EF查询示例
            return await context.Message.FirstOrDefaultAsync(m => m.AccountId == parentId);
        }
    }

    public class BaseService
    {
        private const int Hour = 3600;

        private ConnectionMultiplexer connection;
        private IDatabaseAsync database;

        // 关键修改:fromSource改为异步委托类型
        public async Task<T1> ResultFromCache<T1, T2>(T2 param1, Func<T2, Task<T1>> fromSource, string cacheKey, int cacheDuration = Hour)
        {
            // 从缓存获取数据
            var result = await CacheGet<T1>(cacheKey);

            if (result != null)
                return result;

            // 从数据库异步获取数据
            result = await fromSource(param1);

            // 补全缓存写入逻辑
            if (result != null)
            {
                var json = JsonSerializer.Serialize(result);
                await database.StringSetAsync(cacheKey, json, TimeSpan.FromSeconds(cacheDuration));
            }

            return result;
        }

        public async Task<T> CacheGet<T>(string key)
        {
            var value = await database.StringGetAsync(key);

            if (value.IsNull)
            {
                return default;
            }

            return JsonSerializer.Deserialize<T>(value);
        }
    }
}

关键修改点说明

  1. 委托类型调整:将Func<T2, T1>改为Func<T2, Task<T1>>,明确接受返回Task<T1>的异步方法,避免泛型类型推断错误
  2. 异步调用处理:调用fromSource(param1)时加上await,直接拿到T1类型的结果,消除嵌套的Task包装
  3. 补全缓存写入:完善了原代码的TODO部分,实现Cache-Aside模式的完整流程,确保数据库查询结果能正确存入缓存

修改后,业务方法只需一次await即可正常编译运行,使用者无需再写await await。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 02:45:17