Search Results for

    Show / Hide Table of Contents

    AK1005 - Warning

    When accessing Sender or Self inside a lambda expression passed as an asynchronous method argument, you must always close over Sender or Self to ensure that the property is captured before the method is invoked, because there is no guarantee that asynchronous context will be preserved when the lambda is invoked inside the method.

    Cause

    Akka.NET tries its best to preserve asynchronous context if asynchronous codes were to run inside the actor. However, there are times where this is impossible to enforce, especially when a code that accesses variables that were thread execution specific such as the actor Context, Sender, and Self is being invoked outside the actor threading context.

    For example, lets assume that you're using an asynchronous third party API package that invokes a callback when it completes its operation:

    using System;
    using System.Threading.Tasks;
    
    namespace ThirdPartyApi;
    
    public sealed class JobManager
    {
        public async Task SubmitJobAsync(string job, Func<Task> asyncCallback)
        {
            // This breaks asynchronous context
            await ConnectToServer().ConfigureAwait(false); 
            // other codes here
            
            await asyncCallback();
        }
        
        private async Task ConnectToServer()
        {
            // codes here
        }
    }
    

    In the code above, the ConfigureAwait(false) call breaks the asynchronous context before the callback method were invoked. If this third party API code were to be used inside Akka.NET and Context, Sender, or Self were accessed inside the callback, we will see an error during runtime:

    using System;
    using System.Threading.Tasks;
    using Akka.Actor;
    using ThirdPartyApi;
    
    namespace MyApplication.Actors;
    
    public sealed class MyActor : ReceiveActor
    {
        private readonly JobManager _jobManager = new();
        
        public MyActor()
        {
            ReceiveAsync<string>(async job => {
                _jobManager.SubmitJobAsync(job, async () => 
                {
                    // This callback will not work
                    Context.Sender.Tell($"{job} submitted.", Self);
                })
            });
        }
    }
    
    NotSupportedException
    There is no active ActorContext, this is most likely due to use of async operations from within this actor.
      at MyApplication.Actors.MyActor`1<>c__DisplayClass48_0.<<InvokeTestMethodAsync>b__1>d.MoveNext() in \src\MyApplication.Actors\MyActor.cs:line 16
    

    Resolution

    While working with a third party API, it is most likely that you will not have a way to modify their codes to make them compatible with your asynchronous flow. To avoid this entire category of problem, we should close over any access to Sontext.Self, Context.Sender, Self, and Sender property in a local variable.

    For the non-working example above, we can fix it by modifying our code:

    using System;
    using System.Threading.Tasks;
    using Akka.Actor;
    using ThirdPartyApi;
    
    namespace MyApplication.Actors;
    
    public sealed class MyActor : ReceiveActor
    {
        private readonly JobManager _jobManager = new();
        
        public MyActor()
        {
            ReceiveAsync<string>(async job => {
                // Store Context.Sender and Self inside local variable
                var sender = Context.Sender;
                var self = Self;
                
                _jobManager.SubmitJobAsync(job, async () => 
                {
                    // Use local variables instead of accessing Context directly
                    sender.Tell($"{job} submitted.", self);
                })
            });
        }
    }
    
    In this article
    • githubEdit this page
    Back to top
    Contribute
    • Project Chat
    • Discussion Forum
    • Source Code
    Support
    • Akka.NET Support Plans
    • Akka.NET Observability Tools
    • Akka.NET Training & Consulting
    Maintained By
    • Petabridge - The Akka.NET Company
    • Learn Akka.NET