There is a trend in the .NET world to make time-consuming calls asynchronous, to prevent applications from getting blocked when waiting for a response from a web service and things like that. Many existing APIs were enhanced with asynchronous variants of previously synchronous actions and some newer APIs offer only asynchronous methods. So… how can we call such asynchronous methods from X++?
For a demo, imagine that I want to read the content of a file. I can use StreamReader.ReadToEnd() or the asynchronous alternative, ReadToEndAsync().
ReadToEnd() returns a string, therefore I can simply assign the result to a string variable.
str file = "devenv.exe.config"; using (var reader = new System.IO.StreamReader(file)) { str s = reader.ReadToEnd(); }
By the way, the code is written for F&O, where we can simplify our code with using and var keywords, but the core logic can be used in older versions of AX as well. .NET Interop as such was introduced in AX 4.0.
ReadToEndAsync() doesn’t return a string, it returns a task returning a string (Task<string>). A task represent a piece of work to be done, in this case reading the file. In normal .NET development, you could use threads for parallel processing for a long time before tasks (Task Parallel Library) were introduced, but tasks make it all much easier. It’s been further enhanced by adding async and await keyworks to languages such as C#, which allows writing code for dealing with asynchronous methods in a very succinct way.
We don’t have async/await in X++, but we surely can work with tasks directly. When I call an async method, I get a task object and the easiest thing I can do with it it is waiting for completion and then accessing the result:
str file = "devenv.exe.config"; using (var reader = new System.IO.StreamReader(file)) { var task = reader.ReadToEndAsync(); task.Wait(); str s = task.Result; }
I’ll get exactly the same result as in the synchronous variant mentioned above.
This is actually a synchronous execution of an asynchronous API, therefore it doesn’t bring many advantages for parallelism, but it’s often what you want anyway. For instance, you call your logic from a batch job (you don’t mind blocking), you can’t continue without the result and the API doesn’t support synchronous operations.
You can build something more interesting on top of this simple example. For instance, you can create several tasks (without calling Wait()), put them into an array and then use Task.WaitAll() to execute all of them at once (in parallel if possible) and wait for completion.
To make things truly asynchronous, you would need callback methods instead of waiting for completion.
Hi Martin. I’m Gabriel, I work as a Dynamics 365 Finance & Operations developer.
I need help about something that is required.
I have a custom service that sends invoices to an external service for processing. The external service processes the invoices at an unknown time and sends them back to me. What I need is that when the external service sends me the invoices, an asynchronous process is triggered so that it does not interrupt the dynamics 365 environment.
I create a service that works synchronously, have the contract class, the request class, the response class and the service class where the business logic is. I receive a json-formatted document as a response from the external service.
Thanks.
Using an synchronous service would indeed be wrong – the process is asynchronous by nature.
But the solution has nothing to do with the topic of this blog post. You’ll simply call the external system, which will store the message somewhere for later processing. You don’t wait for anything else.
When the external system processes the invoice, it’ll either send the message to a web service exposed by F&O, or it’ll store the message somewhere where you can read it from (e.g. Service Bus queue or Azure storage).
Hi Martin, long time no see!
Have you gotten a callback to work successfully from an async method? I’m experimenting with X++ registering a callback eventhandler to the C# library, calling a C# void sync method that spins up a bunch of async tasks, then later the async tasks upon completion execute the callback to X++ code, but when the callback happens from an async method, I get a memory error. If I keep the callback in the sync method, it succeeds. This sort of defeats the purpose though because I was hoping to make it truly asynchronous.
Also, does your code above handle exceptions? I think `Task.Wait` + `Task.Result` ends up dumping `AggregateException` where `Task.GetAwaiter().GetResults()` would give the actual exception, but I haven’t tried it yet.
Hi Alex. No, I didn’t try a callback. It’s possible that your call back to F&O needs to execute on the same thread that called .NET Interop.
There is indeed no code for exception handling above. As yourself, I also think that Wait() will thrown an exception (if something fails) which can be caught in the usual way. AggregateException contains a collection of actual exceptions in its InnerExceptions property.