Skip to content
Sergey Ivasenko edited this page Apr 4, 2016 · 9 revisions

Declaration

Actors are the main (but not the only) consumers and producers of the messages in kino. Actor's implementation class declares one or more methods, each of which is responsible for processing one specific message:

public class MyMessageProcessor : Actor
{
    [MessageHandlerDefinition(typeof (MyMessage))]
    public async Task<IActorResult> MyMessageHandler(IMessage message)
    {
        // method body
    }
    
    [MessageHandlerDefinition(typeof (OtherMessage))]
    public Task<IActorResult> OtherMessageHandler(IMessage message)
    {
        // method body
    }
}

Registration could be done as well by overriding the following method of the base class:

 public class MyMessageProcessor : Actor
{
     public override IEnumerable<MessageHandlerDefinition> GetInterfaceDefinition()
     {
         // return list of registrations for this actor
     }
}        

One Actor class may not have more than one method, that processes specific message type, regardless of whether the message is sent unicast or broadcast.

One of the basic principles in kino is that unicast message uniquely points to the functionality: if you want something to be executed, you send a corresponding message. Therefore, it is logical, that any actor that registered itself as a handler for a specific message, implements the same logic.

Nevertheless, for broadcast messages the above mentioned restriction is not a must. For instance, in your network you may have two or more actors handling the same message and having different responsibilities. In this case, message, sent as broadcast, will be forwarded to all those actors and processed accordingly to the logic, implemented in each actor.

Local Actors

All Actors registrations are global by default and available for the whole kino network. Nevertheless, you may want to process some messages only within a local node. Following is the way you tell ActorHost to keep message handler registration localy, i.e. only within InternalRoutingTable:

public class LocalMessageProcessor : Actor
{
    // Keep Actor's registration local
    [MessageHandlerDefinition(typeof (LocalMessage), true)]
    public async Task<IActorResult> MyMessageHandler(IMessage message)
    {
        // method body
    }
}

Processing In and Sending Out Messages

Actor's method is guaranteed to receive as an input only the message type it registered itself for. So, until you hold to the agreement "one message type - one payload class", there is no need in extra check for message Identity and Version and you can immediately access the payload of the message:

public class MyMessageProcessor : Actor
{
    [MessageHandlerDefinition(typeof (MyMessage))]
    public Task<IActorResult> MyMessageHandler(IMessage message)
    {
        MyMessage payload = message.GetPayload<MyMessage>();
        // method body
    }   
}

It is possible to call async/await functions inside message handler. In this case, actor's method signature should be changed to async:

public class MyMessageProcessor : Actor
{
    [MessageHandlerDefinition(typeof (MyMessage))]
    public async Task<IActorResult> MyMessageHandler(IMessage message)
    {
        MyMessage payload = message.GetPayload<MyMessage>();
        
        var result = await ReadSomethingFromDb();
        
       // method body
    }    
}

Keep in mind, that after result is retrieved, execution may be continued on a different thread.

As a response, handler method may return one or more messages:

public class MyMessageProcessor : Actor
{
    [MessageHandlerDefinition(typeof (MyMessage))]
    public async Task<IActorResult> MyMessageHandler(IMessage message)
    {
        MyMessage payload = message.GetPayload<MyMessage>();
        
        var result = await ReadSomethingFromDb();
        
        IMessage response = Message.Create(new ResponseMessage(result));
        IMessage loggingRequest = Message.Create(new LogMessage(result));
        
        return new ActionResult(response, loggingRequest);
    }    
}

or nothing at all:

public class MyMessageProcessor : Actor
{
    [MessageHandlerDefinition(typeof (MyMessage))]
    public Task<IActorResult> MyMessageHandler(IMessage message)
    {
        MyMessage payload = message.GetPayload<MyMessage>();
        
        logger.Log(payload);
        
        return null;
    }    
}

ActorHost

Clone this wiki locally