Using Predicates in Akka.NET Receive Actors

When using the Receive actor API in Akka.NET, we can take advantage of overloads of the Receive method that allow us to specify predicates.

A message will now be handled if it is of the correct type and the predicate is met.

Take the following actor that sends emails with a different message if a customer is “high value”:

class WelcomeEmailSender : ReceiveActor
{
    public WelcomeEmailSender()
    {
        Receive<SendNewCustomerWelcomeEmail>(message => HandleMessage(message));
    }

    private void HandleMessage(SendNewCustomerWelcomeEmail message)
    {            
        if (message.IsHighValueCustomer)
        {
            SendEmail("Dear high value customer...", message.EmailAddress);
        }
        else
        {
            SendEmail("Yo! ...", message.EmailAddress);
        }
    }

    private void SendEmail(string greeting, string emailAddress)
    {
        Console.WriteLine("{0} to {1}", greeting, emailAddress);
    }
}

In the preceding code, the if statement is deciding what action is taken – the welcome message to be sent.

Using an overload of the receive method, we can add a predicate that replaces this if statement:

class WelcomeEmailSender2 : ReceiveActor
{
    public WelcomeEmailSender2()
    {
        Receive<SendNewCustomerWelcomeEmail>(
            message =>
            {
                SendEmail("Dear high value customer...", message.EmailAddress);
            },
            handleIf => handleIf.IsHighValueCustomer);

        Receive<SendNewCustomerWelcomeEmail>(
            message =>
            {
                SendEmail("Yo! ...", message.EmailAddress);
            }, 
            handleIf => handleIf.IsHighValueCustomer == false);
    }

    private void SendEmail(string greeting, string emailAddress)
    {
        Console.WriteLine("{0} to {1}", greeting, emailAddress);
    }
}

In the preceding code the predicate (represented by the lambda: handleIf => handleIf.IsHighValueCustomer) replaces the if.

In these example we only have two possible predicates because we are looking at the boolean “IsHighValueCustomer”. The following code shows predicates being defined for non-boolean (string) values:

class SomeActor : ReceiveActor
{
    public SomeActor()
    {
        Receive<string>(message => Console.WriteLine("Knock knock..."), handleIfMessage => handleIfMessage.Equals("Tell me a joke"));
        Receive<string>(message => Console.WriteLine("Hello There!"), handleIfMessage => handleIfMessage.Equals("Say Hi"));
        Receive<string>(message => Console.WriteLine("Woof"), handleIfMessage => handleIfMessage.Equals("Bark like a dog"));
    }
}

If we were to send this actor the following messages they would be received and handled:

IActorRef someActor = actorSystem.ActorOf<SomeActor>("Some");

someActor.Tell("Tell me a joke");
someActor.Tell("Say Hi");
someActor.Tell("Bark like a dog");

However, the following would not be received as it has no matching predicate (even though the message is still of type string):

someActor.Tell("Say bye");

To learn more about Akka.NET, check out the docs or my Pluralsight courses: Building Concurrent Applications with the Actor Model in Akka.NET and Implementing Logging and Dependency Injection in Akka.NET.

You can start watching with a Pluralsight free trial.

SHARE:

New Pluralsight Course: Implementing Logging and Dependency Injection in Akka.NET

If you’ve already watched my Akka.NET fundamentals Pluralsight course and want to learn more about using DI and logging, check out my new Implementing Logging and Dependency Injection in Akka.NET course.

You can start watching with a Pluralsight free trial.

Course Description

Akka.NET makes building concurrent and distributed applications easier. As with other architectures, Akka.NET based systems need effective logging for the monitoring and diagnosing of systems when deployed to production. Just because we use Akka.NET to get the benefits of the Actor Model, it doesn’t mean that best practices for object construction and dependencies such as dependency injection should be ignored. By the end of the course, you’ll understand how to implement effective logging in your Akka.NET system and how to use dependency injection to ensure the services your actors depend on are still provided in a loosely coupled and configurable way.

SHARE:

Switchable Actor Behaviour in Akka.NET

One of the things that actors can do is change their behaviour in response to an incoming message. This doesn’t however mean that we end up with a single “god actor” that has too many responsibilities. Whilst this may also seem like a potentially dangerous thing to do it is actually a very poweful concept. Because an actor instance only processes one message at a time, we don’t have to worry about this behaviour switch affecting the currently processing message, in fact the change in behaviour applies to the next message processed.

There’s a number of benefits to using switchable behaviours including the potential reduction/simplification of the amount of code we need to write and the ability for our brains to conceptualize what the the actor does more readily.

As an example, consider the following code that represents an actor that can respond in one of two ways to an incoming message depending on its current (boolean) state:

class LightSwitchActor : ReceiveActor
{
    private bool _isTurnedOn;
    public LightSwitchActor()
    {
        Receive<FlipTheSwitchMessage>(message => HandleMessage(message));
    }

    private void HandleMessage(FlipTheSwitchMessage message)
    {
        if (_isTurnedOn)
        {
            // do some processing when currently turned on and message received

            // flip the switch
            _isTurnedOn = false;
        }
        else
        {
            // do some processing when currently turned off and message received

            // flip the switch
            _isTurnedOn = true;
        }
    }
}

In the preceding code, the state of the boolean field _isTurnedOn determines how the actor behaves in response to an incoming FlipTheSwitchMessage.

This actor could be refactored so that the isTurnedOn field is removed and the different behavioural states become more explicit:

class LightSwitchBehaviourActor : ReceiveActor
{
    public LightSwitchBehaviourActor()
    {
        TurnedOff();   
    }


    private void TurnedOn()
    {
        Receive<FlipTheSwitchMessage>(
            message =>
            {
                // do some processing when currently turned on and message received                            

                // flip the switch
                Become(TurnedOff);
            });
    }

    private void TurnedOff()
    {
        Receive<FlipTheSwitchMessage>(
            message =>
            {
                // do some processing when currently turned off and message received                                  

                // flip the switch
                Become(TurnedOn);
            });
    }
}

Note the use of the Become() method which allows us to change how we respond to the next message that gets processed.

Whilst in these simple examples there are only two states (TurnedOn and TurnedOff) an actor could describe a greater number of potentially more complex behaviours.

For more information check out the Akka.NET docs or my Pluralsight course: Building Concurrent Applications with the Actor Model in Akka.NET.

You can start watching with a Pluralsight free trial.

SHARE:

Actor Models Come to .NET Developers with Akka.NET

Akka.NET is a port of the Java/Scala Akka framework.

In some ways, the emergence of Akka.NET could be seen as .NET truly “coming of age” - in the sense that other .NET technologies for building web, phone, services, apps etc. are great whereas the distributed .NET story has not been as fully realised – until now with Akka.NET and also Orleans.

In Akka.NET there are two primary constructs: actors and messages.

Actors do work and messages allow actors (and the outside world) to communicate. Actors don’t expose their internal state to other actors, all communication is done with messages.

Messages can be built-in .NET types such as a string or int, or can be custom types containing multiple data items.

Actors perform the work of the system and can essentially do one of four things:

  • Receive and respond to incoming messages
  • Send messages to other actors
  • Create new actors
  • Change behaviour in response to an incoming message

Actors have an incoming mailbox and they process one message at a time, then move onto the next message, and so on. If there are no messages to process the actor sits around being lazy, it only reacts to incoming messages.

The ability of actors to change their behaviour allows the simplification of actor code and also allows the building of finite state machines.

Actors can also be distributed across multiple processes/machines. Akka.NET makes this easier by providing “location transparency”, essentially not having to worry about changing our code just because we want to send a message to an actor in a different process. Akka.NET goes even further and can actually deploy instances of actors automatically to remote machines when they are created by simple modification of config – again requiring no code changes.

Akka.NET also supports the idea that systems can be “self-healing”. Through a hierarchy of supervision, parent actors supervise the child actors that they create. If a child actors errors, the parent supervising it can decide how to handle the failure. We can accept the default supervision strategy or create our own as necessary.

For more information on Akka.NET check out the home page, follow the project on Twitter, or check out my Pluralsight course: Building Concurrent Applications with the Actor Model in Akka.NET.

You can start watching with a Pluralsight free trial.

SHARE: