【问题标题】:Is there a way to refactor two methods, one that calls an awaited method, and one that calls a normal one?有没有办法重构两种方法,一种调用等待方法,另一种调用普通方法?
【发布时间】:2014-05-08 15:27:06
【问题描述】:

我正在为网站构建缓存服务,我想提供一个公共接口,该接口可以采用Func<TKey,TValue> 或Func<TKey,Task<TValue>> 方法,该方法可以由服务调用以处理缓存未命中。

在处理这两种委托类型时,我最终会重复代码。有没有办法巩固?我特别担心“Func”方法不是线程安全的,并且不适合包装在“Task.Run”中。

这是我的代码:

public interface ICacheServiceEngine
{
    Task<CacheResult<TValue>> TryGetValueAsync<TValue>(string key);
    Task<CacheResult<TValue>> 
        TryGetValueAsync<TKey,TValue>(TKey key, Func<TKey,string> keyFunc);
    Task<TValue> GetValueAsync<TValue>(string key, 
        Func<string, TValue> valueSourceFunc);
    Task<TValue> GetValueAsync<TKey,TValue>(TKey key, 
        Func<TKey,string> keyFunc, Func<TKey, TValue> valueSourceFunc);
    Task<TValue> GetValueAsync<TValue>(string key, 
        Func<string, Task<TValue>> valueSourceFuncAsync);
    Task<TValue> GetValueAsync<TKey, TValue>(TKey key, 
        Func<TKey,string> keyFunc, Func<TKey, Task<TValue>> valueSourceFuncAsync);
}

public interface ICacheServiceDataAccessor
{
    Task<CacheResult<TValue>> TryGetAsync<TValue>(string key);
    Task PutAsync<TValue>(string key , TValue result);
}


public class CacheServiceEngine : ICacheServiceEngine
{
    private ICacheServiceDataAccessor cacheDataAccessor;


    public CacheServiceEngine(ICacheServiceDataAccessor cacheDataAccessor)
    {   
        // add guard
        this.cacheDataAccessor = cacheDataAccessor;
    }


    public async Task<CacheResult<TValue>> TryGetValueAsync<TValue>(string key)
    {
        return await this.cacheDataAccessor.TryGetAsync<TValue>(key);
    }

    public async Task<CacheResult<TValue>> TryGetValueAsync<TKey,TValue>(TKey key, 
        Func<TKey,string> keyFunc)
    {
        string keyString = keyFunc(key);
        return await this.cacheDataAccessor.TryGetAsync<TValue>(keyString);
    }   

    public async Task<TValue> GetValueAsync<TValue>(string key, 
        Func<string, TValue> valueSourceFunc)
    {
        return await this.InnerGetValueAsync(key, () => valueSourceFunc(key));
    }

    public async Task<TValue> GetValueAsync<TKey,TValue>(TKey key, 
        Func<TKey,string> keyFunc, Func<TKey, TValue> valueSourceFunc)
    {
        string keyString = keyFunc(key);
        return await this.InnerGetValueAsync(keyString, () => valueSourceFunc(key));
    }

    public async Task<TValue> GetValueAsync<TValue>(string key, 
        Func<string, Task<TValue>> valueSourceFuncAsync)
    {
        return await this.InnerGetValueAsync(key, () => valueSourceFuncAsync(key));
    }

    public async Task<TValue> GetValueAsync<TKey, TValue>(TKey key, 
        Func<TKey,string> keyFunc, Func<TKey, Task<TValue>> valueSourceFuncAsync)
    {
        string keyString = keyFunc(key);
        return await this.InnerGetValueAsync(keyString, 
            () => valueSourceFuncAsync(key));
    }


    // the two private methods are very close to each other
    // can I pull out the similarities, if I assume that 'valueSourceFunc'
    // is not thread-safe?
    private async Task<TValue> InnerGetValueAsync<TValue>(string key, 
        Func<TValue> valueSourceFunc)
    {
        TValue value;
        CacheResult<TValue> cacheResult = 
            await this.cacheDataAccessor.TryGetAsync<TValue>(key);

        if (cacheResult.InCache)
        {
            value  = cacheResult.Value;
        }
        else
        {
            // this call is normal (synchronous)
            value = valueSourceFunc();
            await this.cacheDataAccessor.PutAsync(key, value);
        }
        return value;   
    }

    private async Task<TValue> InnerGetValueAsync<TValue>(string key, 
        Func<Task<TValue>> valueSourceFuncAsync)
    {
        TValue value;
        CacheResult<TValue> cacheResult = 
            await this.cacheDataAccessor.TryGetAsync<TValue>(key);

        if (cacheResult.InCache)
        {
            value  = cacheResult.Value;
        }
        else 
        {
            // this call has to be awaited
            value = await valueSourceFuncAsync();
            await this.cacheDataAccessor.PutAsync(key, value);
        }

        return value;
    }
}

【问题讨论】:

    标签: c# asp.net-mvc-4 functional-programming async-await func


    【解决方案1】:

    首先,您应该重新考虑您的ICacheServiceDataAccessor。当键不在缓存中时,您完全有可能不必要地计算值。我会建议这样的事情:

    public interface ICacheServiceDataAccessor
    {
      Task<CacheResult<TValue>> TryGetAsync<TValue>(string key);
      Task<CacheResult<TValue>> GetOrPutAsync<TValue>(string key, Func<Task<TValue>> result);
    }
    

    但是 - 暂时忽略这个问题 - 有一种方法可以将同步调用视为异步调用:Task.FromResult。

    public Task<CacheResult<TValue>> TryGetValueAsync<TValue>(string key)
    {
        return cacheDataAccessor.TryGetAsync<TValue>(key);
    }
    
    public Task<CacheResult<TValue>> TryGetValueAsync<TKey,TValue>(TKey key, 
        Func<TKey, string> keyFunc)
    {
        string keyString = keyFunc(key);
        return cacheDataAccessor.TryGetAsync<TValue>(keyString);
    }   
    
    public Task<TValue> GetValueAsync<TValue>(string key, 
        Func<string, TValue> valueSourceFunc)
    {
        return InnerGetValueAsync(key, () => Task.FromResult(valueSourceFunc(key)));
    }
    
    public Task<TValue> GetValueAsync<TKey,TValue>(TKey key, 
        Func<TKey,string> keyFunc, Func<TKey, TValue> valueSourceFunc)
    {
        string keyString = keyFunc(key);
        return InnerGetValueAsync(keyString, () => Task.FromResult(valueSourceFunc(key)));
    }
    
    public Task<TValue> GetValueAsync<TValue>(string key, 
        Func<string, Task<TValue>> valueSourceFuncAsync)
    {
        return InnerGetValueAsync(key, () => valueSourceFuncAsync(key));
    }
    
    public async Task<TValue> GetValueAsync<TKey, TValue>(TKey key, 
        Func<TKey,string> keyFunc, Func<TKey, Task<TValue>> valueSourceFuncAsync)
    {
        string keyString = keyFunc(key);
        return InnerGetValueAsync(keyString, () => valueSourceFuncAsync(key));
    }
    

    作为最终的设计说明,我会考虑只让其中最通用的一个成为ICacheServiceEngine 的实际成员。由于其他方法实际上只是该方法的重载(并且无论派生类如何都将始终具有相同的实现),因此可以将它们定义为 ICacheServiceEngine 上的扩展方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-22
      • 2013-02-10
      • 1970-01-01
      • 2014-12-13
      • 2011-07-05
      • 1970-01-01
      • 1970-01-01
      • 2020-11-06
      相关资源
      最近更新 更多