I have the following:
public class User
{
private readonly Lazy<Task<List<ReminderDb>>> _reminders;
public SmsUserDb()
{
// Get _reminderReader from IoC
_reminders = new Lazy<Task<List<ReminderDb>>>(async () => (List<ReminderDb>)await _reminderReader.Get(UserId));
}
public string UserId { get; set; }
public Task<List<ReminderDb>> Reminders => _reminders.Value;
}
When I instantiate an object Like so:
var n = new SmsUserDb {UserId = "123456"};
var rems = await n.Reminders;
This code works and I see that n.Reminders ="Waiting for activation" until I hit await n.Reminders line.
However, when I query this User object from cache like so:
var n1 = await _userReader.GetUserFromCache("+17084556675"); // return SmsUserDb
var n2 = await n1.Reminders;
when it hits GetUserFromCache() it right away calls _reminderReader.Get(UserId) which calls cache again to get reminders. Then it simply times out. So Lazy doesn't work and most likely causes a deadlock.
public async Task<SmsUserDb> GetUserFromCache(string phoneNumber)
{
var hash = CachedObjectType.smsuser.ToString();
var fieldKey = string.Format($"{CachedObjectType.smsuser.ToString()}:user-{phoneNumber}");
var result = await _cacheUserService.GetHashedAsync(hash, fieldKey);
return result;
}
private async Task<List<ReminderDb>> GetRemindersFromCache(string userId)
{
var hash = CachedObjectType.smsreminder.ToString();
var fieldKey = string.Format($"{CachedObjectType.smsreminder.ToString()}:user-{userId}");
var result = await _cacheService.GetHashedAsync(hash, fieldKey);
return result ?? new List<ReminderDb>();
}
What could be the problem?
This all works fine in my test code (shown below). Therefore the error must be in some code that you haven't shown us.
Here's some sample code that works - it proves that Lazy<T>
is working correctly. Therefore the error is elsewhere in your code.
You must post a compilable repro in order for anyone to help you with this.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Demo
{
public class ReminderDb
{
public string Value;
}
public class ReminderReader
{
public async Task<List<ReminderDb>> Get(string userId)
{
Console.WriteLine("In Get() for " + userId);
await Task.Delay(1000);
Console.WriteLine("Returning from Get()");
return new List<ReminderDb>{new ReminderDb{Value = userId}};
}
}
public class Cache
{
public void Add(string key, SmsUserDb value)
{
_cache.Add(key, value);
}
public async Task<SmsUserDb> GetHashedAsync(string key)
{
await Task.Delay(1000);
return _cache[key];
}
readonly Dictionary<string, SmsUserDb> _cache = new Dictionary<string, SmsUserDb>();
}
public class SmsUserDb
{
readonly Lazy<Task<List<ReminderDb>>> _reminders;
readonly ReminderReader _reminderReader = new ReminderReader();
public SmsUserDb()
{
_reminders = new Lazy<Task<List<ReminderDb>>>(async () => (List<ReminderDb>) await _reminderReader.Get(UserId));
}
public string UserId { get; set; }
public Task<List<ReminderDb>> Reminders => _reminders.Value;
}
static class Program
{
static async Task Main()
{
var db = new SmsUserDb(){UserId = "user ID"};
var cache = new Cache();
cache.Add("key", db);
Console.WriteLine("Press RETURN to await cache.GetHashedAsync()");
Console.ReadLine();
var result = await cache.GetHashedAsync("key");
Console.WriteLine("Press RETURN to await Reminders[0].Value");
Console.ReadLine();
Console.WriteLine((await result.Reminders)[0].Value);
}
}
}