Jason Sultana

Follow this space for writings (and ramblings) about interesting things related to software development.

BeginInvoke on .net core

17 Oct 2020 » dotnet

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://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/calling-synchronous-methods-asynchronously

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