Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

return default value or null for Task<T>

Tags:

c#

async-await

I have a wrapper around Cache library and have the following method to retrieve the cached valued based on Key:

public async Task<T> GetAsync<T>(string key)
{
    var serializedObject = await _cache.GetStringAsync(key);

    return JsonConvert.DeserializeObject<T>(serializedObject);
}

The problem is there is nothing in cache, I receive an error from the DeserializeObject method. I'm stuck on what how to return either null or the default value of T when nothing is stored cache.

I tried:

public async Task<T> GetAsync<T>(string key)
{
    var serializedObject = await _cache.GetStringAsync(key);

    if (serializedObject == null) return Task.CompletedTask;

    return JsonConvert.DeserializeObject<T>(serializedObject);
}

But Task.CompletedTask cannot be converted to Task<T>

I tried:

public async Task<T> GetAsync<T>(string key)
    {
        var serializedObject = await _cache.GetStringAsync(key);

        if (serializedObject == null) return Task.FromResult<T>(null);

        return JsonConvert.DeserializeObject<T>(serializedObject);
    }

But null is not a valid parameter for T.

How do I get GetAsync to return either null or the default value of T?

Update

I got the following to work:

public async Task<T> GetAsync<T>(string key)
{
    var serializedObject = await _cache.GetStringAsync(key);

    return serializedObject == null ? default(T) : JsonConvert.DeserializeObject<T>(serializedObject);
}
like image 833
Josh Avatar asked Dec 10 '22 06:12

Josh


2 Answers

You have three options:

  • If you do not need to support value types, add T : class constraint
  • Add a wrapper class that lets your caller know if accessing cache was successful, or
  • Add a method to construct and cache the new value on unsuccessful retrieval.

The first implementation lets your approach compile:

public async Task<T> GetAsync<T>(string key) where T : class {
    var serializedObject = await _cache.GetStringAsync(key);
    if (serializedObject == null) {
        return await Task.FromResult<T>(null);
    }
    return JsonConvert.DeserializeObject<T>(serializedObject);
}

Here is the second implementation:

class CacheResult<T> {
    public bool IsSuccess {get;}
    public T Value {get;}
    public CacheResult(T val, bool isSuccess) {
        Value = val;
        IsSuccess = isSuccess;
    }
}
public async Task<CacheResult<T>> GetAsync<T>(string key) {
    var serializedObject = await _cache.GetStringAsync(key);
    if (serializedObject == null) {
        return new CacheResult(default(T), false);
    }
    return new CacheResult(
        JsonConvert.DeserializeObject<T>(serializedObject)
    ,   true
    );
}

Here is the third implementation:

public async Task<T> GetAsync<T>(string key, Func<string,T> make) {
    var serializedObject = await _cache.GetStringAsync(key);
    if (serializedObject == null) {
        var res = make(key);
        _cache.PutStringAsync(key, JsonConvert.SerializeObject(res));
        return res;
    }
    return JsonConvert.DeserializeObject<T>(serializedObject);
}

The caller would need to provide a "factory" delegate to the second method in order to make a new object if a cached one is not available.

like image 161
Sergey Kalinichenko Avatar answered Dec 29 '22 03:12

Sergey Kalinichenko


Have you tried this? return default(T);

Because you're returning the default value of T, you might as well return it synchronously.

like image 40
Erra Avatar answered Dec 29 '22 04:12

Erra