In my WPF application (.NET 8.0) I want to consume a asynchronous operation from a 3rd party library. When testing with all code inside the MainWindow.xaml.cs it works smoothely. When I move the same code into a separate class, using a static constructor, the operation is blocking indefinitely even with WaitAsync.
Here is my code:
public class VirtuosoCommunication
{
..
public static VirtuosoCommunication? Create(...)
{
VirtuosoCommunication vc = new VirtuosoCommunication();
// setting private variables for vc
if (!vc.TestRead(log)) return null;
if (!vc.TestWrite(log)) return null;
return vc;
}
private bool TestRead(Log? log)
{
string testGraph = "...";
Task<Graph?> task = LoadGraphFromSparqlRemoteEndpoint(testGraph, log);
Graph? g = task.Result;
return g != null;
}
internal async Task<Graph?> LoadGraphFromSparqlRemoteEndpoint(string graphFullName, Log? log)
{
Uri baseUri = new Uri(_readEndpoint ?? "");
HttpClient client = new HttpClient();
client.BaseAddress = baseUri;
SparqlQueryClient sparqlQueryClient = new SparqlQueryClient(client, baseUri);
Graph result = new Graph();
IGraph? tmp = null;
try
{
while (tmp == null || tmp.Triples.Count == limit)
{
string query = "construct { ?s ?p ?o } FROM <" + graphFullName + "> where {?s ?p ?o.} OFFSET " + offSet.ToString() + " LIMIT " + limit.ToString();
Task<IGraph> task = sparqlQueryClient.QueryWithResultGraphAsync(query);
tmp = await task.WaitAsync(TimeSpan.FromSeconds(4));
...
}
}
catch (Exception ex) { ... }
return result;
}
When I run the code line by line, it stops executing at tmp = await task.WaitAsync. When I then hit the "Break execution" Button in Visual Studio, it is showing me the line Graph? g = task.Result
in the TestRead method to be the next to be executed when the thread returns.
As I said, the same async call in a test routine completely placed in MainWindow.xaml.cs works without problem, so I guess I am somehow using the async call wrongly.
UPDATE 25.11.2024
From the answer of this question I changed the TestRead
method to:
private bool TestRead(Log? log)
{
string testGraph = "...";
//Task<Graph?> task = LoadGraphFromSparqlRemoteEndpoint(testGraph, log);
Task<Graph?> task = Task.Run<Graph?>(async() => await LoadGraphFromSparqlRemoteEndpoint(testGraph, log));
Graph? g = task.Result;
return g != null;
}
It works this way (yeah!), but I am not sure if this is the correct way to do it. Regarding parallelization, I just need the call to the SPARQL endpoint to not block my UI, I don't need to run other, parallel tasks meanwhile.
As Fildor said in the comments, you're not using a static cctor
; you're using a static factory method.
And that's a good thing. A ctor
or cctor
cannot be async
, so you wouldn't be able to await
anything. But a factory method can be async
. And in this case, it should be:
public static async Task<VirtuosoCommunication?> Create(...)
{
VirtuosoCommunication vc = new VirtuosoCommunication();
// setting private variables for vc
if (!await vc.TestRead(log)) return null;
if (!await vc.TestWrite(log)) return null;
return vc;
}
private async Task<bool> TestRead(Log? log)
{
string testGraph = "...";
Graph? g = await LoadGraphFromSparqlRemoteEndpoint(testGraph, log);
return g != null;
}
Next, I'd suggest looking at your HttpClient
usage, which is problematic:
You should consider using IHttpClientFactory
instead of creating a new HttpClient
instance on every call.
Use IHttpClientFactory to implement resilient HTTP requests - .NET | Microsoft Learn