Hello, I am trying to access a stream and download its members to use the on a standalone WPF app. I wrote a method to receive these members and cast them into the desired objects. Please find the code below. Even though it receives the stream just fine on a number of machines, I am not able to receive the members and the Operations.Receive method doesn’t seem to resolve. Would you have any idea what the issue may be? Thank you in advance.
/// Receives the data from an account and one of its streams
private async Task ReceiveData(Stream stream, Account account)
if (streamAnnotations != null) streamAnnotations.Clear();
List<Branch> branches = await client.StreamGetBranches(stream.id, 10, 10);
var transport = new ServerTransport(account, stream.id);
var @base = await Operations.Receive(branches.commits.items.referencedObject, remoteTransport: transport);
var receivedBaseObjects = ((IEnumerable)@base.GetMembers()["objects"]).Cast<Base>().ToList();
streamAnnotations = receivedBaseObjects.Select(x => ConvertBaseToAnnotation((Base)x)).ToList();
Hey @GA_LefKour, welcome to the forum! Can you help us a bit - what is the specific error you’re encountering?
Ah, and I’ll follow up with a brainwave: if basically just things hang on the
Operations.Receive call without returning, it might be because of mess-ified threading context and other crazy .net stuff. To see if this is the actual culprit, can you try to do
var @base = Operations.Receive(branches.commits.items.referencedObject, remoteTransport: transport).Result;
awaiting the call?
Hello @dimitrie and thank you for welcoming me to the community. To give a bit more context, I am building a WPF application, with the intent of it being integrated into Revit. The aim of it is to receive some metadata related to the stream’s objects and display them as a list on the window. We have run the same solution on a number of machines and it receives the data just fine, while on the machine I need to work on it doesn’t, despite being the exact same solution. I also run tests with other web requests and disabled the firewall to test if the firewall was blocking it, but the issue still persists.
I also added the above line, but still the program doesn’t execute any statements after that line.
Okay, it’s then a spooky bug. Does the machine you need it to work on have a valid account that has access to the stream you’re picking data from in there? I suspect you checked this…
We could jump on a quick call if you want to try and debug things live - is there a way we can look at that specific windows machine?
Yup, I can get the account and its streams just fine. Certainly, a call will be preferable.
just dm’ed you a meet link!
@GA_LefKour , thanks for all the info
I was able to reproduce locally! Will look into it
Hi @GA_LefKour ,
So I couldn’t reproduce with the initial snippet that you posted here (with the
await), but was easily reproduced with
My guess is that it’s related to
.Result deadlocking if used in the UI thread. (some interesting read here: Await, and UI, and deadlocks! Oh my! - .NET Parallel Programming )
Even if you don’t use
.Result in your code,
Operations.Receive will use it internally anyway in some places. So it’s best not to call the receiving methods on the UI thread.
The easiest way to handle this, is to force the async data-loading methods on a worker thread (not the UI thread), by wrapping the async method call with
Task.Run. For example, instead of
_ = ReceiveData(currentStream, currentAccount)
you can do
Task.Run(() => ReceiveData(currentStream, currentAccount))
to make it execute on a different thread.
With this approach, you should also make sure you update the UI from the UI thread, and not from the background thread that made the request. (google has plenty of resources around this)
I also created a ticket for reviewing our
Core implementation, to see if it can be more friendly around this usecase: https://github.com/specklesystems/speckle-sharp/issues/1049
Thank you Cristian, I will try running the reception on a different thread and I well let you know how it works. Thanks again for your time and effort.
In WPF you can also use the Application dispatcher or the dispatcher associated with any UI Control:
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => MethodName(parameter)));