G’day guys!
Today I wanted to talk a little bit about an interesting observation that I found at work the other day. I was using an open-source library that was presumably written for .NET framework, and it was falling over on a call similar to the following:
Action<string> publishAction = (string message) => { Console.WriteLine(message); };
//publishAction("hello"); // Invoke the action synchronously
var callback = new AsyncCallback((IAsyncResult result) => {
Console.WriteLine("Done");
});
// Invoke the action asynchronously
publishAction.BeginInvoke("hello", callback, null);
and this would result in a very curious exception:
Unhandled Exception: System.PlatformNotSupportedException: Operation is not supported on this platform.
Um, what!?
Yeah, that was my first reaction. Long story short, using delegate.BeginInvoke
is not supported on .net core. We’ll get into why shortly, but for a quick fix, you’ll just need to migrate BeginInvoke
calls to use the new(er) Task-based Asynchronous Pattern. Eg:
Action<string> publishAction = (string message) => { Console.WriteLine(message); };
//publishAction("hello"); // Invoke the action synchronously
// Invoke the action asynchronously
Task.Run(() =>
{
publishAction("hello");
Console.WriteLine("Done");
});
The migration is fairly simple - instead of calling BeginInvoke
on a delegate, just wrap it in a Task.Run
which will queue it to run asynchronously on the threadpool and return a task to keep track of the result. But I was pretty interested in why the older approach didn’t work, so let’s look more into that.
Read on!
From the migration guide listed under further reading, I found a link to this issue on github: https://github.com/dotnet/runtime/issues/16312, which mentions that BeginInvoke
and EndInvoke
actually depend on System.Runtime.Remoting
underneath the hood, and it’s that library in particular which isn’t supported on .net core.
The most insightful article that I found related to this is actually https://docs.microsoft.com/en-us/dotnet/core/porting/net-framework-tech-unavailable. According to this article, System.Runtime.Remoting
is used to support communication between AppDomains. Since AppDomains aren’t supported in .net core (in favour of separate processes or containers), it makes sense that the Remoting
library (and async delegate invocation, by extension) was also dropped.
So that’s it! We’ve been using Tasks for so long now in dotnet-land that I almost forgot these older methods existed, so it was actually refreshing to stumble into this and venture off the beaten path a little bit. Refreshing, like drinking a nice cool glass of battery acid.
Task.Run(() =>
{
return "Anyway, that's all from me guys. Catch ya again next week!";
})
.Wait();
Further reading
https://devblogs.microsoft.com/dotnet/migrating-delegate-begininvoke-calls-for-net-core/
https://docs.microsoft.com/en-us/dotnet/api/system.appdomain?view=netcore-3.1