This package contains some extensions to the Akka.Net F# APIs.
Run the following command in the Package Manager Console
PM> Install-Package Akka.NET.FSharp.API.Extensions
if you use extensively actor Computation Expression from the original Akka.FSharp package, you have certainly noticed that there are no way to handle the actor's lifecycle (PreStart, PostStop, PreRestart, PostRestart).
One way to achieve that is through types instead of actor computation expression. Here is an example:
type PlaybackActor() =
inherit UntypedActor()
override __.OnReceive message =
match message with
| :? string as e -> // sample with handling a string
| :? int as i -> // sample with handling an int
| _ -> __.Unhandled(message)
override __.PreStart() =
// do something, like logging for example
override __.PostStop() =
// do something, like logging for example
override __.PreRestart (e, message) =
// do something, like logging for example
base.PreRestart(e, message)
override __.PostRestart e =
// do something, like logging for example
base.PostRestart(e)With Akka.FSharp.API.Extensions you can handle actor's lifecycle events directly through user predefinied messages.
The idea was "borrowed" from the excellent Akka.Net F# Api implementation called Akkling, although the implementation is different and based directly on the original Akka.Net F# Api.
Let's say we would like to handle the PreStart method:
use system = System.create "actor-system" (Configuration.load())
let actor =
spawn system "actor"
<| fun mailbox ->
let rec loop() = actor {
let! msg = mailbox.Receive()
match msg with
| Lifecycle e ->
match e with
| PreStart -> () // do whatever you need to do
| _ -> ()
| _ -> ()
return! loop ()
}
loop ()The mailbox.Receive() returns a message of type ActorMessage. This is a wrapper around lifecycle messages and regular ones.
You have to handle it accordingly:
use system = System.create "actor-system" (Configuration.load())
let actor =
spawnOpt system "actor"
<| fun mailbox ->
let rec loop() = actor {
let! msg = mailbox.Receive()
match msg with
| Lifecycle e ->
match e with
| PreRestart(_, _) -> // PreRestart handled
| _ -> ()
| Message m ->
if m = "restart"
then failwith "System must be restarted"
else mailbox.Sender() <! m
| _ -> mailbox.Sender() <! msg
return! loop ()
}
loop ()
<| [ SpawnOption.SupervisorStrategy (Strategy.OneForOne (fun error -> Directive.Restart)) ]You can create simply stateful actors with become function. An example is more worth than words:
type Message =
| Print
| MyName of string
let rec namePrinter lastName = function
| Message m ->
match m with
| Print -> printfn "Last name was %s?" lastName
become (namePrinter lastName)
| MyName(who) ->
printfn "Hello %s!" who
become (namePrinter who)
| _ -> become (namePrinter lastName)
let system = System.create "testSystem" (Configuration.load())
let actor =
spawn system "actor"
<| actorOf (namePrinter "No One")
actor <! MyName "Tomasz"
actor <! MyName "Marcel"
actor <! Print