My app has a concept like global resources. There are collections of objects contained which each part in my solution needs. Some of them are loaded at startup and other at first access.
My question goes to the second case (the lazy loaded collections). Below you see a test project:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Wellcome!");
var globalResources = new GlobalResources();
var toolClassA = new MyToolClass(globalResources);
var toolClassB = new MyToolClass(globalResources);
// This two "Init" calls can not be awaited in real project.
// They could be invoked by two different buttons
_ = toolClassA.InitAsync();
_ = toolClassB.InitAsync();
Console.WriteLine("Finish");
Console.ReadLine();
}
}
public class MyToolClass
{
private readonly GlobalResources _globalResources;
public MyToolClass(GlobalResources globalResources)
{
_globalResources = globalResources ?? throw new ArgumentNullException(
nameof(globalResources));
}
public async Task InitAsync()
{
await _globalResources.LoadAsync();
Console.WriteLine("awaited");
}
}
public class GlobalResources
{
public bool _isLoaded;
public readonly object _loadLock = new object();
private string _externalQueryResult;
private Task _runningLoadTask;
public async Task LoadAsync()
{
if (_isLoaded)
{
return;
}
lock (_loadLock)
{
if (_isLoaded)
{
return;
}
}
// A: Current code:
{
_externalQueryResult = await LongRunningQuery();
}
// B: Wanting something like this:
{
// If no task is already running then start one.
if (_runningLoadTask == null)
{
_runningLoadTask = LongRunningQuery();
await _runningLoadTask.Start(); // How await it?
}
// If a task ist already running then no db call should happen.
// Instead the currently running task should be awaited.
else
{
await Task.WaitAll(_runningLoadTask); // ???
return;
}
}
lock (_loadLock)
{
_isLoaded = true;
}
}
public async Task<string> LongRunningQuery()
{
// This method should only be called once.
Console.WriteLine("Loading data from Database (
this line should only be written once on console window).");
await Task.Delay(5000);
return "123";
}
}
The problem I have is in class GlobalResources
method LoadAsync
. There you can find a comment "A" which represents the way I code currently. That way is a problem because if a second load request happens then I have two database calls.
My solution you can find in section "B". I want to store the first task of the first request in a field. If a second call comes in it should await the stored Task which prevent it from call the db a second time.
My problem is I don't know how to code it. I think the async await pattern does not work if I work with Tasks manually. Or have you any idea?